XENBUS_EVTCHN_PARAMETERS Parameters;
BOOLEAN Mask;
ULONG LocalPort;
+ PXENBUS_INTERRUPT Interrupt;
};
struct _XENBUS_EVTCHN_CONTEXT {
PXENBUS_FDO Fdo;
KSPIN_LOCK Lock;
LONG References;
- PXENBUS_INTERRUPT Interrupt;
+ PXENBUS_INTERRUPT LevelSensitiveInterrupt;
+ PXENBUS_INTERRUPT LatchedInterrupt[MAXIMUM_PROCESSORS];
+ KAFFINITY Affinity;
BOOLEAN Enabled;
XENBUS_SUSPEND_INTERFACE SuspendInterface;
PXENBUS_SUSPEND_CALLBACK SuspendCallbackEarly;
IN PXENBUS_EVTCHN_CONTEXT Context
)
{
+ LONG Cpu;
ULONG Line;
NTSTATUS status;
- Trace("<===>\n");
+ Trace("====>\n");
+
+ ASSERT3U(Context->Affinity, ==, 0);
+
+ Cpu = 0;
+ while (Cpu < KeNumberProcessors) {
+ unsigned int vcpu_id;
+ UCHAR Vector;
+
+ vcpu_id = SystemVirtualCpuIndex(Cpu);
+ Vector = FdoGetInterruptVector(Context->Fdo,
+ Context->LatchedInterrupt[Cpu]);
+
+ status = HvmSetEvtchnUpcallVector(vcpu_id, Vector);
+ if (NT_SUCCESS(status)) {
+ Info("CPU %u\n", Cpu);
+ Context->Affinity |= (KAFFINITY)1 << Cpu;
+ }
+
+ Cpu++;
+ }
- Line = FdoGetInterruptLine(Context->Fdo, Context->Interrupt);
+ Line = FdoGetInterruptLine(Context->Fdo,
+ Context->LevelSensitiveInterrupt);
status = HvmSetParam(HVM_PARAM_CALLBACK_IRQ, Line);
ASSERT(NT_SUCCESS(status));
+
+ Trace("<====\n");
}
static VOID
IN PXENBUS_EVTCHN_CONTEXT Context
)
{
+ LONG Cpu;
NTSTATUS status;
UNREFERENCED_PARAMETER(Context);
- Trace("<===>\n");
+ Trace("====>\n");
status = HvmSetParam(HVM_PARAM_CALLBACK_IRQ, 0);
ASSERT(NT_SUCCESS(status));
+
+ Cpu = KeNumberProcessors;
+ while (--Cpu >= 0) {
+ unsigned int vcpu_id;
+
+ vcpu_id = SystemVirtualCpuIndex(Cpu);
+
+ (VOID) HvmSetEvtchnUpcallVector(vcpu_id, 0);
+ Context->Affinity &= ~((KAFFINITY)1 << Cpu);
+ }
+
+ ASSERT3U(Context->Affinity, ==, 0);
+
+ Trace("<====\n");
}
static FORCEINLINE
_IRQL_raises_(HIGH_LEVEL)
KIRQL
__EvtchnAcquireInterruptLock(
- IN PXENBUS_EVTCHN_CONTEXT Context
+ IN PXENBUS_EVTCHN_CONTEXT Context,
+ IN PXENBUS_EVTCHN_CHANNEL Channel
)
{
- return FdoAcquireInterruptLock(Context->Fdo, Context->Interrupt);
+ return FdoAcquireInterruptLock(Context->Fdo, Channel->Interrupt);
}
static FORCEINLINE
VOID
__EvtchnReleaseInterruptLock(
IN PXENBUS_EVTCHN_CONTEXT Context,
+ IN PXENBUS_EVTCHN_CHANNEL Channel,
IN __drv_restoresIRQL KIRQL Irql
)
{
- FdoReleaseInterruptLock(Context->Fdo, Context->Interrupt, Irql);
+ FdoReleaseInterruptLock(Context->Fdo, Channel->Interrupt, Irql);
}
static NTSTATUS
LocalPort = Channel->LocalPort;
+ Channel->Interrupt = (Context->Affinity != 0) ? // Latched available
+ Context->LatchedInterrupt[0] :
+ Context->LevelSensitiveInterrupt;
+
status = XENBUS_EVTCHN_ABI(PortEnable,
&Context->EvtchnAbi,
LocalPort);
return NULL;
}
+#define EVTCHN_SWAP_POINTER(_X, _Y) \
+ do { \
+ (_X) = (PVOID)((ULONG_PTR)(_X) ^ (ULONG_PTR)(_Y)); \
+ (_Y) = (PVOID)((ULONG_PTR)(_X) ^ (ULONG_PTR)(_Y)); \
+ (_X) = (PVOID)((ULONG_PTR)(_X) ^ (ULONG_PTR)(_Y)); \
+ } while (FALSE)
+
+static NTSTATUS
+EvtchnBind(
+ IN PINTERFACE Interface,
+ IN PXENBUS_EVTCHN_CHANNEL Channel,
+ IN ULONG Cpu
+ )
+{
+ PXENBUS_EVTCHN_CONTEXT Context = Interface->Context;
+ PXENBUS_INTERRUPT Interrupt;
+ ULONG LocalPort;
+ unsigned int vcpu_id;
+ KIRQL Irql;
+ NTSTATUS status;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Cpu >= (ULONG)KeNumberProcessors)
+ goto fail1;
+
+ ASSERT(Context->Enabled);
+
+ status = STATUS_NOT_SUPPORTED;
+ if (~Context->Affinity & ((KAFFINITY)1 << Cpu))
+ goto fail2;
+
+ Interrupt = Context->LatchedInterrupt[Cpu];
+
+ if (Channel->Interrupt == Interrupt)
+ goto done;
+
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ // Make sure we always lock in a consistent order
+ if ((ULONG_PTR)Interrupt < (ULONG_PTR)Channel->Interrupt) {
+ (VOID) FdoAcquireInterruptLock(Context->Fdo, Interrupt);
+ (VOID) FdoAcquireInterruptLock(Context->Fdo, Channel->Interrupt);
+ } else {
+ (VOID) FdoAcquireInterruptLock(Context->Fdo, Channel->Interrupt);
+ (VOID) FdoAcquireInterruptLock(Context->Fdo, Interrupt);
+ }
+
+ LocalPort = Channel->LocalPort;
+ vcpu_id = SystemVirtualCpuIndex(Cpu);
+
+ status = EventChannelBindVirtualCpu(LocalPort, vcpu_id);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ EVTCHN_SWAP_POINTER(Channel->Interrupt, Interrupt);
+
+ FdoReleaseInterruptLock(Context->Fdo, Channel->Interrupt, HIGH_LEVEL);
+ FdoReleaseInterruptLock(Context->Fdo, Interrupt, HIGH_LEVEL);
+
+ KeLowerIrql(Irql);
+
+ Info("[%u]: CPU %u\n", LocalPort, Cpu);
+
+done:
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+ FdoReleaseInterruptLock(Context->Fdo, Channel->Interrupt, HIGH_LEVEL);
+ FdoReleaseInterruptLock(Context->Fdo, Interrupt, HIGH_LEVEL);
+
+ KeLowerIrql(Irql);
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
static BOOLEAN
EvtchnUnmask(
IN PINTERFACE Interface,
ASSERT3U(Channel->Magic, ==, XENBUS_EVTCHN_CHANNEL_MAGIC);
- Irql = __EvtchnAcquireInterruptLock(Context);
+ Irql = __EvtchnAcquireInterruptLock(Context, Channel);
ASSERT3U(KeGetCurrentIrql(), >=, DISPATCH_LEVEL);
DoneSomething = FALSE;
}
- __EvtchnReleaseInterruptLock(Context, Irql);
+ __EvtchnReleaseInterruptLock(Context, Channel, Irql);
return DoneSomething;
}
ASSERT(NT_SUCCESS(status));
}
+ Channel->Interrupt = NULL;
+
Channel->LocalPort = 0;
RtlZeroMemory(&Channel->Parameters, sizeof (XENBUS_EVTCHN_PARAMETERS));
BOOLEAN DoneSomething;
NTSTATUS status;
+ DoneSomething = FALSE;
+
status = HashTableLookup(Context->Table,
LocalPort,
(PULONG_PTR)&Channel);
&Context->EvtchnAbi,
LocalPort);
- DoneSomething = FALSE;
goto done;
}
+ if (Context->Affinity != 0) {
+ ULONG Cpu;
+
+ ASSERT3U(KeGetCurrentIrql(), >=, DISPATCH_LEVEL);
+ Cpu = KeGetCurrentProcessorNumber();
+
+ // Only handle events on the correct CPU
+ if (Channel->Interrupt != Context->LatchedInterrupt[Cpu])
+ goto done;
+ }
+
if (Channel->Mask)
XENBUS_EVTCHN_ABI(PortMask,
&Context->EvtchnAbi,
status = EvtchnAbiAcquire(Context);
ASSERT(NT_SUCCESS(status));
- if (Context->Enabled)
+ if (Context->Enabled) {
+ EvtchnInterruptDisable(Context);
EvtchnInterruptEnable(Context);
+ }
}
static VOID
PXENBUS_EVTCHN_CONTEXT Context = Interface->Context;
PXENBUS_FDO Fdo = Context->Fdo;
KIRQL Irql;
+ LONG Cpu;
NTSTATUS status;
KeAcquireSpinLock(&Context->Lock, &Irql);
0,
EvtchnInterruptCallback,
Context,
- &Context->Interrupt);
+ &Context->LevelSensitiveInterrupt);
if (!NT_SUCCESS(status))
goto fail8;
+ Cpu = 0;
+ while (Cpu < KeNumberProcessors) {
+ status = FdoAllocateInterrupt(Fdo,
+ Latched,
+ Cpu,
+ EvtchnInterruptCallback,
+ Context,
+ &Context->LatchedInterrupt[Cpu]);
+ if (!NT_SUCCESS(status))
+ goto fail9;
+
+ Cpu++;
+ }
+
Trace("<====\n");
done:
return STATUS_SUCCESS;
+fail9:
+ Error("fail9\n");
+
+ while (--Cpu >= 0) {
+ FdoFreeInterrupt(Fdo, Context->LatchedInterrupt[Cpu]);
+ Context->LatchedInterrupt[Cpu] = NULL;
+ }
+
+ FdoFreeInterrupt(Fdo, Context->LevelSensitiveInterrupt);
+ Context->LevelSensitiveInterrupt = NULL;
+
fail8:
Error("fail8\n");
)
{
PXENBUS_EVTCHN_CONTEXT Context = Interface->Context;
+ PXENBUS_FDO Fdo = Context->Fdo;
KIRQL Irql;
+ LONG Cpu;
KeAcquireSpinLock(&Context->Lock, &Irql);
if (!IsListEmpty(&Context->List))
BUG("OUTSTANDING EVENT CHANNELS");
- FdoFreeInterrupt(Context->Fdo, Context->Interrupt);
- Context->Interrupt = NULL;
+ Cpu = KeNumberProcessors;
+ while (--Cpu >= 0) {
+ FdoFreeInterrupt(Fdo, Context->LatchedInterrupt[Cpu]);
+ Context->LatchedInterrupt[Cpu] = NULL;
+ }
+
+ FdoFreeInterrupt(Fdo, Context->LevelSensitiveInterrupt);
+ Context->LevelSensitiveInterrupt = NULL;
EvtchnAbiRelease(Context);
EvtchnClose
};
+static struct _XENBUS_EVTCHN_INTERFACE_V2 EvtchnInterfaceVersion2 = {
+ { sizeof (struct _XENBUS_EVTCHN_INTERFACE_V2), 2, NULL, NULL, NULL },
+ EvtchnAcquire,
+ EvtchnRelease,
+ EvtchnOpen,
+ EvtchnBind,
+ EvtchnUnmask,
+ EvtchnSend,
+ EvtchnTrigger,
+ EvtchnGetPort,
+ EvtchnClose
+};
+
NTSTATUS
EvtchnInitialize(
IN PXENBUS_FDO Fdo,
status = STATUS_SUCCESS;
break;
}
+ case 2: {
+ struct _XENBUS_EVTCHN_INTERFACE_V2 *EvtchnInterface;
+
+ EvtchnInterface = (struct _XENBUS_EVTCHN_INTERFACE_V2 *)Interface;
+
+ status = STATUS_BUFFER_OVERFLOW;
+ if (Size < sizeof (struct _XENBUS_EVTCHN_INTERFACE_V2))
+ break;
+
+ *EvtchnInterface = EvtchnInterfaceVersion2;
+
+ ASSERT3U(Interface->Version, ==, Version);
+ Interface->Context = Context;
+
+ status = STATUS_SUCCESS;
+ break;
+ }
default:
status = STATUS_NOT_SUPPORTED;
break;