9290838
From 89d5a2bf3ecb98ba5bfaf1fd6c63924aeb1b6208 Mon Sep 17 00:00:00 2001
c8dfc65
From: Hans de Goede <hdegoede@redhat.com>
c8dfc65
Date: Wed, 5 Sep 2012 09:21:44 +0200
5544c1b
Subject: [PATCH] usb-redir: Add support for migration
c8dfc65
c8dfc65
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
c8dfc65
---
393f81b
 hw/usb/redirect.c | 349 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
393f81b
 1 file changed, 346 insertions(+), 3 deletions(-)
c8dfc65
c8dfc65
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
5544c1b
index 6eb3c6d..b7387b6 100644
c8dfc65
--- a/hw/usb/redirect.c
c8dfc65
+++ b/hw/usb/redirect.c
c8dfc65
@@ -65,8 +65,8 @@ struct endp_data {
c8dfc65
     uint8_t bufpq_prefilled;
c8dfc65
     uint8_t bufpq_dropping_packets;
c8dfc65
     QTAILQ_HEAD(, buf_packet) bufpq;
c8dfc65
-    int bufpq_size;
c8dfc65
-    int bufpq_target_size;
c8dfc65
+    int32_t bufpq_size;
c8dfc65
+    int32_t bufpq_target_size;
c8dfc65
 };
c8dfc65
 
c8dfc65
 struct PacketIdQueueEntry {
5544c1b
@@ -243,6 +243,11 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
c8dfc65
         return 0;
c8dfc65
     }
c8dfc65
 
c8dfc65
+    /* Don't send new data to the chardev until our state is fully synced */
c8dfc65
+    if (!runstate_check(RUN_STATE_RUNNING)) {
c8dfc65
+        return 0;
c8dfc65
+    }
c8dfc65
+
c8dfc65
     r = qemu_chr_fe_write(dev->cs, data, count);
c8dfc65
 
c8dfc65
     if (r < 0) {
5544c1b
@@ -870,6 +875,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev)
c8dfc65
 {
c8dfc65
     uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
c8dfc65
     char version[32];
c8dfc65
+    int flags = 0;
c8dfc65
 
c8dfc65
     /* Make sure any pending closes are handled (no-op if none pending) */
c8dfc65
     usbredir_chardev_close_bh(dev);
5544c1b
@@ -902,7 +908,12 @@ static void usbredir_chardev_open(USBRedirDevice *dev)
c8dfc65
     usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
c8dfc65
     usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
c8dfc65
     usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
5544c1b
-    usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0);
c8dfc65
+
c8dfc65
+    if (runstate_check(RUN_STATE_INMIGRATE)) {
c8dfc65
+        flags |= usbredirparser_fl_no_hello;
c8dfc65
+    }
5544c1b
+    usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE,
c8dfc65
+                        flags);
c8dfc65
     usbredirparser_do_write(dev->parser);
c8dfc65
 }
c8dfc65
 
5544c1b
@@ -948,6 +959,11 @@ static int usbredir_chardev_can_read(void *opaque)
c8dfc65
         return 0;
c8dfc65
     }
c8dfc65
 
c8dfc65
+    /* Don't read new data from the chardev until our state is fully synced */
c8dfc65
+    if (!runstate_check(RUN_STATE_RUNNING)) {
c8dfc65
+        return 0;
c8dfc65
+    }
c8dfc65
+
c8dfc65
     /* usbredir_parser_do_read will consume *all* data we give it */
c8dfc65
     return 1024 * 1024;
c8dfc65
 }
5544c1b
@@ -999,6 +1015,15 @@ static const QemuChrHandlers usbredir_chr_handlers = {
c8dfc65
  * init + destroy
c8dfc65
  */
c8dfc65
 
c8dfc65
+static void usbredir_vm_state_change(void *priv, int running, RunState state)
c8dfc65
+{
c8dfc65
+    USBRedirDevice *dev = priv;
c8dfc65
+
c8dfc65
+    if (state == RUN_STATE_RUNNING && dev->parser != NULL) {
c8dfc65
+        usbredirparser_do_write(dev->parser); /* Flush any pending writes */
c8dfc65
+    }
c8dfc65
+}
c8dfc65
+
c8dfc65
 static int usbredir_initfn(USBDevice *udev)
c8dfc65
 {
c8dfc65
     USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
5544c1b
@@ -1036,6 +1061,7 @@ static int usbredir_initfn(USBDevice *udev)
c8dfc65
     qemu_chr_fe_open(dev->cs);
c8dfc65
     qemu_chr_add_handlers(dev->cs, &usbredir_chr_handlers, dev);
c8dfc65
 
c8dfc65
+    qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev);
c8dfc65
     add_boot_device_path(dev->bootindex, &udev->qdev, NULL);
c8dfc65
     return 0;
c8dfc65
 }
5544c1b
@@ -1525,6 +1551,322 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
c8dfc65
     }
c8dfc65
 }
c8dfc65
 
c8dfc65
+/*
c8dfc65
+ * Migration code
c8dfc65
+ */
c8dfc65
+
c8dfc65
+static void usbredir_pre_save(void *priv)
c8dfc65
+{
c8dfc65
+    USBRedirDevice *dev = priv;
c8dfc65
+
c8dfc65
+    usbredir_fill_already_in_flight(dev);
c8dfc65
+}
c8dfc65
+
c8dfc65
+static int usbredir_post_load(void *priv, int version_id)
c8dfc65
+{
c8dfc65
+    USBRedirDevice *dev = priv;
c8dfc65
+    struct USBEndpoint *usb_ep;
c8dfc65
+    int i;
c8dfc65
+
393f81b
+    switch (dev->device_info.speed) {
393f81b
+    case usb_redir_speed_low:
393f81b
+        dev->dev.speed = USB_SPEED_LOW;
393f81b
+        break;
393f81b
+    case usb_redir_speed_full:
393f81b
+        dev->dev.speed = USB_SPEED_FULL;
393f81b
+        break;
393f81b
+    case usb_redir_speed_high:
393f81b
+        dev->dev.speed = USB_SPEED_HIGH;
393f81b
+        break;
393f81b
+    case usb_redir_speed_super:
393f81b
+        dev->dev.speed = USB_SPEED_SUPER;
393f81b
+        break;
393f81b
+    default:
393f81b
+        dev->dev.speed = USB_SPEED_FULL;
393f81b
+    }
393f81b
+    dev->dev.speedmask = (1 << dev->dev.speed);
393f81b
+
c8dfc65
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
c8dfc65
+        usb_ep = usb_ep_get(&dev->dev,
c8dfc65
+                            (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
c8dfc65
+                            i & 0x0f);
c8dfc65
+        usb_ep->type = dev->endpoint[i].type;
c8dfc65
+        usb_ep->ifnum = dev->endpoint[i].interface;
c8dfc65
+        usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
c8dfc65
+        if (dev->endpoint[i].type == usb_redir_type_bulk) {
c8dfc65
+            usb_ep->pipeline = true;
c8dfc65
+        }
c8dfc65
+    }
c8dfc65
+    return 0;
c8dfc65
+}
c8dfc65
+
c8dfc65
+/* For usbredirparser migration */
c8dfc65
+static void usbredir_put_parser(QEMUFile *f, void *priv, size_t unused)
c8dfc65
+{
c8dfc65
+    USBRedirDevice *dev = priv;
c8dfc65
+    uint8_t *data;
c8dfc65
+    int len;
c8dfc65
+
c8dfc65
+    if (dev->parser == NULL) {
c8dfc65
+        qemu_put_be32(f, 0);
c8dfc65
+        return;
c8dfc65
+    }
c8dfc65
+
c8dfc65
+    usbredirparser_serialize(dev->parser, &data, &len;;
c8dfc65
+    qemu_oom_check(data);
c8dfc65
+
c8dfc65
+    qemu_put_be32(f, len);
c8dfc65
+    qemu_put_buffer(f, data, len);
c8dfc65
+
c8dfc65
+    free(data);
c8dfc65
+}
c8dfc65
+
c8dfc65
+static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused)
c8dfc65
+{
c8dfc65
+    USBRedirDevice *dev = priv;
c8dfc65
+    uint8_t *data;
c8dfc65
+    int len, ret;
c8dfc65
+
c8dfc65
+    len = qemu_get_be32(f);
c8dfc65
+    if (len == 0) {
c8dfc65
+        return 0;
c8dfc65
+    }
c8dfc65
+
c8dfc65
+    /*
c8dfc65
+     * Our chardev should be open already at this point, otherwise
c8dfc65
+     * the usbredir channel will be broken (ie spice without seamless)
c8dfc65
+     */
c8dfc65
+    if (dev->parser == NULL) {
c8dfc65
+        ERROR("get_parser called with closed chardev, failing migration\n");
c8dfc65
+        return -1;
c8dfc65
+    }
c8dfc65
+
c8dfc65
+    data = g_malloc(len);
c8dfc65
+    qemu_get_buffer(f, data, len);
c8dfc65
+
c8dfc65
+    ret = usbredirparser_unserialize(dev->parser, data, len);
c8dfc65
+
c8dfc65
+    g_free(data);
c8dfc65
+
c8dfc65
+    return ret;
c8dfc65
+}
c8dfc65
+
c8dfc65
+static const VMStateInfo usbredir_parser_vmstate_info = {
c8dfc65
+    .name = "usb-redir-parser",
c8dfc65
+    .put  = usbredir_put_parser,
c8dfc65
+    .get  = usbredir_get_parser,
c8dfc65
+};
c8dfc65
+
c8dfc65
+
c8dfc65
+/* For buffered packets (iso/irq) queue migration */
c8dfc65
+static void usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused)
c8dfc65
+{
c8dfc65
+    struct endp_data *endp = priv;
c8dfc65
+    struct buf_packet *bufp;
c8dfc65
+    int remain = endp->bufpq_size;
c8dfc65
+
c8dfc65
+    qemu_put_be32(f, endp->bufpq_size);
c8dfc65
+    QTAILQ_FOREACH(bufp, &endp->bufpq, next) {
c8dfc65
+        qemu_put_be32(f, bufp->len);
c8dfc65
+        qemu_put_be32(f, bufp->status);
c8dfc65
+        qemu_put_buffer(f, bufp->data, bufp->len);
c8dfc65
+        remain--;
c8dfc65
+    }
c8dfc65
+    assert(remain == 0);
c8dfc65
+}
c8dfc65
+
c8dfc65
+static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused)
c8dfc65
+{
c8dfc65
+    struct endp_data *endp = priv;
c8dfc65
+    struct buf_packet *bufp;
c8dfc65
+    int i;
c8dfc65
+
c8dfc65
+    endp->bufpq_size = qemu_get_be32(f);
c8dfc65
+    for (i = 0; i < endp->bufpq_size; i++) {
c8dfc65
+        bufp = g_malloc(sizeof(struct buf_packet));
c8dfc65
+        bufp->len = qemu_get_be32(f);
c8dfc65
+        bufp->status = qemu_get_be32(f);
c8dfc65
+        bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */
c8dfc65
+        qemu_get_buffer(f, bufp->data, bufp->len);
c8dfc65
+        QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next);
c8dfc65
+    }
c8dfc65
+    return 0;
c8dfc65
+}
c8dfc65
+
c8dfc65
+static const VMStateInfo usbredir_ep_bufpq_vmstate_info = {
c8dfc65
+    .name = "usb-redir-bufpq",
c8dfc65
+    .put  = usbredir_put_bufpq,
c8dfc65
+    .get  = usbredir_get_bufpq,
c8dfc65
+};
c8dfc65
+
c8dfc65
+
c8dfc65
+/* For endp_data migration */
c8dfc65
+static const VMStateDescription usbredir_ep_vmstate = {
c8dfc65
+    .name = "usb-redir-ep",
c8dfc65
+    .version_id = 1,
c8dfc65
+    .minimum_version_id = 1,
c8dfc65
+    .fields = (VMStateField []) {
c8dfc65
+        VMSTATE_UINT8(type, struct endp_data),
c8dfc65
+        VMSTATE_UINT8(interval, struct endp_data),
c8dfc65
+        VMSTATE_UINT8(interface, struct endp_data),
c8dfc65
+        VMSTATE_UINT16(max_packet_size, struct endp_data),
c8dfc65
+        VMSTATE_UINT8(iso_started, struct endp_data),
c8dfc65
+        VMSTATE_UINT8(iso_error, struct endp_data),
c8dfc65
+        VMSTATE_UINT8(interrupt_started, struct endp_data),
c8dfc65
+        VMSTATE_UINT8(interrupt_error, struct endp_data),
c8dfc65
+        VMSTATE_UINT8(bufpq_prefilled, struct endp_data),
c8dfc65
+        VMSTATE_UINT8(bufpq_dropping_packets, struct endp_data),
c8dfc65
+        {
c8dfc65
+            .name         = "bufpq",
c8dfc65
+            .version_id   = 0,
c8dfc65
+            .field_exists = NULL,
c8dfc65
+            .size         = 0,
c8dfc65
+            .info         = &usbredir_ep_bufpq_vmstate_info,
c8dfc65
+            .flags        = VMS_SINGLE,
c8dfc65
+            .offset       = 0,
c8dfc65
+        },
c8dfc65
+        VMSTATE_INT32(bufpq_target_size, struct endp_data),
c8dfc65
+        VMSTATE_END_OF_LIST()
c8dfc65
+    }
c8dfc65
+};
c8dfc65
+
c8dfc65
+
c8dfc65
+/* For PacketIdQueue migration */
c8dfc65
+static void usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused)
c8dfc65
+{
c8dfc65
+    struct PacketIdQueue *q = priv;
c8dfc65
+    USBRedirDevice *dev = q->dev;
c8dfc65
+    struct PacketIdQueueEntry *e;
c8dfc65
+    int remain = q->size;
c8dfc65
+
c8dfc65
+    DPRINTF("put_packet_id_q %s size %d\n", q->name, q->size);
c8dfc65
+    qemu_put_be32(f, q->size);
c8dfc65
+    QTAILQ_FOREACH(e, &q->head, next) {
c8dfc65
+        qemu_put_be64(f, e->id);
c8dfc65
+        remain--;
c8dfc65
+    }
c8dfc65
+    assert(remain == 0);
c8dfc65
+}
c8dfc65
+
c8dfc65
+static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused)
c8dfc65
+{
c8dfc65
+    struct PacketIdQueue *q = priv;
c8dfc65
+    USBRedirDevice *dev = q->dev;
c8dfc65
+    int i, size;
c8dfc65
+    uint64_t id;
c8dfc65
+
c8dfc65
+    size = qemu_get_be32(f);
c8dfc65
+    DPRINTF("get_packet_id_q %s size %d\n", q->name, size);
c8dfc65
+    for (i = 0; i < size; i++) {
c8dfc65
+        id = qemu_get_be64(f);
c8dfc65
+        packet_id_queue_add(q, id);
c8dfc65
+    }
c8dfc65
+    assert(q->size == size);
c8dfc65
+    return 0;
c8dfc65
+}
c8dfc65
+
c8dfc65
+static const VMStateInfo usbredir_ep_packet_id_q_vmstate_info = {
c8dfc65
+    .name = "usb-redir-packet-id-q",
c8dfc65
+    .put  = usbredir_put_packet_id_q,
c8dfc65
+    .get  = usbredir_get_packet_id_q,
c8dfc65
+};
c8dfc65
+
c8dfc65
+static const VMStateDescription usbredir_ep_packet_id_queue_vmstate = {
c8dfc65
+    .name = "usb-redir-packet-id-queue",
c8dfc65
+    .version_id = 1,
c8dfc65
+    .minimum_version_id = 1,
c8dfc65
+    .fields = (VMStateField []) {
c8dfc65
+        {
c8dfc65
+            .name         = "queue",
c8dfc65
+            .version_id   = 0,
c8dfc65
+            .field_exists = NULL,
c8dfc65
+            .size         = 0,
c8dfc65
+            .info         = &usbredir_ep_packet_id_q_vmstate_info,
c8dfc65
+            .flags        = VMS_SINGLE,
c8dfc65
+            .offset       = 0,
c8dfc65
+        },
c8dfc65
+        VMSTATE_END_OF_LIST()
c8dfc65
+    }
c8dfc65
+};
c8dfc65
+
c8dfc65
+
c8dfc65
+/* For usb_redir_device_connect_header migration */
c8dfc65
+static const VMStateDescription usbredir_device_info_vmstate = {
c8dfc65
+    .name = "usb-redir-device-info",
c8dfc65
+    .version_id = 1,
c8dfc65
+    .minimum_version_id = 1,
c8dfc65
+    .fields = (VMStateField []) {
c8dfc65
+        VMSTATE_UINT8(speed, struct usb_redir_device_connect_header),
c8dfc65
+        VMSTATE_UINT8(device_class, struct usb_redir_device_connect_header),
c8dfc65
+        VMSTATE_UINT8(device_subclass, struct usb_redir_device_connect_header),
c8dfc65
+        VMSTATE_UINT8(device_protocol, struct usb_redir_device_connect_header),
c8dfc65
+        VMSTATE_UINT16(vendor_id, struct usb_redir_device_connect_header),
c8dfc65
+        VMSTATE_UINT16(product_id, struct usb_redir_device_connect_header),
c8dfc65
+        VMSTATE_UINT16(device_version_bcd,
c8dfc65
+                       struct usb_redir_device_connect_header),
c8dfc65
+        VMSTATE_END_OF_LIST()
c8dfc65
+    }
c8dfc65
+};
c8dfc65
+
c8dfc65
+
c8dfc65
+/* For usb_redir_interface_info_header migration */
c8dfc65
+static const VMStateDescription usbredir_interface_info_vmstate = {
c8dfc65
+    .name = "usb-redir-interface-info",
c8dfc65
+    .version_id = 1,
c8dfc65
+    .minimum_version_id = 1,
c8dfc65
+    .fields = (VMStateField []) {
c8dfc65
+        VMSTATE_UINT32(interface_count,
c8dfc65
+                       struct usb_redir_interface_info_header),
c8dfc65
+        VMSTATE_UINT8_ARRAY(interface,
c8dfc65
+                            struct usb_redir_interface_info_header, 32),
c8dfc65
+        VMSTATE_UINT8_ARRAY(interface_class,
c8dfc65
+                            struct usb_redir_interface_info_header, 32),
c8dfc65
+        VMSTATE_UINT8_ARRAY(interface_subclass,
c8dfc65
+                            struct usb_redir_interface_info_header, 32),
c8dfc65
+        VMSTATE_UINT8_ARRAY(interface_protocol,
c8dfc65
+                            struct usb_redir_interface_info_header, 32),
c8dfc65
+        VMSTATE_END_OF_LIST()
c8dfc65
+    }
c8dfc65
+};
c8dfc65
+
c8dfc65
+
c8dfc65
+/* And finally the USBRedirDevice vmstate itself */
c8dfc65
+static const VMStateDescription usbredir_vmstate = {
c8dfc65
+    .name = "usb-redir",
c8dfc65
+    .version_id = 1,
c8dfc65
+    .minimum_version_id = 1,
c8dfc65
+    .pre_save = usbredir_pre_save,
c8dfc65
+    .post_load = usbredir_post_load,
c8dfc65
+    .fields = (VMStateField []) {
c8dfc65
+        VMSTATE_USB_DEVICE(dev, USBRedirDevice),
c8dfc65
+        VMSTATE_TIMER(attach_timer, USBRedirDevice),
c8dfc65
+        {
c8dfc65
+            .name         = "parser",
c8dfc65
+            .version_id   = 0,
c8dfc65
+            .field_exists = NULL,
c8dfc65
+            .size         = 0,
c8dfc65
+            .info         = &usbredir_parser_vmstate_info,
c8dfc65
+            .flags        = VMS_SINGLE,
c8dfc65
+            .offset       = 0,
c8dfc65
+        },
c8dfc65
+        VMSTATE_STRUCT_ARRAY(endpoint, USBRedirDevice, MAX_ENDPOINTS, 1,
c8dfc65
+                             usbredir_ep_vmstate, struct endp_data),
c8dfc65
+        VMSTATE_STRUCT(cancelled, USBRedirDevice, 1,
c8dfc65
+                       usbredir_ep_packet_id_queue_vmstate,
c8dfc65
+                       struct PacketIdQueue),
c8dfc65
+        VMSTATE_STRUCT(already_in_flight, USBRedirDevice, 1,
c8dfc65
+                       usbredir_ep_packet_id_queue_vmstate,
c8dfc65
+                       struct PacketIdQueue),
c8dfc65
+        VMSTATE_STRUCT(device_info, USBRedirDevice, 1,
c8dfc65
+                       usbredir_device_info_vmstate,
c8dfc65
+                       struct usb_redir_device_connect_header),
c8dfc65
+        VMSTATE_STRUCT(interface_info, USBRedirDevice, 1,
c8dfc65
+                       usbredir_interface_info_vmstate,
c8dfc65
+                       struct usb_redir_interface_info_header),
c8dfc65
+        VMSTATE_END_OF_LIST()
c8dfc65
+    }
c8dfc65
+};
c8dfc65
+
c8dfc65
 static Property usbredir_properties[] = {
c8dfc65
     DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
c8dfc65
     DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
5544c1b
@@ -1545,6 +1887,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data)
c8dfc65
     uc->handle_reset   = usbredir_handle_reset;
c8dfc65
     uc->handle_data    = usbredir_handle_data;
c8dfc65
     uc->handle_control = usbredir_handle_control;
c8dfc65
+    dc->vmsd           = &usbredir_vmstate;
c8dfc65
     dc->props          = usbredir_properties;
c8dfc65
 }
c8dfc65