usb: Don't allow USB_RET_ASYNC for interrupt packets
authorHans de Goede <hdegoede@redhat.com>
Sat, 17 Nov 2012 11:47:15 +0000 (12:47 +0100)
committerGerd Hoffmann <kraxel@redhat.com>
Tue, 4 Dec 2012 13:41:54 +0000 (14:41 +0100)
It is tempting to use USB_RET_ASYNC for interrupt packets, rather then the
current NAK + polling approach, but this causes issues for migration, as
an async completed packet will not getting written back to guest memory until
the next poll time, and if a migration happens in between it will get lost!

Make an exception for host devices, because:
1) host-linux actually uses async completion for interrupt endpoints
2) host devices don't migrate anyways

Ideally we would convert host-linux.c to handle (input) interrupt endpoints in
a buffered manner like it does for isoc endpoints, keeping multiple urbs
submitted to ensure the devices timing requirements are met, as well as making
its interrupt ep handling the same as other usb-devices.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
hw/usb.h
hw/usb/core.c
hw/usb/host-bsd.c
hw/usb/host-linux.c

index 7d6de69ec49f5df8f11e4a9f0feb5b67b061aa0d..58f812f7d013456179f3f2c5c93e32ed1beec8d4 100644 (file)
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -197,6 +197,7 @@ struct USBEndpoint {
 
 enum USBDeviceFlags {
     USB_DEV_FLAG_FULL_PATH,
+    USB_DEV_FLAG_IS_HOST,
 };
 
 /* definition of a USB device */
index 52b53108cdaf00339ad5974e8ed7fc6cffddb2bb..8e360d3ec03dfbe122b89e0a6ccad961ed35fe6b 100644 (file)
@@ -406,7 +406,11 @@ void usb_handle_packet(USBDevice *dev, USBPacket *p)
     if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
         usb_process_one(p);
         if (p->status == USB_RET_ASYNC) {
+            /* hcd drivers cannot handle async for isoc */
             assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
+            /* using async for interrupt packets breaks migration */
+            assert(p->ep->type != USB_ENDPOINT_XFER_INT ||
+                   (dev->flags & USB_DEV_FLAG_IS_HOST));
             usb_packet_set_state(p, USB_PACKET_ASYNC);
             QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
         } else if (p->status == USB_RET_ADD_TO_QUEUE) {
index 6473e8b74780c213dbfb5804da68607866b89262..dae000937881a13e91fb9b394d3572e521c570f6 100644 (file)
@@ -292,6 +292,7 @@ static void usb_host_handle_destroy(USBDevice *opaque)
 
 static int usb_host_initfn(USBDevice *dev)
 {
+    dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
     return 0;
 }
 
index aa77b7704da29630da8ad6f082d67b22daf03262..bdafb6bc87a9cd88218dcdba645c6d1a762c2326 100644 (file)
@@ -1476,6 +1476,7 @@ static int usb_host_initfn(USBDevice *dev)
 {
     USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
 
+    dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
     dev->auto_attach = 0;
     s->fd = -1;
     s->hub_fd = -1;