mvadkert / rpms / qemu

Forked from rpms/qemu 6 years ago
Clone
5544c1b
From 5d369bd44cf2611f47fec52c7402472cd2436b4a Mon Sep 17 00:00:00 2001
c8dfc65
From: Hans de Goede <hdegoede@redhat.com>
c8dfc65
Date: Mon, 3 Sep 2012 11:01:13 +0200
5544c1b
Subject: [PATCH] ehci: Fix memory leak in handling of NAK-ed packets
c8dfc65
c8dfc65
Currently each time we try to execute a NAK-ed packet we redo
c8dfc65
ehci_init_transfer, and usb_packet_map, re-allocing (without freeing) the
c8dfc65
sg list every time.
c8dfc65
c8dfc65
This patch fixes this, it does this by introducing another async state, so
c8dfc65
that we also properly cleanup a NAK-ed packet on cancel.
c8dfc65
c8dfc65
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
c8dfc65
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
5544c1b
(cherry picked from commit ef5b234477df80700b128f561f5877a0688a70c8)
5544c1b
5544c1b
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
c8dfc65
---
c8dfc65
 hw/usb/hcd-ehci.c | 38 +++++++++++++++++++++++++++-----------
c8dfc65
 1 file changed, 27 insertions(+), 11 deletions(-)
c8dfc65
c8dfc65
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
c8dfc65
index 5a88268..d87aca8 100644
c8dfc65
--- a/hw/usb/hcd-ehci.c
c8dfc65
+++ b/hw/usb/hcd-ehci.c
c8dfc65
@@ -345,6 +345,7 @@ typedef struct EHCIState EHCIState;
c8dfc65
 
c8dfc65
 enum async_state {
c8dfc65
     EHCI_ASYNC_NONE = 0,
c8dfc65
+    EHCI_ASYNC_INITIALIZED,
c8dfc65
     EHCI_ASYNC_INFLIGHT,
c8dfc65
     EHCI_ASYNC_FINISHED,
c8dfc65
 };
c8dfc65
@@ -764,6 +765,10 @@ static void ehci_free_packet(EHCIPacket *p)
c8dfc65
         return;
c8dfc65
     }
c8dfc65
     trace_usb_ehci_packet_action(p->queue, p, "free");
c8dfc65
+    if (p->async == EHCI_ASYNC_INITIALIZED) {
c8dfc65
+        usb_packet_unmap(&p->packet, &p->sgl);
c8dfc65
+        qemu_sglist_destroy(&p->sgl);
c8dfc65
+    }
c8dfc65
     if (p->async == EHCI_ASYNC_INFLIGHT) {
c8dfc65
         usb_cancel_packet(&p->packet);
c8dfc65
         usb_packet_unmap(&p->packet, &p->sgl);
c8dfc65
@@ -1485,8 +1490,8 @@ static void ehci_execute_complete(EHCIQueue *q)
c8dfc65
 
c8dfc65
     assert(p != NULL);
c8dfc65
     assert(p->qtdaddr == q->qtdaddr);
c8dfc65
-    assert(p->async != EHCI_ASYNC_INFLIGHT);
c8dfc65
-    p->async = EHCI_ASYNC_NONE;
c8dfc65
+    assert(p->async == EHCI_ASYNC_INITIALIZED ||
c8dfc65
+           p->async == EHCI_ASYNC_FINISHED);
c8dfc65
 
c8dfc65
     DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
c8dfc65
             q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
c8dfc65
@@ -1531,6 +1536,7 @@ static void ehci_execute_complete(EHCIQueue *q)
c8dfc65
     ehci_finish_transfer(q, p->usb_status);
c8dfc65
     usb_packet_unmap(&p->packet, &p->sgl);
c8dfc65
     qemu_sglist_destroy(&p->sgl);
c8dfc65
+    p->async = EHCI_ASYNC_NONE;
c8dfc65
 
c8dfc65
     q->qh.token ^= QTD_TOKEN_DTOGGLE;
c8dfc65
     q->qh.token &= ~QTD_TOKEN_ACTIVE;
c8dfc65
@@ -1548,6 +1554,9 @@ static int ehci_execute(EHCIPacket *p, const char *action)
c8dfc65
     int ret;
c8dfc65
     int endp;
c8dfc65
 
c8dfc65
+    assert(p->async == EHCI_ASYNC_NONE ||
c8dfc65
+           p->async == EHCI_ASYNC_INITIALIZED);
c8dfc65
+
c8dfc65
     if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) {
c8dfc65
         fprintf(stderr, "Attempting to execute inactive qtd\n");
c8dfc65
         return USB_RET_PROCERR;
c8dfc65
@@ -1576,15 +1585,18 @@ static int ehci_execute(EHCIPacket *p, const char *action)
c8dfc65
         break;
c8dfc65
     }
c8dfc65
 
c8dfc65
-    if (ehci_init_transfer(p) != 0) {
c8dfc65
-        return USB_RET_PROCERR;
c8dfc65
-    }
c8dfc65
-
c8dfc65
     endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
c8dfc65
     ep = usb_ep_get(p->queue->dev, p->pid, endp);
c8dfc65
 
c8dfc65
-    usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
c8dfc65
-    usb_packet_map(&p->packet, &p->sgl);
c8dfc65
+    if (p->async == EHCI_ASYNC_NONE) {
c8dfc65
+        if (ehci_init_transfer(p) != 0) {
c8dfc65
+            return USB_RET_PROCERR;
c8dfc65
+        }
c8dfc65
+
c8dfc65
+        usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
c8dfc65
+        usb_packet_map(&p->packet, &p->sgl);
c8dfc65
+        p->async = EHCI_ASYNC_INITIALIZED;
c8dfc65
+    }
c8dfc65
 
c8dfc65
     trace_usb_ehci_packet_action(p->queue, p, action);
c8dfc65
     ret = usb_handle_packet(p->queue->dev, &p->packet);
c8dfc65
@@ -2021,11 +2033,15 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
c8dfc65
     } else if (p != NULL) {
c8dfc65
         switch (p->async) {
c8dfc65
         case EHCI_ASYNC_NONE:
c8dfc65
+            /* Should never happen packet should at least be initialized */
c8dfc65
+            assert(0);
c8dfc65
+            break;
c8dfc65
+        case EHCI_ASYNC_INITIALIZED:
c8dfc65
             /* Previously nacked packet (likely interrupt ep) */
c8dfc65
-           ehci_set_state(q->ehci, q->async, EST_EXECUTE);
c8dfc65
-           break;
c8dfc65
+            ehci_set_state(q->ehci, q->async, EST_EXECUTE);
c8dfc65
+            break;
c8dfc65
         case EHCI_ASYNC_INFLIGHT:
c8dfc65
-            /* Unfinyshed async handled packet, go horizontal */
c8dfc65
+            /* Unfinished async handled packet, go horizontal */
c8dfc65
             ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
c8dfc65
             break;
c8dfc65
         case EHCI_ASYNC_FINISHED:
c8dfc65
-- 
5544c1b
1.7.12.1
c8dfc65