mvadkert / rpms / qemu

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