peterdelevoryas / rpms / qemu

Forked from rpms/qemu 2 years ago
Clone

Blame 0332-xhci-drop-buffering.patch

93b7e3
From ba184fe5a0e63bd40956b456d85a01da13d6183d Mon Sep 17 00:00:00 2001
c8dfc6
From: Gerd Hoffmann <kraxel@redhat.com>
c8dfc6
Date: Fri, 17 Aug 2012 11:04:36 +0200
93b7e3
Subject: [PATCH 332/369] xhci: drop buffering
c8dfc6
c8dfc6
This patch splits the xhci_xfer_data function into three.
c8dfc6
The xhci_xfer_data function used to do does two things:
c8dfc6
c8dfc6
  (1) copy transfer data between guest memory and a temporary buffer.
c8dfc6
  (2) report transfer results to the guest using events.
c8dfc6
c8dfc6
Now we three functions to handle this:
c8dfc6
c8dfc6
  (1) xhci_xfer_map creates a scatter list for the transfer and
c8dfc6
      uses that (instead of the temporary buffer) to build a
c8dfc6
      USBPacket.
c8dfc6
  (2) xhci_xfer_unmap undoes the mapping.
c8dfc6
  (3) xhci_xfer_report sends out events.
c8dfc6
c8dfc6
The patch also fixes reporting of transaction errors which must be
c8dfc6
reported unconditinally, not only in case the guest asks for it
c8dfc6
using the ISP flag.
c8dfc6
93b7e3
[ v2: fix warning ]
93b7e3
c8dfc6
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
c8dfc6
---
93b7e3
 hw/usb/hcd-xhci.c | 185 +++++++++++++++++++++---------------------------------
c8dfc6
 trace-events      |   2 +-
93b7e3
 2 files changed, 72 insertions(+), 115 deletions(-)
c8dfc6
c8dfc6
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
93b7e3
index c0a2476..446d692 100644
c8dfc6
--- a/hw/usb/hcd-xhci.c
c8dfc6
+++ b/hw/usb/hcd-xhci.c
c8dfc6
@@ -305,6 +305,7 @@ typedef struct XHCIState XHCIState;
c8dfc6
 typedef struct XHCITransfer {
c8dfc6
     XHCIState *xhci;
c8dfc6
     USBPacket packet;
c8dfc6
+    QEMUSGList sgl;
c8dfc6
     bool running_async;
c8dfc6
     bool running_retry;
c8dfc6
     bool cancelled;
c8dfc6
@@ -319,10 +320,6 @@ typedef struct XHCITransfer {
c8dfc6
     unsigned int trb_alloced;
c8dfc6
     XHCITRB *trbs;
c8dfc6
 
c8dfc6
-    unsigned int data_length;
c8dfc6
-    unsigned int data_alloced;
c8dfc6
-    uint8_t *data;
c8dfc6
-
c8dfc6
     TRBCCode status;
c8dfc6
 
c8dfc6
     unsigned int pkts;
c8dfc6
@@ -906,14 +903,9 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
c8dfc6
         if (t->trbs) {
c8dfc6
             g_free(t->trbs);
c8dfc6
         }
c8dfc6
-        if (t->data) {
c8dfc6
-            g_free(t->data);
c8dfc6
-        }
c8dfc6
 
c8dfc6
         t->trbs = NULL;
c8dfc6
-        t->data = NULL;
c8dfc6
         t->trb_count = t->trb_alloced = 0;
c8dfc6
-        t->data_length = t->data_alloced = 0;
c8dfc6
         xferi = (xferi + 1) % TD_QUEUE;
c8dfc6
     }
c8dfc6
     return killed;
c8dfc6
@@ -1072,24 +1064,13 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
c8dfc6
     return CC_SUCCESS;
c8dfc6
 }
c8dfc6
 
c8dfc6
-static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
c8dfc6
-                          unsigned int length, bool in_xfer, bool out_xfer,
c8dfc6
-                          bool report)
c8dfc6
+static int xhci_xfer_map(XHCITransfer *xfer)
c8dfc6
 {
c8dfc6
-    int i;
c8dfc6
-    uint32_t edtla = 0;
c8dfc6
-    unsigned int transferred = 0;
c8dfc6
-    unsigned int left = length;
c8dfc6
-    bool reported = 0;
c8dfc6
-    bool shortpkt = 0;
c8dfc6
-    XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
c8dfc6
+    int in_xfer = (xfer->packet.pid == USB_TOKEN_IN);
c8dfc6
     XHCIState *xhci = xfer->xhci;
c8dfc6
+    int i;
c8dfc6
 
c8dfc6
-    DPRINTF("xhci_xfer_data(len=%d, in_xfer=%d, out_xfer=%d, report=%d)\n",
c8dfc6
-            length, in_xfer, out_xfer, report);
c8dfc6
-
c8dfc6
-    assert(!(in_xfer && out_xfer));
c8dfc6
-
c8dfc6
+    pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count);
c8dfc6
     for (i = 0; i < xfer->trb_count; i++) {
c8dfc6
         XHCITRB *trb = &xfer->trbs[i];
c8dfc6
         dma_addr_t addr;
c8dfc6
@@ -1099,54 +1080,70 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
c8dfc6
         case TR_DATA:
c8dfc6
             if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) {
c8dfc6
                 fprintf(stderr, "xhci: data direction mismatch for TR_DATA\n");
c8dfc6
-                xhci_die(xhci);
c8dfc6
-                return transferred;
c8dfc6
+                goto err;
c8dfc6
             }
c8dfc6
             /* fallthrough */
c8dfc6
         case TR_NORMAL:
c8dfc6
         case TR_ISOCH:
c8dfc6
             addr = xhci_mask64(trb->parameter);
c8dfc6
             chunk = trb->status & 0x1ffff;
c8dfc6
+            if (trb->control & TRB_TR_IDT) {
c8dfc6
+                if (chunk > 8 || in_xfer) {
c8dfc6
+                    fprintf(stderr, "xhci: invalid immediate data TRB\n");
c8dfc6
+                    goto err;
c8dfc6
+                }
c8dfc6
+                qemu_sglist_add(&xfer->sgl, trb->addr, chunk);
c8dfc6
+            } else {
c8dfc6
+                qemu_sglist_add(&xfer->sgl, addr, chunk);
c8dfc6
+            }
c8dfc6
+            break;
c8dfc6
+        }
c8dfc6
+    }
c8dfc6
+
c8dfc6
+    usb_packet_map(&xfer->packet, &xfer->sgl);
c8dfc6
+    return 0;
c8dfc6
+
c8dfc6
+err:
c8dfc6
+    qemu_sglist_destroy(&xfer->sgl);
c8dfc6
+    xhci_die(xhci);
c8dfc6
+    return -1;
c8dfc6
+}
c8dfc6
+
c8dfc6
+static void xhci_xfer_unmap(XHCITransfer *xfer)
c8dfc6
+{
c8dfc6
+    usb_packet_unmap(&xfer->packet, &xfer->sgl);
c8dfc6
+    qemu_sglist_destroy(&xfer->sgl);
c8dfc6
+}
c8dfc6
+
c8dfc6
+static void xhci_xfer_report(XHCITransfer *xfer)
c8dfc6
+{
c8dfc6
+    uint32_t edtla = 0;
c8dfc6
+    unsigned int left;
c8dfc6
+    bool reported = 0;
c8dfc6
+    bool shortpkt = 0;
c8dfc6
+    XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
c8dfc6
+    XHCIState *xhci = xfer->xhci;
c8dfc6
+    int i;
c8dfc6
+
c8dfc6
+    left = xfer->packet.result < 0 ? 0 : xfer->packet.result;
c8dfc6
+
c8dfc6
+    for (i = 0; i < xfer->trb_count; i++) {
c8dfc6
+        XHCITRB *trb = &xfer->trbs[i];
c8dfc6
+        unsigned int chunk = 0;
c8dfc6
+
c8dfc6
+        switch (TRB_TYPE(*trb)) {
c8dfc6
+        case TR_DATA:
c8dfc6
+        case TR_NORMAL:
c8dfc6
+        case TR_ISOCH:
c8dfc6
+            chunk = trb->status & 0x1ffff;
c8dfc6
             if (chunk > left) {
c8dfc6
                 chunk = left;
c8dfc6
-                shortpkt = 1;
c8dfc6
-            }
c8dfc6
-            if (in_xfer || out_xfer) {
c8dfc6
-                if (trb->control & TRB_TR_IDT) {
c8dfc6
-                    uint64_t idata;
c8dfc6
-                    if (chunk > 8 || in_xfer) {
c8dfc6
-                        fprintf(stderr, "xhci: invalid immediate data TRB\n");
c8dfc6
-                        xhci_die(xhci);
c8dfc6
-                        return transferred;
c8dfc6
-                    }
c8dfc6
-                    idata = le64_to_cpu(trb->parameter);
c8dfc6
-                    memcpy(data, &idata, chunk);
c8dfc6
-                } else {
c8dfc6
-                    DPRINTF("xhci_xfer_data: r/w(%d) %d bytes at "
c8dfc6
-                            DMA_ADDR_FMT "\n", in_xfer, chunk, addr);
c8dfc6
-                    if (in_xfer) {
c8dfc6
-                        pci_dma_write(&xhci->pci_dev, addr, data, chunk);
c8dfc6
-                    } else {
c8dfc6
-                        pci_dma_read(&xhci->pci_dev, addr, data, chunk);
c8dfc6
-                    }
c8dfc6
-#ifdef DEBUG_DATA
c8dfc6
-                    unsigned int count = chunk;
c8dfc6
-                    int i;
c8dfc6
-                    if (count > 16) {
c8dfc6
-                        count = 16;
c8dfc6
-                    }
c8dfc6
-                    DPRINTF(" ::");
c8dfc6
-                    for (i = 0; i < count; i++) {
c8dfc6
-                        DPRINTF(" %02x", data[i]);
c8dfc6
-                    }
c8dfc6
-                    DPRINTF("\n");
c8dfc6
-#endif
c8dfc6
+                if (xfer->status == CC_SUCCESS) {
c8dfc6
+                    shortpkt = 1;
c8dfc6
                 }
c8dfc6
             }
c8dfc6
             left -= chunk;
c8dfc6
-            data += chunk;
c8dfc6
             edtla += chunk;
c8dfc6
-            transferred += chunk;
c8dfc6
             break;
c8dfc6
         case TR_STATUS:
c8dfc6
             reported = 0;
c8dfc6
@@ -1154,8 +1151,9 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
c8dfc6
             break;
c8dfc6
         }
c8dfc6
 
c8dfc6
-        if (report && !reported && (trb->control & TRB_TR_IOC ||
c8dfc6
-            (shortpkt && (trb->control & TRB_TR_ISP)))) {
c8dfc6
+        if (!reported && ((trb->control & TRB_TR_IOC) ||
c8dfc6
+                          (shortpkt && (trb->control & TRB_TR_ISP)) ||
c8dfc6
+                          (xfer->status != CC_SUCCESS))) {
c8dfc6
             event.slotid = xfer->slotid;
c8dfc6
             event.epid = xfer->epid;
c8dfc6
             event.length = (trb->status & 0x1ffff) - chunk;
c8dfc6
@@ -1175,9 +1173,11 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
c8dfc6
             }
c8dfc6
             xhci_event(xhci, &event);
c8dfc6
             reported = 1;
c8dfc6
+            if (xfer->status != CC_SUCCESS) {
c8dfc6
+                return;
c8dfc6
+            }
c8dfc6
         }
c8dfc6
     }
c8dfc6
-    return transferred;
c8dfc6
 }
c8dfc6
 
c8dfc6
 static void xhci_stall_ep(XHCITransfer *xfer)
c8dfc6
@@ -1204,7 +1204,7 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
c8dfc6
     dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
c8dfc6
     ep = usb_ep_get(dev, dir, xfer->epid >> 1);
c8dfc6
     usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr);
c8dfc6
-    usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
c8dfc6
+    xhci_xfer_map(xfer);
c8dfc6
     DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
c8dfc6
             xfer->packet.pid, dev->addr, ep->nr);
c8dfc6
     return 0;
c8dfc6
@@ -1230,12 +1230,13 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
c8dfc6
         xfer->running_async = 0;
c8dfc6
         xfer->running_retry = 0;
c8dfc6
         xfer->complete = 1;
c8dfc6
+        xhci_xfer_unmap(xfer);
c8dfc6
     }
c8dfc6
 
c8dfc6
     if (ret >= 0) {
c8dfc6
-        xfer->status = CC_SUCCESS;
c8dfc6
-        xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1);
c8dfc6
         trace_usb_xhci_xfer_success(xfer, ret);
c8dfc6
+        xfer->status = CC_SUCCESS;
c8dfc6
+        xhci_xfer_report(xfer);
c8dfc6
         return 0;
c8dfc6
     }
c8dfc6
 
c8dfc6
@@ -1244,12 +1245,12 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
c8dfc6
     switch (ret) {
c8dfc6
     case USB_RET_NODEV:
c8dfc6
         xfer->status = CC_USB_TRANSACTION_ERROR;
c8dfc6
-        xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
c8dfc6
+        xhci_xfer_report(xfer);
c8dfc6
         xhci_stall_ep(xfer);
c8dfc6
         break;
c8dfc6
     case USB_RET_STALL:
c8dfc6
         xfer->status = CC_STALL_ERROR;
c8dfc6
-        xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
c8dfc6
+        xhci_xfer_report(xfer);
c8dfc6
         xhci_stall_ep(xfer);
c8dfc6
         break;
c8dfc6
     default:
93b7e3
@@ -1271,7 +1272,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
93b7e3
 {
93b7e3
     XHCITRB *trb_setup, *trb_status;
93b7e3
     uint8_t bmRequestType;
93b7e3
-    uint16_t wLength;
93b7e3
     XHCIPort *port;
93b7e3
     USBDevice *dev;
93b7e3
     int ret;
93b7e3
@@ -1279,8 +1279,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
c8dfc6
     trb_setup = &xfer->trbs[0];
c8dfc6
     trb_status = &xfer->trbs[xfer->trb_count-1];
c8dfc6
 
c8dfc6
-    trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid,
c8dfc6
-                              trb_setup->parameter >> 48);
c8dfc6
+    trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
c8dfc6
 
c8dfc6
     /* at most one Event Data TRB allowed after STATUS */
c8dfc6
     if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
93b7e3
@@ -1309,19 +1308,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
93b7e3
     }
c8dfc6
 
93b7e3
     bmRequestType = trb_setup->parameter;
93b7e3
-    wLength = trb_setup->parameter >> 48;
93b7e3
-
c8dfc6
-    if (xfer->data && xfer->data_alloced < wLength) {
c8dfc6
-        xfer->data_alloced = 0;
c8dfc6
-        g_free(xfer->data);
c8dfc6
-        xfer->data = NULL;
c8dfc6
-    }
c8dfc6
-    if (!xfer->data) {
c8dfc6
-        DPRINTF("xhci: alloc %d bytes data\n", wLength);
c8dfc6
-        xfer->data = g_malloc(wLength+1);
c8dfc6
-        xfer->data_alloced = wLength;
c8dfc6
-    }
c8dfc6
-    xfer->data_length = wLength;
93b7e3
 
c8dfc6
     port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
c8dfc6
     dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
93b7e3
@@ -1336,9 +1322,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
c8dfc6
 
c8dfc6
     xhci_setup_packet(xfer, dev);
c8dfc6
     xfer->packet.parameter = trb_setup->parameter;
c8dfc6
-    if (!xfer->in_xfer) {
c8dfc6
-        xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0);
c8dfc6
-    }
c8dfc6
 
c8dfc6
     ret = usb_handle_packet(dev, &xfer->packet);
c8dfc6
 
93b7e3
@@ -1359,16 +1342,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
c8dfc6
 
c8dfc6
     xfer->in_xfer = epctx->type>>2;
c8dfc6
 
c8dfc6
-    if (xfer->data && xfer->data_alloced < xfer->data_length) {
c8dfc6
-        xfer->data_alloced = 0;
c8dfc6
-        g_free(xfer->data);
c8dfc6
-        xfer->data = NULL;
c8dfc6
-    }
c8dfc6
-    if (!xfer->data && xfer->data_length) {
c8dfc6
-        DPRINTF("xhci: alloc %d bytes data\n", xfer->data_length);
c8dfc6
-        xfer->data = g_malloc(xfer->data_length);
c8dfc6
-        xfer->data_alloced = xfer->data_length;
c8dfc6
-    }
c8dfc6
     if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) {
c8dfc6
         xfer->pkts = 1;
c8dfc6
     } else {
93b7e3
@@ -1402,9 +1375,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
c8dfc6
         return -1;
c8dfc6
     }
c8dfc6
 
c8dfc6
-    if (!xfer->in_xfer) {
c8dfc6
-        xhci_xfer_data(xfer, xfer->data, xfer->data_length, 0, 1, 0);
c8dfc6
-    }
c8dfc6
     ret = usb_handle_packet(dev, &xfer->packet);
c8dfc6
 
c8dfc6
     xhci_complete_packet(xfer, ret);
93b7e3
@@ -1416,20 +1386,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
c8dfc6
 
c8dfc6
 static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
c8dfc6
 {
c8dfc6
-    int i;
c8dfc6
-    unsigned int length = 0;
c8dfc6
-    XHCITRB *trb;
c8dfc6
-
c8dfc6
-    for (i = 0; i < xfer->trb_count; i++) {
c8dfc6
-        trb = &xfer->trbs[i];
c8dfc6
-        if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) {
c8dfc6
-            length += trb->status & 0x1ffff;
c8dfc6
-        }
c8dfc6
-    }
c8dfc6
-
c8dfc6
-    trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length);
c8dfc6
-
c8dfc6
-    xfer->data_length = length;
c8dfc6
+    trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
c8dfc6
     return xhci_submit(xhci, xfer, epctx);
c8dfc6
 }
c8dfc6
 
c8dfc6
diff --git a/trace-events b/trace-events
c8dfc6
index 10bc04e..c83d65e 100644
c8dfc6
--- a/trace-events
c8dfc6
+++ b/trace-events
c8dfc6
@@ -326,7 +326,7 @@ usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
c8dfc6
 usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
c8dfc6
 usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
c8dfc6
 usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
c8dfc6
-usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t length) "%p: slotid %d, epid %d, length %d"
c8dfc6
+usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid) "%p: slotid %d, epid %d"
c8dfc6
 usb_xhci_xfer_async(void *xfer) "%p"
c8dfc6
 usb_xhci_xfer_nak(void *xfer) "%p"
c8dfc6
 usb_xhci_xfer_retry(void *xfer) "%p"
c8dfc6
-- 
c8dfc6
1.7.12
c8dfc6