* [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads @ 2012-09-12 13:08 Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 2/9] ehci: Walk async schedule before and after migration Hans de Goede ` (8 more replies) 0 siblings, 9 replies; 11+ messages in thread From: Hans de Goede @ 2012-09-12 13:08 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel When removing unseen queue-heads from the async queue list, we should not set the seen flag to 0, as this may cause them to be removed by ehci_queues_rip_unused() during the next call to ehci_advance_async_state() if the timer is late or running at a low frequency. Note: 1) This *may* have caused the instant unlink / relinks described in commit 9bc3a3a216e2689bfcdd36c3e079333bbdbf3ba0 2) Rather then putting more if-s inside ehci_queues_rip_unused, this patch instead introduces a new ehci_queues_rip_unseen function. 3) This patch also makes it save to call ehci_queues_rip_unseen() multiple times, which gets used in the folluw up patch titled: "ehci: Walk async schedule before and after migration" Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- hw/usb/hcd-ehci.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 2f3e9c0..c5f2635 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -853,10 +853,10 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, return NULL; } -static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) +static void ehci_queues_rip_unused(EHCIState *ehci, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - const char *warn = (async && !flush) ? "guest unlinked busy QH" : NULL; + const char *warn = async ? "guest unlinked busy QH" : NULL; uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4; EHCIQueue *q, *tmp; @@ -866,13 +866,25 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) q->ts = ehci->last_run_ns; continue; } - if (!flush && ehci->last_run_ns < q->ts + maxage) { + if (ehci->last_run_ns < q->ts + maxage) { continue; } ehci_free_queue(q, warn); } } +static void ehci_queues_rip_unseen(EHCIState *ehci, int async) +{ + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + EHCIQueue *q, *tmp; + + QTAILQ_FOREACH_SAFE(q, head, next, tmp) { + if (!q->seen) { + ehci_free_queue(q, NULL); + } + } +} + static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; @@ -1732,7 +1744,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) ehci_set_usbsts(ehci, USBSTS_REC); } - ehci_queues_rip_unused(ehci, async, 0); + ehci_queues_rip_unused(ehci, async); /* Find the head of the list (4.9.1.1) */ for(i = 0; i < MAX_QH; i++) { @@ -2364,7 +2376,7 @@ static void ehci_advance_async_state(EHCIState *ehci) */ if (ehci->usbcmd & USBCMD_IAAD) { /* Remove all unseen qhs from the async qhs queue */ - ehci_queues_rip_unused(ehci, async, 1); + ehci_queues_rip_unseen(ehci, async); trace_usb_ehci_doorbell_ack(); ehci->usbcmd &= ~USBCMD_IAAD; ehci_raise_irq(ehci, USBSTS_IAA); @@ -2417,7 +2429,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) ehci_set_fetch_addr(ehci, async,entry); ehci_set_state(ehci, async, EST_FETCHENTRY); ehci_advance_state(ehci, async); - ehci_queues_rip_unused(ehci, async, 0); + ehci_queues_rip_unused(ehci, async); break; default: -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH 2/9] ehci: Walk async schedule before and after migration 2012-09-12 13:08 [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Hans de Goede @ 2012-09-12 13:08 ` Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 3/9] usb-redir: Change cancelled packet code into a generic packet-id queue Hans de Goede ` (7 subsequent siblings) 8 siblings, 0 replies; 11+ messages in thread From: Hans de Goede @ 2012-09-12 13:08 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- hw/usb/hcd-ehci.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index c5f2635..e67cbc7 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -34,6 +34,7 @@ #include "monitor.h" #include "trace.h" #include "dma.h" +#include "sysemu.h" #define EHCI_DEBUG 0 @@ -2574,6 +2575,32 @@ static int usb_ehci_post_load(void *opaque, int version_id) return 0; } +static void usb_ehci_vm_state_change(void *opaque, int running, RunState state) +{ + EHCIState *ehci = opaque; + + /* + * We don't migrate the EHCIQueue-s, instead we rebuild them for the + * schedule in guest memory. We must do the rebuilt ASAP, so that + * USB-devices which have async handled packages have a packet in the + * ep queue to match the completion with. + */ + if (state == RUN_STATE_RUNNING) { + ehci_advance_async_state(ehci); + } + + /* + * The schedule rebuilt from guest memory could cause the migration dest + * to miss a QH unlink, and fail to cancel packets, since the unlinked QH + * will never have existed on the destination. Therefor we must flush the + * async schedule on savevm to catch any not yet noticed unlinks. + */ + if (state == RUN_STATE_SAVE_VM) { + ehci_advance_async_state(ehci); + ehci_queues_rip_unseen(ehci, 1); + } +} + static const VMStateDescription vmstate_ehci = { .name = "ehci", .version_id = 2, @@ -2723,6 +2750,7 @@ static int usb_ehci_initfn(PCIDevice *dev) usb_packet_init(&s->ipacket); qemu_register_reset(ehci_reset, s); + qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH 3/9] usb-redir: Change cancelled packet code into a generic packet-id queue 2012-09-12 13:08 [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 2/9] ehci: Walk async schedule before and after migration Hans de Goede @ 2012-09-12 13:08 ` Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 4/9] usb-redir: Add an already_in_flight " Hans de Goede ` (6 subsequent siblings) 8 siblings, 0 replies; 11+ messages in thread From: Hans de Goede @ 2012-09-12 13:08 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- hw/usb/redirect.c | 102 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 5301a69..603262a 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -43,7 +43,6 @@ #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) #define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) -typedef struct Cancelled Cancelled; typedef struct USBRedirDevice USBRedirDevice; /* Struct to hold buffered packets (iso or int input packets) */ @@ -69,6 +68,18 @@ struct endp_data { int bufpq_target_size; }; +struct PacketIdQueueEntry { + uint64_t id; + QTAILQ_ENTRY(PacketIdQueueEntry)next; +}; + +struct PacketIdQueue { + USBRedirDevice *dev; + const char *name; + QTAILQ_HEAD(, PacketIdQueueEntry) head; + int size; +}; + struct USBRedirDevice { USBDevice dev; /* Properties */ @@ -86,7 +97,7 @@ struct USBRedirDevice { int64_t next_attach_time; struct usbredirparser *parser; struct endp_data endpoint[MAX_ENDPOINTS]; - QTAILQ_HEAD(, Cancelled) cancelled; + struct PacketIdQueue cancelled; /* Data for device filtering */ struct usb_redir_device_connect_header device_info; struct usb_redir_interface_info_header interface_info; @@ -94,11 +105,6 @@ struct USBRedirDevice { int filter_rules_count; }; -struct Cancelled { - uint64_t id; - QTAILQ_ENTRY(Cancelled)next; -}; - static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); static void usbredir_device_connect(void *priv, struct usb_redir_device_connect_header *device_connect); @@ -239,37 +245,75 @@ static int usbredir_write(void *priv, uint8_t *data, int count) * Cancelled and buffered packets helpers */ -static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) +static void packet_id_queue_init(struct PacketIdQueue *q, + USBRedirDevice *dev, const char *name) { - USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); - Cancelled *c; + q->dev = dev; + q->name = name; + QTAILQ_INIT(&q->head); + q->size = 0; +} + +static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id) +{ + USBRedirDevice *dev = q->dev; + struct PacketIdQueueEntry *e; + + DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name); + + e = g_malloc0(sizeof(struct PacketIdQueueEntry)); + e->id = id; + QTAILQ_INSERT_TAIL(&q->head, e, next); + q->size++; +} + +static int packet_id_queue_remove(struct PacketIdQueue *q, uint64_t id) +{ + USBRedirDevice *dev = q->dev; + struct PacketIdQueueEntry *e; + + QTAILQ_FOREACH(e, &q->head, next) { + if (e->id == id) { + DPRINTF("removing packet id %"PRIu64" from %s queue\n", + id, q->name); + QTAILQ_REMOVE(&q->head, e, next); + q->size--; + g_free(e); + return 1; + } + } + return 0; +} + +static void packet_id_queue_empty(struct PacketIdQueue *q) +{ + USBRedirDevice *dev = q->dev; + struct PacketIdQueueEntry *e, *next_e; - DPRINTF("cancel packet id %"PRIu64"\n", p->id); + DPRINTF("removing %d packet-ids from %s queue\n", q->size, q->name); - c = g_malloc0(sizeof(Cancelled)); - c->id = p->id; - QTAILQ_INSERT_TAIL(&dev->cancelled, c, next); + QTAILQ_FOREACH_SAFE(e, &q->head, next, next_e) { + QTAILQ_REMOVE(&q->head, e, next); + g_free(e); + } + q->size = 0; +} +static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + + packet_id_queue_add(&dev->cancelled, p->id); usbredirparser_send_cancel_data_packet(dev->parser, p->id); usbredirparser_do_write(dev->parser); } static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id) { - Cancelled *c; - if (!dev->dev.attached) { return 1; /* Treat everything as cancelled after a disconnect */ } - - QTAILQ_FOREACH(c, &dev->cancelled, next) { - if (c->id == id) { - QTAILQ_REMOVE(&dev->cancelled, c, next); - g_free(c); - return 1; - } - } - return 0; + return packet_id_queue_remove(&dev->cancelled, id); } static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, @@ -914,7 +958,7 @@ static int usbredir_initfn(USBDevice *udev) dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev); dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); - QTAILQ_INIT(&dev->cancelled); + packet_id_queue_init(&dev->cancelled, dev, "cancelled"); for (i = 0; i < MAX_ENDPOINTS; i++) { QTAILQ_INIT(&dev->endpoint[i].bufpq); } @@ -933,13 +977,9 @@ static int usbredir_initfn(USBDevice *udev) static void usbredir_cleanup_device_queues(USBRedirDevice *dev) { - Cancelled *c, *next_c; int i; - QTAILQ_FOREACH_SAFE(c, &dev->cancelled, next, next_c) { - QTAILQ_REMOVE(&dev->cancelled, c, next); - g_free(c); - } + packet_id_queue_empty(&dev->cancelled); for (i = 0; i < MAX_ENDPOINTS; i++) { usbredir_free_bufpq(dev, I2EP(i)); } -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH 4/9] usb-redir: Add an already_in_flight packet-id queue 2012-09-12 13:08 [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 2/9] ehci: Walk async schedule before and after migration Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 3/9] usb-redir: Change cancelled packet code into a generic packet-id queue Hans de Goede @ 2012-09-12 13:08 ` Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 5/9] usb-redir: Store max_packet_size in endp_data Hans de Goede ` (5 subsequent siblings) 8 siblings, 0 replies; 11+ messages in thread From: Hans de Goede @ 2012-09-12 13:08 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel After a live migration, the usb-hcd will re-queue all packets by walking over the schedule in the guest memory again, but requests which were encountered on the migration source before will already be in flight, so these should *not* be re-send to the usbredir-host. This patch adds an already in flight packet ud queue, which will be filled by the source before migration and then moved over to the migration dest, any async handled packets are then checked against this queue to avoid sending the same packet to the usbredir-host twice. Signed-off-by: Hans de Goede <hdegoede@redhat,com> --- hw/usb/redirect.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 603262a..f474da8 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -98,6 +98,7 @@ struct USBRedirDevice { struct usbredirparser *parser; struct endp_data endpoint[MAX_ENDPOINTS]; struct PacketIdQueue cancelled; + struct PacketIdQueue already_in_flight; /* Data for device filtering */ struct usb_redir_device_connect_header device_info; struct usb_redir_interface_info_header interface_info; @@ -316,6 +317,34 @@ static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id) return packet_id_queue_remove(&dev->cancelled, id); } +static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev, + struct USBEndpoint *ep) +{ + static USBPacket *p; + + QTAILQ_FOREACH(p, &ep->queue, queue) { + packet_id_queue_add(&dev->already_in_flight, p->id); + } +} + +static void usbredir_fill_already_in_flight(USBRedirDevice *dev) +{ + int ep; + struct USBDevice *udev = &dev->dev; + + usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_ctl); + + for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { + usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_in[ep]); + usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_out[ep]); + } +} + +static int usbredir_already_in_flight(USBRedirDevice *dev, uint64_t id) +{ + return packet_id_queue_remove(&dev->already_in_flight, id); +} + static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, uint8_t ep, uint64_t id) { @@ -531,6 +560,10 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id); + if (usbredir_already_in_flight(dev, p->id)) { + return USB_RET_ASYNC; + } + bulk_packet.endpoint = ep; bulk_packet.length = p->iov.size; bulk_packet.stream_id = 0; @@ -611,6 +644,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id); + if (usbredir_already_in_flight(dev, p->id)) { + return USB_RET_ASYNC; + } + interrupt_packet.endpoint = ep; interrupt_packet.length = p->iov.size; @@ -753,6 +790,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); struct usb_redir_control_packet_header control_packet; + if (usbredir_already_in_flight(dev, p->id)) { + return USB_RET_ASYNC; + } + /* Special cases for certain standard device requests */ switch (request) { case DeviceOutRequest | USB_REQ_SET_ADDRESS: @@ -959,6 +1000,7 @@ static int usbredir_initfn(USBDevice *udev) dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); packet_id_queue_init(&dev->cancelled, dev, "cancelled"); + packet_id_queue_init(&dev->already_in_flight, dev, "already-in-flight"); for (i = 0; i < MAX_ENDPOINTS; i++) { QTAILQ_INIT(&dev->endpoint[i].bufpq); } @@ -980,6 +1022,7 @@ static void usbredir_cleanup_device_queues(USBRedirDevice *dev) int i; packet_id_queue_empty(&dev->cancelled); + packet_id_queue_empty(&dev->already_in_flight); for (i = 0; i < MAX_ENDPOINTS; i++) { usbredir_free_bufpq(dev, I2EP(i)); } -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH 5/9] usb-redir: Store max_packet_size in endp_data 2012-09-12 13:08 [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Hans de Goede ` (2 preceding siblings ...) 2012-09-12 13:08 ` [Qemu-devel] [PATCH 4/9] usb-redir: Add an already_in_flight " Hans de Goede @ 2012-09-12 13:08 ` Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 6/9] usb-redir: Add support for migration Hans de Goede ` (4 subsequent siblings) 8 siblings, 0 replies; 11+ messages in thread From: Hans de Goede @ 2012-09-12 13:08 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel So that we've a place to migrate it to / from to allow restoring it after migration. Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- hw/usb/redirect.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index f474da8..3196665 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -57,6 +57,7 @@ struct endp_data { uint8_t type; uint8_t interval; uint8_t interface; /* bInterfaceNumber this ep belongs to */ + uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */ uint8_t iso_started; uint8_t iso_error; /* For reporting iso errors to the HC */ uint8_t interrupt_started; @@ -1278,7 +1279,8 @@ static void usbredir_ep_info(void *priv, usb_ep->ifnum = dev->endpoint[i].interface; if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_ep_info_max_packet_size)) { - usb_ep->max_packet_size = ep_info->max_packet_size[i]; + dev->endpoint[i].max_packet_size = + usb_ep->max_packet_size = ep_info->max_packet_size[i]; } if (ep_info->type[i] == usb_redir_type_bulk) { usb_ep->pipeline = true; -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH 6/9] usb-redir: Add support for migration 2012-09-12 13:08 [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Hans de Goede ` (3 preceding siblings ...) 2012-09-12 13:08 ` [Qemu-devel] [PATCH 5/9] usb-redir: Store max_packet_size in endp_data Hans de Goede @ 2012-09-12 13:08 ` Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 7/9] usb-redir: Add chardev open / close debug logging Hans de Goede ` (3 subsequent siblings) 8 siblings, 0 replies; 11+ messages in thread From: Hans de Goede @ 2012-09-12 13:08 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- hw/usb/redirect.c | 349 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 346 insertions(+), 3 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 3196665..8d3cf3b 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -65,8 +65,8 @@ struct endp_data { uint8_t bufpq_prefilled; uint8_t bufpq_dropping_packets; QTAILQ_HEAD(, buf_packet) bufpq; - int bufpq_size; - int bufpq_target_size; + int32_t bufpq_size; + int32_t bufpq_target_size; }; struct PacketIdQueueEntry { @@ -240,6 +240,11 @@ static int usbredir_write(void *priv, uint8_t *data, int count) return 0; } + /* Don't send new data to the chardev until our state is fully synced */ + if (!runstate_check(RUN_STATE_RUNNING)) { + return 0; + } + return qemu_chr_fe_write(dev->cs, data, count); } @@ -858,6 +863,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev) { uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; char version[32]; + int flags = 0; /* Make sure any pending closes are handled (no-op if none pending) */ usbredir_chardev_close_bh(dev); @@ -893,7 +899,12 @@ static void usbredir_chardev_open(USBRedirDevice *dev) usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size); usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); - usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0); + + if (runstate_check(RUN_STATE_INMIGRATE)) { + flags |= usbredirparser_fl_no_hello; + } + usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, + flags); usbredirparser_do_write(dev->parser); } @@ -939,6 +950,11 @@ static int usbredir_chardev_can_read(void *opaque) return 0; } + /* Don't read new data from the chardev until our state is fully synced */ + if (!runstate_check(RUN_STATE_RUNNING)) { + return 0; + } + /* usbredir_parser_do_read will consume *all* data we give it */ return 1024 * 1024; } @@ -976,6 +992,15 @@ static void usbredir_chardev_event(void *opaque, int event) * init + destroy */ +static void usbredir_vm_state_change(void *priv, int running, RunState state) +{ + USBRedirDevice *dev = priv; + + if (state == RUN_STATE_RUNNING && dev->parser != NULL) { + usbredirparser_do_write(dev->parser); /* Flush any pending writes */ + } +} + static int usbredir_initfn(USBDevice *udev) { USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); @@ -1014,6 +1039,7 @@ static int usbredir_initfn(USBDevice *udev) qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, usbredir_chardev_read, usbredir_chardev_event, dev); + qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev); add_boot_device_path(dev->bootindex, &udev->qdev, NULL); return 0; } @@ -1503,6 +1529,322 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id, } } +/* + * Migration code + */ + +static void usbredir_pre_save(void *priv) +{ + USBRedirDevice *dev = priv; + + usbredir_fill_already_in_flight(dev); +} + +static int usbredir_post_load(void *priv, int version_id) +{ + USBRedirDevice *dev = priv; + struct USBEndpoint *usb_ep; + int i; + + switch (dev->device_info.speed) { + case usb_redir_speed_low: + dev->dev.speed = USB_SPEED_LOW; + break; + case usb_redir_speed_full: + dev->dev.speed = USB_SPEED_FULL; + break; + case usb_redir_speed_high: + dev->dev.speed = USB_SPEED_HIGH; + break; + case usb_redir_speed_super: + dev->dev.speed = USB_SPEED_SUPER; + break; + default: + dev->dev.speed = USB_SPEED_FULL; + } + dev->dev.speedmask = (1 << dev->dev.speed); + + for (i = 0; i < MAX_ENDPOINTS; i++) { + usb_ep = usb_ep_get(&dev->dev, + (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, + i & 0x0f); + usb_ep->type = dev->endpoint[i].type; + usb_ep->ifnum = dev->endpoint[i].interface; + usb_ep->max_packet_size = dev->endpoint[i].max_packet_size; + if (dev->endpoint[i].type == usb_redir_type_bulk) { + usb_ep->pipeline = true; + } + } + return 0; +} + +/* For usbredirparser migration */ +static void usbredir_put_parser(QEMUFile *f, void *priv, size_t unused) +{ + USBRedirDevice *dev = priv; + uint8_t *data; + int len; + + if (dev->parser == NULL) { + qemu_put_be32(f, 0); + return; + } + + usbredirparser_serialize(dev->parser, &data, &len); + qemu_oom_check(data); + + qemu_put_be32(f, len); + qemu_put_buffer(f, data, len); + + free(data); +} + +static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused) +{ + USBRedirDevice *dev = priv; + uint8_t *data; + int len, ret; + + len = qemu_get_be32(f); + if (len == 0) { + return 0; + } + + /* + * Our chardev should be open already at this point, otherwise + * the usbredir channel will be broken (ie spice without seamless) + */ + if (dev->parser == NULL) { + ERROR("get_parser called with closed chardev, failing migration\n"); + return -1; + } + + data = g_malloc(len); + qemu_get_buffer(f, data, len); + + ret = usbredirparser_unserialize(dev->parser, data, len); + + g_free(data); + + return ret; +} + +static const VMStateInfo usbredir_parser_vmstate_info = { + .name = "usb-redir-parser", + .put = usbredir_put_parser, + .get = usbredir_get_parser, +}; + + +/* For buffered packets (iso/irq) queue migration */ +static void usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused) +{ + struct endp_data *endp = priv; + struct buf_packet *bufp; + int remain = endp->bufpq_size; + + qemu_put_be32(f, endp->bufpq_size); + QTAILQ_FOREACH(bufp, &endp->bufpq, next) { + qemu_put_be32(f, bufp->len); + qemu_put_be32(f, bufp->status); + qemu_put_buffer(f, bufp->data, bufp->len); + remain--; + } + assert(remain == 0); +} + +static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused) +{ + struct endp_data *endp = priv; + struct buf_packet *bufp; + int i; + + endp->bufpq_size = qemu_get_be32(f); + for (i = 0; i < endp->bufpq_size; i++) { + bufp = g_malloc(sizeof(struct buf_packet)); + bufp->len = qemu_get_be32(f); + bufp->status = qemu_get_be32(f); + bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */ + qemu_get_buffer(f, bufp->data, bufp->len); + QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next); + } + return 0; +} + +static const VMStateInfo usbredir_ep_bufpq_vmstate_info = { + .name = "usb-redir-bufpq", + .put = usbredir_put_bufpq, + .get = usbredir_get_bufpq, +}; + + +/* For endp_data migration */ +static const VMStateDescription usbredir_ep_vmstate = { + .name = "usb-redir-ep", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8(type, struct endp_data), + VMSTATE_UINT8(interval, struct endp_data), + VMSTATE_UINT8(interface, struct endp_data), + VMSTATE_UINT16(max_packet_size, struct endp_data), + VMSTATE_UINT8(iso_started, struct endp_data), + VMSTATE_UINT8(iso_error, struct endp_data), + VMSTATE_UINT8(interrupt_started, struct endp_data), + VMSTATE_UINT8(interrupt_error, struct endp_data), + VMSTATE_UINT8(bufpq_prefilled, struct endp_data), + VMSTATE_UINT8(bufpq_dropping_packets, struct endp_data), + { + .name = "bufpq", + .version_id = 0, + .field_exists = NULL, + .size = 0, + .info = &usbredir_ep_bufpq_vmstate_info, + .flags = VMS_SINGLE, + .offset = 0, + }, + VMSTATE_INT32(bufpq_target_size, struct endp_data), + VMSTATE_END_OF_LIST() + } +}; + + +/* For PacketIdQueue migration */ +static void usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused) +{ + struct PacketIdQueue *q = priv; + USBRedirDevice *dev = q->dev; + struct PacketIdQueueEntry *e; + int remain = q->size; + + DPRINTF("put_packet_id_q %s size %d\n", q->name, q->size); + qemu_put_be32(f, q->size); + QTAILQ_FOREACH(e, &q->head, next) { + qemu_put_be64(f, e->id); + remain--; + } + assert(remain == 0); +} + +static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused) +{ + struct PacketIdQueue *q = priv; + USBRedirDevice *dev = q->dev; + int i, size; + uint64_t id; + + size = qemu_get_be32(f); + DPRINTF("get_packet_id_q %s size %d\n", q->name, size); + for (i = 0; i < size; i++) { + id = qemu_get_be64(f); + packet_id_queue_add(q, id); + } + assert(q->size == size); + return 0; +} + +static const VMStateInfo usbredir_ep_packet_id_q_vmstate_info = { + .name = "usb-redir-packet-id-q", + .put = usbredir_put_packet_id_q, + .get = usbredir_get_packet_id_q, +}; + +static const VMStateDescription usbredir_ep_packet_id_queue_vmstate = { + .name = "usb-redir-packet-id-queue", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + { + .name = "queue", + .version_id = 0, + .field_exists = NULL, + .size = 0, + .info = &usbredir_ep_packet_id_q_vmstate_info, + .flags = VMS_SINGLE, + .offset = 0, + }, + VMSTATE_END_OF_LIST() + } +}; + + +/* For usb_redir_device_connect_header migration */ +static const VMStateDescription usbredir_device_info_vmstate = { + .name = "usb-redir-device-info", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8(speed, struct usb_redir_device_connect_header), + VMSTATE_UINT8(device_class, struct usb_redir_device_connect_header), + VMSTATE_UINT8(device_subclass, struct usb_redir_device_connect_header), + VMSTATE_UINT8(device_protocol, struct usb_redir_device_connect_header), + VMSTATE_UINT16(vendor_id, struct usb_redir_device_connect_header), + VMSTATE_UINT16(product_id, struct usb_redir_device_connect_header), + VMSTATE_UINT16(device_version_bcd, + struct usb_redir_device_connect_header), + VMSTATE_END_OF_LIST() + } +}; + + +/* For usb_redir_interface_info_header migration */ +static const VMStateDescription usbredir_interface_info_vmstate = { + .name = "usb-redir-interface-info", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(interface_count, + struct usb_redir_interface_info_header), + VMSTATE_UINT8_ARRAY(interface, + struct usb_redir_interface_info_header, 32), + VMSTATE_UINT8_ARRAY(interface_class, + struct usb_redir_interface_info_header, 32), + VMSTATE_UINT8_ARRAY(interface_subclass, + struct usb_redir_interface_info_header, 32), + VMSTATE_UINT8_ARRAY(interface_protocol, + struct usb_redir_interface_info_header, 32), + VMSTATE_END_OF_LIST() + } +}; + + +/* And finally the USBRedirDevice vmstate itself */ +static const VMStateDescription usbredir_vmstate = { + .name = "usb-redir", + .version_id = 1, + .minimum_version_id = 1, + .pre_save = usbredir_pre_save, + .post_load = usbredir_post_load, + .fields = (VMStateField []) { + VMSTATE_USB_DEVICE(dev, USBRedirDevice), + VMSTATE_TIMER(attach_timer, USBRedirDevice), + { + .name = "parser", + .version_id = 0, + .field_exists = NULL, + .size = 0, + .info = &usbredir_parser_vmstate_info, + .flags = VMS_SINGLE, + .offset = 0, + }, + VMSTATE_STRUCT_ARRAY(endpoint, USBRedirDevice, MAX_ENDPOINTS, 1, + usbredir_ep_vmstate, struct endp_data), + VMSTATE_STRUCT(cancelled, USBRedirDevice, 1, + usbredir_ep_packet_id_queue_vmstate, + struct PacketIdQueue), + VMSTATE_STRUCT(already_in_flight, USBRedirDevice, 1, + usbredir_ep_packet_id_queue_vmstate, + struct PacketIdQueue), + VMSTATE_STRUCT(device_info, USBRedirDevice, 1, + usbredir_device_info_vmstate, + struct usb_redir_device_connect_header), + VMSTATE_STRUCT(interface_info, USBRedirDevice, 1, + usbredir_interface_info_vmstate, + struct usb_redir_interface_info_header), + VMSTATE_END_OF_LIST() + } +}; + static Property usbredir_properties[] = { DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0), @@ -1523,6 +1865,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data) uc->handle_reset = usbredir_handle_reset; uc->handle_data = usbredir_handle_data; uc->handle_control = usbredir_handle_control; + dc->vmsd = &usbredir_vmstate; dc->props = usbredir_properties; } -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH 7/9] usb-redir: Add chardev open / close debug logging 2012-09-12 13:08 [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Hans de Goede ` (4 preceding siblings ...) 2012-09-12 13:08 ` [Qemu-devel] [PATCH 6/9] usb-redir: Add support for migration Hans de Goede @ 2012-09-12 13:08 ` Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 8/9] usb-redir: Revert usb-redir part of commit 93bfef4c Hans de Goede ` (2 subsequent siblings) 8 siblings, 0 replies; 11+ messages in thread From: Hans de Goede @ 2012-09-12 13:08 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- hw/usb/redirect.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 8d3cf3b..e438fd1 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -854,6 +854,7 @@ static void usbredir_chardev_close_bh(void *opaque) usbredir_device_disconnect(dev); if (dev->parser) { + DPRINTF("destroying usbredirparser\n"); usbredirparser_destroy(dev->parser); dev->parser = NULL; } @@ -869,6 +870,8 @@ static void usbredir_chardev_open(USBRedirDevice *dev) usbredir_chardev_close_bh(dev); qemu_bh_cancel(dev->chardev_close_bh); + DPRINTF("creating usbredirparser\n"); + strcpy(version, "qemu usb-redir guest "); pstrcat(version, sizeof(version), qemu_get_version()); @@ -980,9 +983,11 @@ static void usbredir_chardev_event(void *opaque, int event) switch (event) { case CHR_EVENT_OPENED: + DPRINTF("chardev open\n"); usbredir_chardev_open(dev); break; case CHR_EVENT_CLOSED: + DPRINTF("chardev close\n"); qemu_bh_schedule(dev->chardev_close_bh); break; } @@ -1228,6 +1233,7 @@ static void usbredir_device_disconnect(void *priv) qemu_del_timer(dev->attach_timer); if (dev->dev.attached) { + DPRINTF("detaching device\n"); usb_device_detach(&dev->dev); /* * Delay next usb device attach to give the guest a chance to see -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH 8/9] usb-redir: Revert usb-redir part of commit 93bfef4c 2012-09-12 13:08 [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Hans de Goede ` (5 preceding siblings ...) 2012-09-12 13:08 ` [Qemu-devel] [PATCH 7/9] usb-redir: Add chardev open / close debug logging Hans de Goede @ 2012-09-12 13:08 ` Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 9/9] uhci: Don't queue up packets after one with the SPD flag set Hans de Goede 2012-09-13 5:21 ` [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Gerd Hoffmann 8 siblings, 0 replies; 11+ messages in thread From: Hans de Goede @ 2012-09-12 13:08 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel Commit 93bfef4c6e4b23caea9d51e1099d06433d8835a4 makes qemu-devices which report the qemu version string to the guest in some way use a qemu_get_version function which reports a machine-specific version string. However usb-redir does not expose the qemu version to the guest, only to the usbredir-host as part of the initial handshake. This can then be logged on the usbredir-host side for debugging purposes and is otherwise completely unused! For debugging purposes it is important to have the real qemu version in there, rather then the machine-specific version. Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- hw/usb/redirect.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index e438fd1..f327b94 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -142,6 +142,8 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id, static int usbredir_handle_status(USBRedirDevice *dev, int status, int actual_len); +#define VERSION "qemu usb-redir guest " QEMU_VERSION + /* * Logging stuff */ @@ -863,7 +865,6 @@ static void usbredir_chardev_close_bh(void *opaque) static void usbredir_chardev_open(USBRedirDevice *dev) { uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; - char version[32]; int flags = 0; /* Make sure any pending closes are handled (no-op if none pending) */ @@ -872,9 +873,6 @@ static void usbredir_chardev_open(USBRedirDevice *dev) DPRINTF("creating usbredirparser\n"); - strcpy(version, "qemu usb-redir guest "); - pstrcat(version, sizeof(version), qemu_get_version()); - dev->parser = qemu_oom_check(usbredirparser_create()); dev->parser->priv = dev; dev->parser->log_func = usbredir_log; @@ -906,7 +904,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev) if (runstate_check(RUN_STATE_INMIGRATE)) { flags |= usbredirparser_fl_no_hello; } - usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, + usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, flags); usbredirparser_do_write(dev->parser); } -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH 9/9] uhci: Don't queue up packets after one with the SPD flag set 2012-09-12 13:08 [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Hans de Goede ` (6 preceding siblings ...) 2012-09-12 13:08 ` [Qemu-devel] [PATCH 8/9] usb-redir: Revert usb-redir part of commit 93bfef4c Hans de Goede @ 2012-09-12 13:08 ` Hans de Goede 2012-09-13 5:21 ` [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Gerd Hoffmann 8 siblings, 0 replies; 11+ messages in thread From: Hans de Goede @ 2012-09-12 13:08 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel Don't queue up packets after a packet with the SPD (short packet detect) flag set. Since we won't know if the packet will actually be short until it has completed, and if it is short we should stop the queue. This fixes a miniature photoframe emulating a USB cdrom with the windows software for it not working. Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- hw/usb/hcd-uhci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index c7c8786..cdc8bc3 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1000,6 +1000,9 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td) } assert(ret == TD_RESULT_ASYNC_START); assert(int_mask == 0); + if (ptd.ctrl & TD_CTRL_SPD) { + break; + } plink = ptd.link; } } @@ -1097,7 +1100,7 @@ static void uhci_process_frame(UHCIState *s) case TD_RESULT_ASYNC_START: trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf); - if (is_valid(td.link)) { + if (is_valid(td.link) && !(td.ctrl & TD_CTRL_SPD)) { uhci_fill_queue(s, &td); } link = curr_qh ? qh.link : td.link; -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads 2012-09-12 13:08 [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Hans de Goede ` (7 preceding siblings ...) 2012-09-12 13:08 ` [Qemu-devel] [PATCH 9/9] uhci: Don't queue up packets after one with the SPD flag set Hans de Goede @ 2012-09-13 5:21 ` Gerd Hoffmann 8 siblings, 0 replies; 11+ messages in thread From: Gerd Hoffmann @ 2012-09-13 5:21 UTC (permalink / raw) To: Hans de Goede; +Cc: qemu-devel On 09/12/12 15:08, Hans de Goede wrote: > When removing unseen queue-heads from the async queue list, we should not > set the seen flag to 0, as this may cause them to be removed by > ehci_queues_rip_unused() during the next call to ehci_advance_async_state() > if the timer is late or running at a low frequency. Patch series added to usb patch queue. thanks, Gerd ^ permalink raw reply [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads @ 2012-09-12 11:39 Hans de Goede 2012-09-12 11:39 ` [Qemu-devel] [PATCH 2/9] ehci: Walk async schedule before and after migration Hans de Goede 0 siblings, 1 reply; 11+ messages in thread From: Hans de Goede @ 2012-09-12 11:39 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel When removing unseen queue-heads from the async queue list, we should not set the seen flag to 0, as this may cause them to be removed by ehci_queues_rip_unused() during the next call to ehci_advance_async_state() if the timer is late or running at a low frequency. Note: 1) This *may* have caused the instant unlink / relinks described in commit 9bc3a3a216e2689bfcdd36c3e079333bbdbf3ba0 2) Rather then putting more if-s inside ehci_queues_rip_unused, this patch instead introduces a new ehci_queues_rip_unseen function. 3) This patch also makes it save to call ehci_queues_rip_unseen() multiple times, which gets used in the folluw up patch titled: "ehci: Walk async schedule before and after migration" Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- hw/usb/hcd-ehci.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 2f3e9c0..c5f2635 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -853,10 +853,10 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, return NULL; } -static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) +static void ehci_queues_rip_unused(EHCIState *ehci, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - const char *warn = (async && !flush) ? "guest unlinked busy QH" : NULL; + const char *warn = async ? "guest unlinked busy QH" : NULL; uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4; EHCIQueue *q, *tmp; @@ -866,13 +866,25 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) q->ts = ehci->last_run_ns; continue; } - if (!flush && ehci->last_run_ns < q->ts + maxage) { + if (ehci->last_run_ns < q->ts + maxage) { continue; } ehci_free_queue(q, warn); } } +static void ehci_queues_rip_unseen(EHCIState *ehci, int async) +{ + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + EHCIQueue *q, *tmp; + + QTAILQ_FOREACH_SAFE(q, head, next, tmp) { + if (!q->seen) { + ehci_free_queue(q, NULL); + } + } +} + static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; @@ -1732,7 +1744,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) ehci_set_usbsts(ehci, USBSTS_REC); } - ehci_queues_rip_unused(ehci, async, 0); + ehci_queues_rip_unused(ehci, async); /* Find the head of the list (4.9.1.1) */ for(i = 0; i < MAX_QH; i++) { @@ -2364,7 +2376,7 @@ static void ehci_advance_async_state(EHCIState *ehci) */ if (ehci->usbcmd & USBCMD_IAAD) { /* Remove all unseen qhs from the async qhs queue */ - ehci_queues_rip_unused(ehci, async, 1); + ehci_queues_rip_unseen(ehci, async); trace_usb_ehci_doorbell_ack(); ehci->usbcmd &= ~USBCMD_IAAD; ehci_raise_irq(ehci, USBSTS_IAA); @@ -2417,7 +2429,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) ehci_set_fetch_addr(ehci, async,entry); ehci_set_state(ehci, async, EST_FETCHENTRY); ehci_advance_state(ehci, async); - ehci_queues_rip_unused(ehci, async, 0); + ehci_queues_rip_unused(ehci, async); break; default: -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH 2/9] ehci: Walk async schedule before and after migration 2012-09-12 11:39 Hans de Goede @ 2012-09-12 11:39 ` Hans de Goede 0 siblings, 0 replies; 11+ messages in thread From: Hans de Goede @ 2012-09-12 11:39 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- hw/usb/hcd-ehci.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index c5f2635..e67cbc7 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -34,6 +34,7 @@ #include "monitor.h" #include "trace.h" #include "dma.h" +#include "sysemu.h" #define EHCI_DEBUG 0 @@ -2574,6 +2575,32 @@ static int usb_ehci_post_load(void *opaque, int version_id) return 0; } +static void usb_ehci_vm_state_change(void *opaque, int running, RunState state) +{ + EHCIState *ehci = opaque; + + /* + * We don't migrate the EHCIQueue-s, instead we rebuild them for the + * schedule in guest memory. We must do the rebuilt ASAP, so that + * USB-devices which have async handled packages have a packet in the + * ep queue to match the completion with. + */ + if (state == RUN_STATE_RUNNING) { + ehci_advance_async_state(ehci); + } + + /* + * The schedule rebuilt from guest memory could cause the migration dest + * to miss a QH unlink, and fail to cancel packets, since the unlinked QH + * will never have existed on the destination. Therefor we must flush the + * async schedule on savevm to catch any not yet noticed unlinks. + */ + if (state == RUN_STATE_SAVE_VM) { + ehci_advance_async_state(ehci); + ehci_queues_rip_unseen(ehci, 1); + } +} + static const VMStateDescription vmstate_ehci = { .name = "ehci", .version_id = 2, @@ -2723,6 +2750,7 @@ static int usb_ehci_initfn(PCIDevice *dev) usb_packet_init(&s->ipacket); qemu_register_reset(ehci_reset, s); + qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); -- 1.7.12 ^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2012-09-13 5:21 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-09-12 13:08 [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 2/9] ehci: Walk async schedule before and after migration Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 3/9] usb-redir: Change cancelled packet code into a generic packet-id queue Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 4/9] usb-redir: Add an already_in_flight " Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 5/9] usb-redir: Store max_packet_size in endp_data Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 6/9] usb-redir: Add support for migration Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 7/9] usb-redir: Add chardev open / close debug logging Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 8/9] usb-redir: Revert usb-redir part of commit 93bfef4c Hans de Goede 2012-09-12 13:08 ` [Qemu-devel] [PATCH 9/9] uhci: Don't queue up packets after one with the SPD flag set Hans de Goede 2012-09-13 5:21 ` [Qemu-devel] [PATCH 1/9] ehci: Don't set seen to 0 when removing unseen queue-heads Gerd Hoffmann -- strict thread matches above, loose matches on Subject: below -- 2012-09-12 11:39 Hans de Goede 2012-09-12 11:39 ` [Qemu-devel] [PATCH 2/9] ehci: Walk async schedule before and after migration Hans de Goede
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).