On Wed, Dec 29, 2010 at 04:52:46PM +0200, Michael S. Tsirkin wrote: > This is on top of the ioeventfd patches, and > fixes two potential issues when ioeventfd is mixed with vhost-net: > - ioeventfd could start running then get stopped for vhost-net. > For example, vm state change handlers run in no particular order. > This would be hard to debug. Prevent this by always running state > callback before ioeventfd start and after ioeventfd stop. > - Separate the flags for user-requested ioeventfd and for ioeventfd > being overridden by vhost backend. > This will make it possible to enable/disable vhost depending on > guest behaviour. > > I'll probably split this patch in two, and merge into the > appropriate patches in the ioeventfd series. > > Compile-tested only so far, would appreciate feedback/test reports. > > Signed-off-by: Michael S. Tsirkin <m...@redhat.com>
Stefan - just to clarify, I'm waiting for you ack to merge this + the ioeventfd patches. > diff --git a/hw/vhost.c b/hw/vhost.c > index 6082da2..1d09ed0 100644 > --- a/hw/vhost.c > +++ b/hw/vhost.c > @@ -630,6 +630,17 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) > int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) > { > int i, r; > + /* A binding must support vmstate notifiers (for migration to work) */ > + if (!vdev->binding->vmstate_change) { > + fprintf(stderr, "binding does not support vmstate notifiers\n"); > + r = -ENOSYS; > + goto fail; > + } > + if (!vdev->binding->set_guest_notifiers) { > + fprintf(stderr, "binding does not support guest notifiers\n"); > + r = -ENOSYS; > + goto fail; > + } > if (!vdev->binding->set_guest_notifiers) { > fprintf(stderr, "binding does not support guest notifiers\n"); > r = -ENOSYS; > diff --git a/hw/virtio-net.c b/hw/virtio-net.c > index ec1bf8d..ccb3e63 100644 > --- a/hw/virtio-net.c > +++ b/hw/virtio-net.c > @@ -54,8 +54,6 @@ typedef struct VirtIONet > uint8_t nouni; > uint8_t nobcast; > uint8_t vhost_started; > - bool vm_running; > - VMChangeStateEntry *vmstate; > struct { > int in_use; > int first_multi; > @@ -102,7 +100,7 @@ static void virtio_net_set_config(VirtIODevice *vdev, > const uint8_t *config) > static bool virtio_net_started(VirtIONet *n, uint8_t status) > { > return (status & VIRTIO_CONFIG_S_DRIVER_OK) && > - (n->status & VIRTIO_NET_S_LINK_UP) && n->vm_running; > + (n->status & VIRTIO_NET_S_LINK_UP) && n->vdev.vm_running; > } > > static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) > @@ -453,7 +451,7 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, > VirtQueue *vq) > static int virtio_net_can_receive(VLANClientState *nc) > { > VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > - if (!n->vm_running) { > + if (!n->vdev.vm_running) { > return 0; > } > > @@ -708,7 +706,7 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, > VirtQueue *vq) > return num_packets; > } > > - assert(n->vm_running); > + assert(n->vdev.vm_running); > > if (n->async_tx.elem.out_num) { > virtio_queue_set_notification(n->tx_vq, 0); > @@ -769,7 +767,7 @@ static void virtio_net_handle_tx_timer(VirtIODevice > *vdev, VirtQueue *vq) > VirtIONet *n = to_virtio_net(vdev); > > /* This happens when device was stopped but VCPU wasn't. */ > - if (!n->vm_running) { > + if (!n->vdev.vm_running) { > n->tx_waiting = 1; > return; > } > @@ -796,7 +794,7 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, > VirtQueue *vq) > } > n->tx_waiting = 1; > /* This happens when device was stopped but VCPU wasn't. */ > - if (!n->vm_running) { > + if (!n->vdev.vm_running) { > return; > } > virtio_queue_set_notification(vq, 0); > @@ -806,7 +804,7 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, > VirtQueue *vq) > static void virtio_net_tx_timer(void *opaque) > { > VirtIONet *n = opaque; > - assert(n->vm_running); > + assert(n->vdev.vm_running); > > n->tx_waiting = 0; > > @@ -823,7 +821,7 @@ static void virtio_net_tx_bh(void *opaque) > VirtIONet *n = opaque; > int32_t ret; > > - assert(n->vm_running); > + assert(n->vdev.vm_running); > > n->tx_waiting = 0; > > @@ -988,16 +986,6 @@ static NetClientInfo net_virtio_info = { > .link_status_changed = virtio_net_set_link_status, > }; > > -static void virtio_net_vmstate_change(void *opaque, int running, int reason) > -{ > - VirtIONet *n = opaque; > - n->vm_running = running; > - /* This is called when vm is started/stopped, > - * it will start/stop vhost backend if appropriate > - * e.g. after migration. */ > - virtio_net_set_status(&n->vdev, n->vdev.status); > -} > - > VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > virtio_net_conf *net) > { > @@ -1052,7 +1040,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf > *conf, > n->qdev = dev; > register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, > virtio_net_save, virtio_net_load, n); > - n->vmstate = qemu_add_vm_change_state_handler(virtio_net_vmstate_change, > n); > > add_boot_device_path(conf->bootindex, dev, "/ethernet-...@0"); > > @@ -1062,7 +1049,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf > *conf, > void virtio_net_exit(VirtIODevice *vdev) > { > VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); > - qemu_del_vm_change_state_handler(n->vmstate); > > /* This will stop vhost backend if appropriate. */ > virtio_net_set_status(vdev, 0); > diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c > index b2181fc..75b5e53 100644 > --- a/hw/virtio-pci.c > +++ b/hw/virtio-pci.c > @@ -112,8 +112,8 @@ typedef struct { > /* Max. number of ports we can have for a the virtio-serial device */ > uint32_t max_virtserial_ports; > virtio_net_conf net; > + bool ioeventfd_disabled; > bool ioeventfd_started; > - VMChangeStateEntry *vm_change_state_entry; > } VirtIOPCIProxy; > > /* virtio device */ > @@ -251,6 +251,7 @@ static int virtio_pci_start_ioeventfd(VirtIOPCIProxy > *proxy) > int n, r; > > if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) || > + proxy->ioeventfd_disabled || > proxy->ioeventfd_started) { > return 0; > } > @@ -280,7 +281,7 @@ assign_error: > virtio_pci_set_host_notifier_internal(proxy, n, false); > } > proxy->ioeventfd_started = false; > - proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; > + proxy->ioeventfd_disabled = true; > return r; > } > > @@ -350,13 +351,16 @@ static void virtio_ioport_write(void *opaque, uint32_t > addr, uint32_t val) > virtio_queue_notify(vdev, val); > break; > case VIRTIO_PCI_STATUS: > - if (val & VIRTIO_CONFIG_S_DRIVER_OK) { > - virtio_pci_start_ioeventfd(proxy); > - } else { > + if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) { > virtio_pci_stop_ioeventfd(proxy); > } > > virtio_set_status(vdev, val & 0xFF); > + > + if (val & VIRTIO_CONFIG_S_DRIVER_OK) { > + virtio_pci_start_ioeventfd(proxy); > + } > + > if (vdev->status == 0) { > virtio_reset(proxy->vdev); > msix_unuse_all_vectors(&proxy->pci_dev); > @@ -618,22 +622,24 @@ static int virtio_pci_set_host_notifier(void *opaque, > int n, bool assign) > /* Stop using ioeventfd for virtqueue kick if the device starts using > host > * notifiers. This makes it easy to avoid stepping on each others' toes. > */ > - if (proxy->ioeventfd_started) { > + proxy->ioeventfd_disabled = assign; > + if (assign) { > virtio_pci_stop_ioeventfd(proxy); > - proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; > } > > return virtio_pci_set_host_notifier_internal(proxy, n, assign); > } > > -static void virtio_pci_vm_change_state_handler(void *opaque, int running, > int reason) > +static void virtio_pci_vmstate_change(void *opaque, bool running) > { > VirtIOPCIProxy *proxy = opaque; > > if (running && (proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { > + virtio_set_status(proxy->vdev, proxy->vdev->status); > virtio_pci_start_ioeventfd(proxy); > } else { > virtio_pci_stop_ioeventfd(proxy); > + virtio_set_status(proxy->vdev, proxy->vdev->status); > } > } > > @@ -646,6 +652,7 @@ static const VirtIOBindings virtio_pci_bindings = { > .get_features = virtio_pci_get_features, > .set_host_notifier = virtio_pci_set_host_notifier, > .set_guest_notifiers = virtio_pci_set_guest_notifiers, > + .vmstate_change = virtio_pci_vmstate_change, > }; > > static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, > @@ -699,9 +706,6 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, > VirtIODevice *vdev, > proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE; > proxy->host_features = vdev->get_features(vdev, proxy->host_features); > > - proxy->vm_change_state_entry = qemu_add_vm_change_state_handler( > - virtio_pci_vm_change_state_handler, > - proxy); > } > > static int virtio_blk_init_pci(PCIDevice *pci_dev) > @@ -729,9 +733,6 @@ static int virtio_blk_init_pci(PCIDevice *pci_dev) > > static int virtio_exit_pci(PCIDevice *pci_dev) > { > - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); > - > - qemu_del_vm_change_state_handler(proxy->vm_change_state_entry); > return msix_uninit(pci_dev); > } > > diff --git a/hw/virtio.c b/hw/virtio.c > index e40296a..13c3373 100644 > --- a/hw/virtio.c > +++ b/hw/virtio.c > @@ -751,11 +751,21 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) > > void virtio_cleanup(VirtIODevice *vdev) > { > + qemu_del_vm_change_state_handler(vdev->vmstate); > if (vdev->config) > qemu_free(vdev->config); > qemu_free(vdev->vq); > } > > +static void virtio_vmstate_change(void *opaque, int running, int reason) > +{ > + VirtIODevice *vdev = opaque; > + vdev->vm_running = running; > + if (vdev->binding->vmstate_change) { > + vdev->binding->vmstate_change(vdev->binding_opaque, running); > + } > +} > + > VirtIODevice *virtio_common_init(const char *name, uint16_t device_id, > size_t config_size, size_t struct_size) > { > @@ -782,6 +792,8 @@ VirtIODevice *virtio_common_init(const char *name, > uint16_t device_id, > else > vdev->config = NULL; > > + vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, > vdev); > + > return vdev; > } > > diff --git a/hw/virtio.h b/hw/virtio.h > index 5ae521c..d8546d5 100644 > --- a/hw/virtio.h > +++ b/hw/virtio.h > @@ -95,6 +95,7 @@ typedef struct { > unsigned (*get_features)(void * opaque); > int (*set_guest_notifiers)(void * opaque, bool assigned); > int (*set_host_notifier)(void * opaque, int n, bool assigned); > + void (*vmstate_change)(void * opaque, bool running); > } VirtIOBindings; > > #define VIRTIO_PCI_QUEUE_MAX 64 > @@ -123,6 +124,8 @@ struct VirtIODevice > const VirtIOBindings *binding; > void *binding_opaque; > uint16_t device_id; > + bool vm_running; > + VMChangeStateEntry *vmstate; > }; > > static inline void virtio_set_status(VirtIODevice *vdev, uint8_t val)