Blame 0039-xhci-emulate-intr-endpoint-intervals-correctly.patch

298366
From dc6fbaa8322ca53f46d9a6cc7e2f82de5362ea83 Mon Sep 17 00:00:00 2001
298366
From: Gerd Hoffmann <kraxel@redhat.com>
298366
Date: Wed, 28 Aug 2013 11:38:44 +0200
298366
Subject: [PATCH] xhci: emulate intr endpoint intervals correctly
298366
298366
Respect the interval for interrupt endpoints, so we don't finish
298366
transfers as fast as possible but at the rate configured by the guest.
298366
298366
Fixes guest deadlocks triggered by interrupt storms.
298366
298366
Cc:
298366
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
298366
(cherry picked from commit 4d7a81c06f5f17e019a2d3a18300500bd64f6f40)
298366
298366
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
298366
---
298366
 hw/usb/hcd-xhci.c | 44 +++++++++++++++++++++++++++++++++++++-------
298366
 1 file changed, 37 insertions(+), 7 deletions(-)
298366
298366
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
298366
index a6f55a1..8010a6d 100644
298366
--- a/hw/usb/hcd-xhci.c
298366
+++ b/hw/usb/hcd-xhci.c
298366
@@ -355,6 +355,7 @@ typedef struct XHCITransfer {
298366
     unsigned int streamid;
298366
     bool in_xfer;
298366
     bool iso_xfer;
298366
+    bool timed_xfer;
298366
 
298366
     unsigned int trb_count;
298366
     unsigned int trb_alloced;
298366
@@ -1803,6 +1804,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
298366
 
298366
     xfer->in_xfer = bmRequestType & USB_DIR_IN;
298366
     xfer->iso_xfer = false;
298366
+    xfer->timed_xfer = false;
298366
 
298366
     if (xhci_setup_packet(xfer) < 0) {
298366
         return -1;
298366
@@ -1818,6 +1820,17 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
298366
     return 0;
298366
 }
298366
 
298366
+static void xhci_calc_intr_kick(XHCIState *xhci, XHCITransfer *xfer,
298366
+                                XHCIEPContext *epctx, uint64_t mfindex)
298366
+{
298366
+    uint64_t asap = ((mfindex + epctx->interval - 1) &
298366
+                     ~(epctx->interval-1));
298366
+    uint64_t kick = epctx->mfindex_last + epctx->interval;
298366
+
298366
+    assert(epctx->interval != 0);
298366
+    xfer->mfindex_kick = MAX(asap, kick);
298366
+}
298366
+
298366
 static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
298366
                                XHCIEPContext *epctx, uint64_t mfindex)
298366
 {
298366
@@ -1840,8 +1853,8 @@ static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
298366
     }
298366
 }
298366
 
298366
-static void xhci_check_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
298366
-                                XHCIEPContext *epctx, uint64_t mfindex)
298366
+static void xhci_check_intr_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
298366
+                                     XHCIEPContext *epctx, uint64_t mfindex)
298366
 {
298366
     if (xfer->mfindex_kick > mfindex) {
298366
         qemu_mod_timer(epctx->kick_timer, qemu_get_clock_ns(vm_clock) +
298366
@@ -1866,18 +1879,30 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
298366
     switch(epctx->type) {
298366
     case ET_INTR_OUT:
298366
     case ET_INTR_IN:
298366
+        xfer->pkts = 0;
298366
+        xfer->iso_xfer = false;
298366
+        xfer->timed_xfer = true;
298366
+        mfindex = xhci_mfindex_get(xhci);
298366
+        xhci_calc_intr_kick(xhci, xfer, epctx, mfindex);
298366
+        xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex);
298366
+        if (xfer->running_retry) {
298366
+            return -1;
298366
+        }
298366
+        break;
298366
     case ET_BULK_OUT:
298366
     case ET_BULK_IN:
298366
         xfer->pkts = 0;
298366
         xfer->iso_xfer = false;
298366
+        xfer->timed_xfer = false;
298366
         break;
298366
     case ET_ISO_OUT:
298366
     case ET_ISO_IN:
298366
         xfer->pkts = 1;
298366
         xfer->iso_xfer = true;
298366
+        xfer->timed_xfer = true;
298366
         mfindex = xhci_mfindex_get(xhci);
298366
         xhci_calc_iso_kick(xhci, xfer, epctx, mfindex);
298366
-        xhci_check_iso_kick(xhci, xfer, epctx, mfindex);
298366
+        xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex);
298366
         if (xfer->running_retry) {
298366
             return -1;
298366
         }
298366
@@ -1938,13 +1963,18 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
298366
 
298366
         trace_usb_xhci_xfer_retry(xfer);
298366
         assert(xfer->running_retry);
298366
-        if (xfer->iso_xfer) {
298366
-            /* retry delayed iso transfer */
298366
+        if (xfer->timed_xfer) {
298366
+            /* time to kick the transfer? */
298366
             mfindex = xhci_mfindex_get(xhci);
298366
-            xhci_check_iso_kick(xhci, xfer, epctx, mfindex);
298366
+            xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex);
298366
             if (xfer->running_retry) {
298366
                 return;
298366
             }
298366
+            xfer->timed_xfer = 0;
298366
+            xfer->running_retry = 1;
298366
+        }
298366
+        if (xfer->iso_xfer) {
298366
+            /* retry iso transfer */
298366
             if (xhci_setup_packet(xfer) < 0) {
298366
                 return;
298366
             }
298366
@@ -2030,7 +2060,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
298366
                 epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
298366
                 ep = xfer->packet.ep;
298366
             } else {
298366
-                if (!xfer->iso_xfer) {
298366
+                if (!xfer->timed_xfer) {
298366
                     fprintf(stderr, "xhci: error firing data transfer\n");
298366
                 }
298366
             }