]> xenbits.xensource.com Git - people/pauldu/xenvif.git/commitdiff
Add DPC watchdog avoidance to receiver and transmitter poll
authorPaul Durrant <paul.durrant@citrix.com>
Mon, 25 Jan 2016 10:24:49 +0000 (10:24 +0000)
committerPaul Durrant <paul.durrant@citrix.com>
Tue, 26 Jan 2016 11:26:32 +0000 (11:26 +0000)
Under HCK MPE testing it's possible for both receiver and transmitter
polling loops to run long enough to hit the DPC watchdog. This patch
avoids the problem by terminating the polling loops after a 'batch'
(currently 1/4 of the ring) and then checking the remaining time before the
watchdog fires. If the remaining time is still more than half the total
time then another poll is done, otherwise a timer is scheduled to complete
the work.

Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
src/xenvif/receiver.c
src/xenvif/transmitter.c

index 29877f237a40f3903dca0c4f8a4320a1465291e0..538971e325ff6fc24b7b66b4d5dc08f3b32a2680 100644 (file)
@@ -86,6 +86,8 @@ typedef struct _XENVIF_RECEIVER_RING {
     PXENBUS_EVTCHN_CHANNEL      Channel;
     KDPC                        Dpc;
     ULONG                       Dpcs;
+    KTIMER                      Timer;
+    KDPC                        TimerDpc;
     ULONG                       Events;
     PXENVIF_RECEIVER_FRAGMENT   Pending[XENVIF_RECEIVER_MAXIMUM_FRAGMENT_ID + 1];
     ULONG                       RequestsPosted;
@@ -1783,7 +1785,7 @@ ReceiverRingDebugCallback(
                  Ring->Dpcs);
 }
 
-static DECLSPEC_NOINLINE VOID
+static DECLSPEC_NOINLINE BOOLEAN
 ReceiverRingPoll(
     IN  PXENVIF_RECEIVER_RING   Ring
     )
@@ -1792,12 +1794,14 @@ ReceiverRingPoll(
 
     PXENVIF_RECEIVER            Receiver;
     PXENVIF_FRONTEND            Frontend;
-
-    if (!(Ring->Enabled))
-        return;
+    BOOLEAN                     Retry;
 
     Receiver = Ring->Receiver;
     Frontend = Receiver->Frontend;
+    Retry = FALSE;
+
+    if (!Ring->Enabled)
+        goto done;
 
     for (;;) {
         BOOLEAN                 Error;
@@ -1827,15 +1831,14 @@ ReceiverRingPoll(
 
         KeMemoryBarrier();
 
-        if (rsp_cons == rsp_prod)
+        if (rsp_cons == rsp_prod || Retry)
             break;
 
-        while (rsp_cons != rsp_prod) {
+        while (rsp_cons != rsp_prod && !Retry) {
             netif_rx_response_t         *rsp;
             uint16_t                    id;
             PXENVIF_RECEIVER_FRAGMENT   Fragment;
             PMDL                        Mdl;
-            RING_IDX                    req_prod;
 
             rsp = RING_GET_RESPONSE(&Ring->Front, rsp_cons);
 
@@ -1942,6 +1945,9 @@ ReceiverRingPoll(
                     InsertTailList(&Ring->PacketList, &Packet->ListEntry);
                 }
 
+                if (rsp_cons - Ring->Front.rsp_cons > XENVIF_RECEIVER_BATCH(Ring))
+                    Retry = TRUE;
+
                 Error = FALSE;
                 Info = 0;
                 MaximumSegmentSize = 0;
@@ -1949,16 +1955,6 @@ ReceiverRingPoll(
                 flags = 0;
                 TailMdl = NULL;
             }
-
-            KeMemoryBarrier();
-
-            req_prod = Ring->Front.req_prod_pvt;
-
-            if (req_prod - rsp_cons < XENVIF_RECEIVER_BATCH(Ring) &&
-                !__ReceiverRingIsStopped(Ring)) {
-                Ring->Front.rsp_cons = rsp_cons;
-                ReceiverRingFill(Ring);
-            }
         }
         ASSERT(!Error);
         ASSERT(!Extra);
@@ -1978,6 +1974,9 @@ ReceiverRingPoll(
     if (!__ReceiverRingIsStopped(Ring))
         ReceiverRingFill(Ring);
 
+done:
+    return Retry;
+
 #undef  XENVIF_RECEIVER_BATCH
 }
 
@@ -1999,6 +1998,37 @@ __ReceiverRingUnmask(
                   FALSE);
 }
 
+static FORCEINLINE BOOLEAN
+__ReceiverRingDpcTimeout(
+    IN  PXENVIF_RECEIVER_RING   Ring
+    )
+{
+    KDPC_WATCHDOG_INFORMATION   Watchdog;
+    NTSTATUS                    status;
+
+    UNREFERENCED_PARAMETER(Ring);
+
+    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))
+
 __drv_functionClass(KDEFERRED_ROUTINE)
 __drv_maxIRQL(DISPATCH_LEVEL)
 __drv_minIRQL(DISPATCH_LEVEL)
@@ -2020,15 +2050,27 @@ ReceiverRingDpc(
 
     ASSERT(Ring != NULL);
 
-    Ring->Dpcs++;
+    for (;;) {
+        BOOLEAN Retry;
 
-    __ReceiverRingAcquireLock(Ring);
+        __ReceiverRingAcquireLock(Ring);
+        Retry = ReceiverRingPoll(Ring);
+        __ReceiverRingReleaseLock(Ring);
+
+        if (!Retry) {
+            __ReceiverRingUnmask(Ring);
+            break;
+        }
 
-    if (Ring->Enabled)
-        ReceiverRingPoll(Ring);
+        if (__ReceiverRingDpcTimeout(Ring)) {
+            LARGE_INTEGER   Delay;
 
-    __ReceiverRingReleaseLock(Ring);
-    __ReceiverRingUnmask(Ring);
+            Delay.QuadPart = TIME_RELATIVE(TIME_US(100));
+
+            KeSetTimer(&Ring->Timer, Delay, &Ring->TimerDpc);
+            break;
+        }
+    }
 }
 
 KSERVICE_ROUTINE    ReceiverRingEvtchnCallback;
@@ -2049,7 +2091,8 @@ ReceiverRingEvtchnCallback(
 
     Ring->Events++;
 
-    (VOID) KeInsertQueueDpc(&Ring->Dpc, NULL, NULL);
+    if (KeInsertQueueDpc(&Ring->Dpc, NULL, NULL))
+        Ring->Dpcs++;
 
     Receiver = Ring->Receiver;
     Frontend = Receiver->Frontend;
@@ -2061,11 +2104,6 @@ ReceiverRingEvtchnCallback(
     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))
-
 #define XENVIF_RECEIVER_WATCHDOG_PERIOD 30
 
 static NTSTATUS
@@ -2168,6 +2206,8 @@ __ReceiverRingInitialize(
     InitializeListHead(&(*Ring)->PacketList);
 
     KeInitializeDpc(&(*Ring)->Dpc, ReceiverRingDpc, *Ring);
+    KeInitializeTimer(&(*Ring)->Timer);
+    KeInitializeDpc(&(*Ring)->TimerDpc, ReceiverRingDpc, *Ring);
 
     status = ThreadCreate(ReceiverRingWatchdog,
                           *Ring,
@@ -2180,6 +2220,8 @@ __ReceiverRingInitialize(
 fail3:
     Error("fail3\n");
 
+    RtlZeroMemory(&(*Ring)->TimerDpc, sizeof (KDPC));
+    RtlZeroMemory(&(*Ring)->Timer, sizeof (KTIMER));
     RtlZeroMemory(&(*Ring)->Dpc, sizeof (KDPC));
 
     RtlZeroMemory(&(*Ring)->PacketList, sizeof (LIST_ENTRY));
@@ -2344,6 +2386,7 @@ __ReceiverRingConnect(
     ASSERT(NT_SUCCESS(status));
 
     KeSetTargetProcessorDpcEx(&Ring->Dpc, &ProcNumber);
+    KeSetTargetProcessorDpcEx(&Ring->TimerDpc, &ProcNumber);
 
     (VOID) XENBUS_EVTCHN(Bind,
                          &Receiver->EvtchnInterface,
@@ -2629,6 +2672,8 @@ __ReceiverRingTeardown(
     Receiver = Ring->Receiver;
     Frontend = Receiver->Frontend;
 
+    RtlZeroMemory(&Ring->TimerDpc, sizeof (KDPC));
+    RtlZeroMemory(&Ring->Timer, sizeof (KTIMER));
     RtlZeroMemory(&Ring->Dpc, sizeof (KDPC));
 
     Ring->BackfillSize = 0;
index fdb13348351a91830ff16d699d4df13095c09068..3aa63660d1d2d80b23d4b9408cdcef7c99532624 100644 (file)
@@ -180,6 +180,8 @@ typedef struct _XENVIF_TRANSMITTER_RING {
     PXENBUS_EVTCHN_CHANNEL          Channel;
     KDPC                            Dpc;
     ULONG                           Dpcs;
+    KTIMER                          Timer;
+    KDPC                            TimerDpc;
     ULONG                           Events;
     BOOLEAN                         Connected;
     BOOLEAN                         Enabled;
@@ -2351,7 +2353,7 @@ __TransmitterRingCompletePacket(
     Ring->PacketsCompleted++;
 }
 
-static DECLSPEC_NOINLINE VOID
+static DECLSPEC_NOINLINE BOOLEAN
 TransmitterRingPoll(
     IN  PXENVIF_TRANSMITTER_RING    Ring
     )
@@ -2360,14 +2362,18 @@ TransmitterRingPoll(
 
     PXENVIF_TRANSMITTER             Transmitter;
     PXENVIF_FRONTEND                Frontend;
+    BOOLEAN                         Retry;
 
     Transmitter = Ring->Transmitter;
     Frontend = Transmitter->Frontend;
+    Retry = FALSE;
+
+    if (!Ring->Enabled)
+        goto done;
 
     for (;;) {
         RING_IDX    rsp_prod;
         RING_IDX    rsp_cons;
-        ULONG       Delta;
 
         KeMemoryBarrier();
 
@@ -2376,10 +2382,10 @@ TransmitterRingPoll(
 
         KeMemoryBarrier();
 
-        if (rsp_cons == rsp_prod)
+        if (rsp_cons == rsp_prod || Retry)
             break;
 
-        while (rsp_cons != rsp_prod) {
+        while (rsp_cons != rsp_prod && !Retry) {
             netif_tx_response_t             *rsp;
             uint16_t                        id;
             PXENVIF_TRANSMITTER_FRAGMENT    Fragment;
@@ -2471,10 +2477,8 @@ TransmitterRingPoll(
             Fragment->Extra = FALSE;
             __TransmitterPutFragment(Ring, Fragment);
 
-            if (Packet == NULL) {
-                RtlZeroMemory(rsp, sizeof (netif_tx_response_t));
+            if (Packet == NULL)
                 continue;
-            }
 
             --Packet->Reference;
 
@@ -2495,8 +2499,6 @@ TransmitterRingPoll(
                 }
             }
 
-            RtlZeroMemory(rsp, sizeof (netif_tx_response_t));
-
             if (Packet->Reference != 0)
                 continue;
 
@@ -2504,19 +2506,20 @@ TransmitterRingPoll(
                 Packet->Completion.Status = XENVIF_TRANSMITTER_PACKET_OK;
 
             __TransmitterRingCompletePacket(Ring, Packet);
+
+            if (rsp_cons - Ring->Front.rsp_cons > XENVIF_TRANSMITTER_BATCH(Ring))
+                Retry = TRUE;
         }
 
         KeMemoryBarrier();
 
         Ring->Front.rsp_cons = rsp_cons;
-
-        Delta = Ring->Front.req_prod_pvt - rsp_cons;
-        Delta = __min(Delta, XENVIF_TRANSMITTER_BATCH(Ring));
-        Delta = __max(Delta, 1);
-
-        Ring->Shared->rsp_event = rsp_cons + Delta;
+        Ring->Shared->rsp_event = rsp_cons + 1;
     }
 
+done:
+    return Retry;
+
 #undef XENVIF_TRANSMITTER_BATCH
 }
 
@@ -2930,6 +2933,37 @@ __TransmitterRingUnmask(
                   FALSE);
 }
 
+static FORCEINLINE BOOLEAN
+__TransmitterRingDpcTimeout(
+    IN  PXENVIF_TRANSMITTER_RING    Ring
+    )
+{
+    KDPC_WATCHDOG_INFORMATION       Watchdog;
+    NTSTATUS                        status;
+
+    UNREFERENCED_PARAMETER(Ring);
+
+    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))
+
 __drv_functionClass(KDEFERRED_ROUTINE)
 __drv_maxIRQL(DISPATCH_LEVEL)
 __drv_minIRQL(DISPATCH_LEVEL)
@@ -2951,15 +2985,27 @@ TransmitterRingDpc(
 
     ASSERT(Ring != NULL);
 
-    Ring->Dpcs++;
+    for (;;) {
+        BOOLEAN Retry;
 
-    __TransmitterRingAcquireLock(Ring);
+        __TransmitterRingAcquireLock(Ring);
+        Retry = TransmitterRingPoll(Ring);
+        __TransmitterRingReleaseLock(Ring);
 
-    if (Ring->Enabled)
-        TransmitterRingPoll(Ring);
+        if (!Retry) {
+            __TransmitterRingUnmask(Ring);
+           break;
+        }
 
-    __TransmitterRingReleaseLock(Ring);
-    __TransmitterRingUnmask(Ring);
+        if (__TransmitterRingDpcTimeout(Ring)) {
+            LARGE_INTEGER   Delay;
+
+            Delay.QuadPart = TIME_RELATIVE(TIME_US(100));
+
+            KeSetTimer(&Ring->Timer, Delay, &Ring->TimerDpc);
+            break;
+        }
+    }
 }
 
 KSERVICE_ROUTINE    TransmitterRingEvtchnCallback;
@@ -2985,16 +3031,12 @@ TransmitterRingEvtchnCallback(
 
     Ring->Events++;
 
-    (VOID) KeInsertQueueDpc(&Ring->Dpc, NULL, NULL);
+    if (KeInsertQueueDpc(&Ring->Dpc, NULL, NULL))
+        Ring->Dpcs++;
 
     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))
-
 #define XENVIF_TRANSMITTER_WATCHDOG_PERIOD  30
 
 static NTSTATUS
@@ -3044,7 +3086,7 @@ TransmitterRingWatchdog(
 
                 // Try to move things along
                 __TransmitterRingSend(Ring);
-                TransmitterRingPoll(Ring);
+                (VOID) TransmitterRingPoll(Ring);
             }
 
             PacketsQueued = Ring->PacketsQueued;
@@ -3089,6 +3131,8 @@ __TransmitterRingInitialize(
     InitializeListHead(&(*Ring)->PacketComplete);
 
     KeInitializeDpc(&(*Ring)->Dpc, TransmitterRingDpc, *Ring);
+    KeInitializeTimer(&(*Ring)->Timer);
+    KeInitializeDpc(&(*Ring)->TimerDpc, TransmitterRingDpc, *Ring);
 
     status = ThreadCreate(TransmitterRingWatchdog,
                           *Ring,
@@ -3101,6 +3145,8 @@ __TransmitterRingInitialize(
 fail3:
     Error("fail3\n");
 
+    RtlZeroMemory(&(*Ring)->TimerDpc, sizeof (KDPC));
+    RtlZeroMemory(&(*Ring)->Timer, sizeof (KTIMER));
     RtlZeroMemory(&(*Ring)->Dpc, sizeof (KDPC));
 
     RtlZeroMemory(&(*Ring)->PacketComplete, sizeof (LIST_ENTRY));
@@ -3344,6 +3390,7 @@ __TransmitterRingConnect(
         ASSERT(NT_SUCCESS(status));
 
         KeSetTargetProcessorDpcEx(&Ring->Dpc, &ProcNumber);
+        KeSetTargetProcessorDpcEx(&Ring->TimerDpc, &ProcNumber);
 
         (VOID) XENBUS_EVTCHN(Bind,
                              &Transmitter->EvtchnInterface,
@@ -3587,7 +3634,6 @@ __TransmitterRingDisable(
     __TransmitterRingAcquireLock(Ring);
 
     ASSERT(Ring->Enabled);
-    Ring->Enabled = FALSE;
 
     // Release any fragments associated with a pending packet
     Packet = __TransmitterRingUnprepareFragments(Ring);
@@ -3636,7 +3682,7 @@ __TransmitterRingDisable(
 
         // Try to move things along
         __TransmitterRingSend(Ring);
-        TransmitterRingPoll(Ring);
+        (VOID) TransmitterRingPoll(Ring);
 
         if (State != XenbusStateConnected)
             __TransmitterRingFakeResponses(Ring);
@@ -3649,6 +3695,8 @@ __TransmitterRingDisable(
         KeStallExecutionProcessor(1000);    // 1ms
     }
 
+    Ring->Enabled = FALSE;
+
     __TransmitterRingReleaseLock(Ring);
 }
 
@@ -3751,6 +3799,10 @@ __TransmitterRingTeardown(
     Transmitter = Ring->Transmitter;
     Frontend = Transmitter->Frontend;
 
+    Ring->Dpcs = 0;
+
+    RtlZeroMemory(&Ring->TimerDpc, sizeof (KDPC));
+    RtlZeroMemory(&Ring->Timer, sizeof (KTIMER));
     RtlZeroMemory(&Ring->Dpc, sizeof (KDPC));
 
     ASSERT3U(Ring->PacketsCompleted, ==, Ring->PacketsSent);
@@ -4787,7 +4839,8 @@ TransmitterNotify(
 
     Ring = Transmitter->Ring[Index];
 
-    (VOID) KeInsertQueueDpc(&Ring->Dpc, NULL, NULL);
+    if (KeInsertQueueDpc(&Ring->Dpc, NULL, NULL))
+        Ring->Dpcs++;
 }
 
 VOID