Blame 0314-ehci-Fix-memory-leak-in-handling-of-NAK-ed-packets.patch

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