... after enabling event channel delivery.
New versions of Xen have this sequence in their map_vcpu_info() function:
/*
* Mark everything as being pending just to make sure nothing gets
* lost. The domain will get a spurious event, but it can cope.
*/
if ( !has_32bit_shinfo(d) )
write_atomic(&new_info->native.evtchn_pending_sel, ~0);
else
write_atomic(&vcpu_info(v, evtchn_pending_sel), ~0);
vcpu_mark_events_pending(v);
whereas older versions code this differently:
/*
* Mark everything as being pending just to make sure nothing gets
* lost. The domain will get a spurious event, but it can cope.
*/
vcpu_info(v, evtchn_upcall_pending) = 1;
for ( i = 0; i < BITS_PER_EVTCHN_WORD(d); i++ )
set_bit(i, &vcpu_info(v, evtchn_pending_sel));
The crucial difference is that in the older variant there is no call to
vcpu_mark_events_pending() which means that, for an HVM guest at least,
the upcall function that clears 'evtchn_upcall_pending' does not get
run and hence no events will be received on that vCPU.
This patch makes sure the upcall function for each vCPU is run at least once
thereby ensuring that 'evtchn_upcall_pending' is cleared.
NOTE: The patch also adds a 'Count' to each XENBUS_INTERRUPT object,
incremented each time the interrupt is triggred, and emits a log line
when the value transitions from zero.
Signed-off-by: Paul Durrant <pdurrant@amazon.com>
{
ULONG Cpu;
ULONG Line;
+ KIRQL Irql;
NTSTATUS status;
Trace("====>\n");
ProcNumber.Number,
Vector);
Processor->UpcallEnabled = TRUE;
+
+ Irql = FdoAcquireInterruptLock(Context->Fdo, Processor->Interrupt);
+ (VOID) EvtchnInterruptCallback(NULL, Processor);
+ FdoReleaseInterruptLock(Context->Fdo, Processor->Interrupt, Irql);
}
line:
Info("CALLBACK VIA (Vector = %u)\n", Line);
+ Irql = FdoAcquireInterruptLock(Context->Fdo, Context->Interrupt);
+ (VOID) EvtchnInterruptCallback(NULL, &Context->Processor[0]);
+ FdoReleaseInterruptLock(Context->Fdo, Context->Interrupt, Irql);
+
Trace("<====\n");
}
ULONG Line;
PKSERVICE_ROUTINE Callback;
PVOID Argument;
+ ULONG Count;
};
typedef struct _XENBUS_VIRQ {
if (Interrupt->Callback == NULL)
return FALSE;
+ if (Interrupt->Count++ == 0)
+ LogPrintf(LOG_LEVEL_INFO,
+ "XENBUS: %u:%u INTERRUPT\n",
+ Interrupt->ProcNumber.Group,
+ Interrupt->ProcNumber.Number);
+
return Interrupt->Callback(InterruptObject,
Interrupt->Argument);
}