Patchwork usb-host: add timeout handler

login
register
mail settings
Submitter Gerd Hoffmann
Date April 24, 2012, 1:04 p.m.
Message ID <1335272697-3274-1-git-send-email-kraxel@redhat.com>
Download mbox | patch
Permalink /patch/154682/
State New
Headers show

Comments

Gerd Hoffmann - April 24, 2012, 1:04 p.m.
Add a timeout handler.  In case bulk transfers take too long to finish
the request will be canceled.  The timeout is tunable via property, by
default it is 5 seconds.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/host-linux.c |   27 +++++++++++++++++++++++++++
 trace-events        |    1 +
 2 files changed, 28 insertions(+), 0 deletions(-)

Patch

diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index 048f8ff..616e51c 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -110,6 +110,8 @@  typedef struct USBHostDevice {
     int       closing;
     uint32_t  iso_urb_count;
     uint32_t  options;
+    uint32_t  timeout_secs;
+    uint32_t  timeout_count;
     Notifier  exit;
 
     struct endp_data ep_in[USB_MAX_ENDPOINTS];
@@ -276,6 +278,7 @@  struct AsyncURB
     struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
     USBHostDevice *hdev;
     QLIST_ENTRY(AsyncURB) next;
+    QEMUTimer *timeout;
 
     /* For regular async urbs */
     USBPacket     *packet;
@@ -285,16 +288,34 @@  struct AsyncURB
     int iso_frame_idx; /* -1 means in flight */
 };
 
+static void async_timeout(void *opaque)
+{
+    AsyncURB *aurb = opaque;
+    USBHostDevice *s = aurb->hdev;
+
+    s->timeout_count++;
+    if (s->timeout_count < 10 ||
+        s->timeout_count % 10 == 0) {
+        fprintf(stderr, "husb: urb timeout (%d secs, #%d)\n",
+                s->timeout_secs, s->timeout_count);
+    }
+    trace_usb_host_urb_timeout(s->bus_num, s->addr, aurb);
+    ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
+}
+
 static AsyncURB *async_alloc(USBHostDevice *s)
 {
     AsyncURB *aurb = g_malloc0(sizeof(AsyncURB));
     aurb->hdev = s;
     QLIST_INSERT_HEAD(&s->aurbs, aurb, next);
+    aurb->timeout = qemu_new_timer_ns(vm_clock, async_timeout, aurb);
     return aurb;
 }
 
 static void async_free(AsyncURB *aurb)
 {
+    qemu_del_timer(aurb->timeout);
+    qemu_free_timer(aurb->timeout);
     QLIST_REMOVE(aurb, next);
     g_free(aurb);
 }
@@ -938,6 +959,11 @@  static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
                 return USB_RET_STALL;
             }
         }
+        if (urb->type == USBDEVFS_URB_TYPE_BULK) {
+            qemu_mod_timer(aurb->timeout, qemu_get_clock_ns(vm_clock)
+                           + s->timeout_secs * get_ticks_per_sec());
+        }
+
     } while (rem > 0);
 
     return USB_RET_ASYNC;
@@ -1444,6 +1470,7 @@  static Property usb_host_dev_properties[] = {
     DEFINE_PROP_HEX32("vendorid",  USBHostDevice, match.vendor_id,  0),
     DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
     DEFINE_PROP_UINT32("isobufs",  USBHostDevice, iso_urb_count,    4),
+    DEFINE_PROP_UINT32("timeout",  USBHostDevice, timeout_secs,     5),
     DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex,        -1),
     DEFINE_PROP_BIT("pipeline",    USBHostDevice, options,
                     USB_HOST_OPT_PIPELINE, true),
diff --git a/trace-events b/trace-events
index 87cb96c..710eb28 100644
--- a/trace-events
+++ b/trace-events
@@ -329,6 +329,7 @@  usb_host_req_canceled(int bus, int addr, void *p) "dev %d:%d, packet %p"
 usb_host_urb_submit(int bus, int addr, void *aurb, int length, int more) "dev %d:%d, aurb %p, length %d, more %d"
 usb_host_urb_complete(int bus, int addr, void *aurb, int status, int length, int more) "dev %d:%d, aurb %p, status %d, length %d, more %d"
 usb_host_urb_canceled(int bus, int addr, void *aurb) "dev %d:%d, aurb %p"
+usb_host_urb_timeout(int bus, int addr, void *aurb) "dev %d:%d, aurb %p"
 usb_host_ep_set_halt(int bus, int addr, int ep) "dev %d:%d, ep %d"
 usb_host_ep_clear_halt(int bus, int addr, int ep) "dev %d:%d, ep %d"
 usb_host_ep_start_iso(int bus, int addr, int ep) "dev %d:%d, ep %d"