BlockRing->Submitted = BlockRing->Received = 0;
}
-VOID
+BOOLEAN
BlockRingPoll(
IN PXENVBD_BLOCKRING BlockRing
)
{
PXENVBD_TARGET Target = FrontendGetTarget(BlockRing->Frontend);
+ BOOLEAN Retry = FALSE;
ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
KeAcquireSpinLockAtDpcLevel(&BlockRing->Lock);
KeMemoryBarrier();
- if (rsp_cons == rsp_prod)
+ if (rsp_cons == rsp_prod || Retry)
break;
- while (rsp_cons != rsp_prod) {
+ while (rsp_cons != rsp_prod && !Retry) {
blkif_response_t* Response;
ULONG Tag;
}
RtlZeroMemory(Response, sizeof(union blkif_sring_entry));
+
+ if (rsp_cons - BlockRing->FrontRing.rsp_cons > RING_SIZE(&BlockRing->FrontRing) / 4)
+ Retry = TRUE;
}
KeMemoryBarrier();
done:
KeReleaseSpinLockFromDpcLevel(&BlockRing->Lock);
+
+ return Retry;
}
BOOLEAN
//=============================================================================
__drv_requiresIRQL(DISPATCH_LEVEL)
-VOID
+BOOLEAN
FrontendNotifyResponses(
__in PXENVBD_FRONTEND Frontend
)
{
- BlockRingPoll(Frontend->BlockRing);
- TargetSubmitRequests(Frontend->Target);
+ BOOLEAN Retry = FALSE;
+
+ Retry |= BlockRingPoll(Frontend->BlockRing);
+ Retry |= TargetSubmitRequests(Frontend->Target);
+
+ return Retry;
}
//=============================================================================
ULONG NumInts;
ULONG NumDpcs;
KDPC Dpc;
+ KDPC TimerDpc;
+ KTIMER Timer;
};
#define NOTIFIER_POOL_TAG 'yfNX'
return TRUE;
}
+static FORCEINLINE BOOLEAN
+__NotifierDpcTimeout(
+ IN PXENVBD_NOTIFIER Notifier
+ )
+{
+ KDPC_WATCHDOG_INFORMATION Watchdog;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(Notifier);
+
+ RtlZeroMemory(&Watchdog, sizeof (Watchdog));
+
+ status = KeQueryDpcWatchdogInformation(&Watchdog);
+ ASSERT(NT_SUCCESS(status));
+
+ if (Watchdog.DpcTimeLimit == 0 ||
+ Watchdog.DpcWatchdogLimit == 0)
+ return FALSE;
+
+ if (Watchdog.DpcTimeCount > (Watchdog.DpcTimeLimit / 2) &&
+ Watchdog.DpcWatchdogCount > (Watchdog.DpcWatchdogLimit / 2))
+ return FALSE;
+
+ return TRUE;
+}
+
+#define TIME_US(_us) ((_us) * 10)
+#define TIME_MS(_ms) (TIME_US((_ms) * 1000))
+#define TIME_S(_s) (TIME_MS((_s) * 1000))
+#define TIME_RELATIVE(_t) (-(_t))
+
KDEFERRED_ROUTINE NotifierDpc;
VOID
if (!Notifier->Connected)
return;
- FrontendNotifyResponses(Notifier->Frontend);
+ for (;;) {
+ if (!FrontendNotifyResponses(Notifier->Frontend)) {
+ XENBUS_EVTCHN(Unmask,
+ &Notifier->EvtchnInterface,
+ Notifier->Channel,
+ FALSE);
+ break;
+ }
+ if (__NotifierDpcTimeout(Notifier)) {
+ LARGE_INTEGER Delay;
- XENBUS_EVTCHN(Unmask,
- &Notifier->EvtchnInterface,
- Notifier->Channel,
- FALSE);
+ Delay.QuadPart = TIME_RELATIVE(TIME_US(100));
+
+ KeSetTimer(&Notifier->Timer,
+ Delay,
+ &Notifier->TimerDpc);
+ break;
+ }
+ }
}
NTSTATUS
(*Notifier)->Frontend = Frontend;
KeInitializeDpc(&(*Notifier)->Dpc, NotifierDpc, *Notifier);
+ KeInitializeDpc(&(*Notifier)->TimerDpc, NotifierDpc, *Notifier);
+ KeInitializeTimer(&(*Notifier)->Timer);
return STATUS_SUCCESS;
{
Notifier->Frontend = NULL;
RtlZeroMemory(&Notifier->Dpc, sizeof(KDPC));
+ RtlZeroMemory(&Notifier->TimerDpc, sizeof(KDPC));
+ RtlZeroMemory(&Notifier->Timer, sizeof(KTIMER));
ASSERT(IsZeroMemory(Notifier, sizeof(XENVBD_NOTIFIER)));
ASSERT(Notifier->Enabled == TRUE);
Notifier->Enabled = FALSE;
+
+ //
+ // No new timers can be scheduled once Enabled goes to FALSE.
+ // Cancel any existing ones.
+ //
+ (VOID) KeCancelTimer(&Notifier->Timer);
}
VOID
}
}
-VOID
+BOOLEAN
TargetSubmitRequests(
IN PXENVBD_TARGET Target
)
{
+ BOOLEAN Retry = FALSE;
+
for (;;) {
// submit all prepared requests (0 or more requests)
// return TRUE if submitted 0 or more requests from prepared queue
// return FALSE if prepare failed or fresh queue empty
if (!TargetPrepareFresh(Target))
break;
+
+ // back off, check DPC timeout and try again
+ Retry = TRUE;
+ break;
}
// if no requests/SRBs outstanding, complete any shutdown SRBs
- TargetCompleteShutdown(Target);
+ if (!Retry)
+ TargetCompleteShutdown(Target);
+
+ return Retry;
}
VOID