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