virtio, vhost, pc: fixes
Most notably this fixes a regression with vhost introduced by the pull before last. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJYLyObAAoJECgfDbjSjVRptPoIAK/4SdEAqS9pnXPekPZpIddV KHCFjj4Q68s22i0jpA1hxSXk1yQZIl56dnynU0DIAbCD1NYQIEmWx7uOJjppre9O L64V2s2ItEagFBGFwQDoJnUDIyEhth8KRqsa36V2YWJXYOaH1Rx1QNb9tX9R0aeb 2lVwYE+yig1Gc/2PAYJrcKWwM3iwWrYW6ssycP2LEOGOhBCIrGZwDJkqv7ayDVL9 j4tH2eBRrOAzm8c3fybC3OZkeLqcQJnbVONmD8kV0Q0IphcFvloJQCvcefb/3Ox1 HAz57JxZfpxMZPVtvgU8Q+xzElz8noCXg+6lF/dx71CKicwXxg4lsMF1LyKHUoU= =cuAn -----END PGP SIGNATURE----- Merge remote-tracking branch 'mst/tags/for_upstream' into staging virtio, vhost, pc: fixes Most notably this fixes a regression with vhost introduced by the pull before last. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # gpg: Signature made Fri 18 Nov 2016 03:51:55 PM GMT # gpg: using RSA key 0x281F0DB8D28D5469 # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" # Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67 # Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469 * mst/tags/for_upstream: acpi: Use apic_id_limit when calculating legacy ACPI table size ipmi: fix qemu crash while migrating with ipmi ivshmem: Fix 64 bit memory bar configuration virtio: set ISR on dataplane notifications virtio: access ISR atomically virtio: introduce grab/release_ioeventfd to fix vhost virtio-crypto: fix virtio_queue_set_notification() race Message-id: 1479484366-7977-1-git-send-email-mst@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
		
						commit
						c36ed06e91
					
				@ -68,9 +68,7 @@ static void notify_guest_bh(void *opaque)
 | 
				
			|||||||
            unsigned i = j + ctzl(bits);
 | 
					            unsigned i = j + ctzl(bits);
 | 
				
			||||||
            VirtQueue *vq = virtio_get_queue(s->vdev, i);
 | 
					            VirtQueue *vq = virtio_get_queue(s->vdev, i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (virtio_should_notify(s->vdev, vq)) {
 | 
					            virtio_notify_irqfd(s->vdev, vq);
 | 
				
			||||||
                event_notifier_set(virtio_queue_get_guest_notifier(vq));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            bits &= bits - 1; /* clear right-most bit */
 | 
					            bits &= bits - 1; /* clear right-most bit */
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -2860,7 +2860,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
 | 
				
			|||||||
         */
 | 
					         */
 | 
				
			||||||
        int legacy_aml_len =
 | 
					        int legacy_aml_len =
 | 
				
			||||||
            pcmc->legacy_acpi_table_size +
 | 
					            pcmc->legacy_acpi_table_size +
 | 
				
			||||||
            ACPI_BUILD_LEGACY_CPU_AML_SIZE * max_cpus;
 | 
					            ACPI_BUILD_LEGACY_CPU_AML_SIZE * pcms->apic_id_limit;
 | 
				
			||||||
        int legacy_table_size =
 | 
					        int legacy_table_size =
 | 
				
			||||||
            ROUND_UP(tables_blob->len - aml_len + legacy_aml_len,
 | 
					            ROUND_UP(tables_blob->len - aml_len + legacy_aml_len,
 | 
				
			||||||
                     ACPI_BUILD_ALIGN_SIZE);
 | 
					                     ACPI_BUILD_ALIGN_SIZE);
 | 
				
			||||||
 | 
				
			|||||||
@ -433,10 +433,8 @@ const VMStateDescription vmstate_ISAIPMIKCSDevice = {
 | 
				
			|||||||
        VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice),
 | 
					        VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice),
 | 
				
			||||||
        VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice),
 | 
					        VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice),
 | 
				
			||||||
        VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice),
 | 
					        VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice),
 | 
				
			||||||
        VMSTATE_VBUFFER_UINT32(kcs.outmsg, ISAIPMIKCSDevice, 1, NULL, 0,
 | 
					        VMSTATE_UINT8_ARRAY(kcs.outmsg, ISAIPMIKCSDevice, MAX_IPMI_MSG_SIZE),
 | 
				
			||||||
                               kcs.outlen),
 | 
					        VMSTATE_UINT8_ARRAY(kcs.inmsg, ISAIPMIKCSDevice, MAX_IPMI_MSG_SIZE),
 | 
				
			||||||
        VMSTATE_VBUFFER_UINT32(kcs.inmsg, ISAIPMIKCSDevice, 1, NULL, 0,
 | 
					 | 
				
			||||||
                               kcs.inlen),
 | 
					 | 
				
			||||||
        VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice),
 | 
					        VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice),
 | 
				
			||||||
        VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice),
 | 
					        VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice),
 | 
				
			||||||
        VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice),
 | 
					        VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice),
 | 
				
			||||||
 | 
				
			|||||||
@ -95,13 +95,6 @@ static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n,
 | 
				
			|||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void virtio_scsi_dataplane_notify(VirtIODevice *vdev, VirtIOSCSIReq *req)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    if (virtio_should_notify(vdev, req->vq)) {
 | 
					 | 
				
			||||||
        event_notifier_set(virtio_queue_get_guest_notifier(req->vq));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* assumes s->ctx held */
 | 
					/* assumes s->ctx held */
 | 
				
			||||||
static void virtio_scsi_clear_aio(VirtIOSCSI *s)
 | 
					static void virtio_scsi_clear_aio(VirtIOSCSI *s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
				
			|||||||
@ -69,7 +69,7 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
 | 
				
			|||||||
    qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size);
 | 
					    qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size);
 | 
				
			||||||
    virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size);
 | 
					    virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size);
 | 
				
			||||||
    if (s->dataplane_started && !s->dataplane_fenced) {
 | 
					    if (s->dataplane_started && !s->dataplane_fenced) {
 | 
				
			||||||
        virtio_scsi_dataplane_notify(vdev, req);
 | 
					        virtio_notify_irqfd(vdev, vq);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        virtio_notify(vdev, vq);
 | 
					        virtio_notify(vdev, vq);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "
 | 
				
			|||||||
virtqueue_flush(void *vq, unsigned int count) "vq %p count %u"
 | 
					virtqueue_flush(void *vq, unsigned int count) "vq %p count %u"
 | 
				
			||||||
virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u"
 | 
					virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u"
 | 
				
			||||||
virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p"
 | 
					virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p"
 | 
				
			||||||
virtio_irq(void *vq) "vq %p"
 | 
					virtio_notify_irqfd(void *vdev, void *vq) "vdev %p vq %p"
 | 
				
			||||||
virtio_notify(void *vdev, void *vq) "vdev %p vq %p"
 | 
					virtio_notify(void *vdev, void *vq) "vdev %p vq %p"
 | 
				
			||||||
virtio_set_status(void *vdev, uint8_t val) "vdev %p val %u"
 | 
					virtio_set_status(void *vdev, uint8_t val) "vdev %p val %u"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1214,17 +1214,17 @@ void vhost_dev_cleanup(struct vhost_dev *hdev)
 | 
				
			|||||||
int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
 | 
					int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
 | 
					    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
 | 
				
			||||||
    VirtioBusState *vbus = VIRTIO_BUS(qbus);
 | 
					 | 
				
			||||||
    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
 | 
					 | 
				
			||||||
    int i, r, e;
 | 
					    int i, r, e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!k->ioeventfd_assign) {
 | 
					    /* We will pass the notifiers to the kernel, make sure that QEMU
 | 
				
			||||||
 | 
					     * doesn't interfere.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    r = virtio_device_grab_ioeventfd(vdev);
 | 
				
			||||||
 | 
					    if (r < 0) {
 | 
				
			||||||
        error_report("binding does not support host notifiers");
 | 
					        error_report("binding does not support host notifiers");
 | 
				
			||||||
        r = -ENOSYS;
 | 
					 | 
				
			||||||
        goto fail;
 | 
					        goto fail;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    virtio_device_stop_ioeventfd(vdev);
 | 
					 | 
				
			||||||
    for (i = 0; i < hdev->nvqs; ++i) {
 | 
					    for (i = 0; i < hdev->nvqs; ++i) {
 | 
				
			||||||
        r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i,
 | 
					        r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i,
 | 
				
			||||||
                                         true);
 | 
					                                         true);
 | 
				
			||||||
@ -1244,7 +1244,7 @@ fail_vq:
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        assert (e >= 0);
 | 
					        assert (e >= 0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    virtio_device_start_ioeventfd(vdev);
 | 
					    virtio_device_release_ioeventfd(vdev);
 | 
				
			||||||
fail:
 | 
					fail:
 | 
				
			||||||
    return r;
 | 
					    return r;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1267,7 +1267,7 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        assert (r >= 0);
 | 
					        assert (r >= 0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    virtio_device_start_ioeventfd(vdev);
 | 
					    virtio_device_release_ioeventfd(vdev);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Test and clear event pending status.
 | 
					/* Test and clear event pending status.
 | 
				
			||||||
 | 
				
			|||||||
@ -147,6 +147,39 @@ void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* On success, ioeventfd ownership belongs to the caller.  */
 | 
				
			||||||
 | 
					int virtio_bus_grab_ioeventfd(VirtioBusState *bus)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* vhost can be used even if ioeventfd=off in the proxy device,
 | 
				
			||||||
 | 
					     * so do not check k->ioeventfd_enabled.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    if (!k->ioeventfd_assign) {
 | 
				
			||||||
 | 
					        return -ENOSYS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (bus->ioeventfd_grabbed == 0 && bus->ioeventfd_started) {
 | 
				
			||||||
 | 
					        virtio_bus_stop_ioeventfd(bus);
 | 
				
			||||||
 | 
					        /* Remember that we need to restart ioeventfd
 | 
				
			||||||
 | 
					         * when ioeventfd_grabbed becomes zero.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        bus->ioeventfd_started = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    bus->ioeventfd_grabbed++;
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void virtio_bus_release_ioeventfd(VirtioBusState *bus)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(bus->ioeventfd_grabbed != 0);
 | 
				
			||||||
 | 
					    if (--bus->ioeventfd_grabbed == 0 && bus->ioeventfd_started) {
 | 
				
			||||||
 | 
					        /* Force virtio_bus_start_ioeventfd to act.  */
 | 
				
			||||||
 | 
					        bus->ioeventfd_started = false;
 | 
				
			||||||
 | 
					        virtio_bus_start_ioeventfd(bus);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int virtio_bus_start_ioeventfd(VirtioBusState *bus)
 | 
					int virtio_bus_start_ioeventfd(VirtioBusState *bus)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
 | 
					    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
 | 
				
			||||||
@ -161,10 +194,14 @@ int virtio_bus_start_ioeventfd(VirtioBusState *bus)
 | 
				
			|||||||
    if (bus->ioeventfd_started) {
 | 
					    if (bus->ioeventfd_started) {
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    r = vdc->start_ioeventfd(vdev);
 | 
					
 | 
				
			||||||
    if (r < 0) {
 | 
					    /* Only set our notifier if we have ownership.  */
 | 
				
			||||||
        error_report("%s: failed. Fallback to userspace (slower).", __func__);
 | 
					    if (!bus->ioeventfd_grabbed) {
 | 
				
			||||||
        return r;
 | 
					        r = vdc->start_ioeventfd(vdev);
 | 
				
			||||||
 | 
					        if (r < 0) {
 | 
				
			||||||
 | 
					            error_report("%s: failed. Fallback to userspace (slower).", __func__);
 | 
				
			||||||
 | 
					            return r;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    bus->ioeventfd_started = true;
 | 
					    bus->ioeventfd_started = true;
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
@ -179,9 +216,12 @@ void virtio_bus_stop_ioeventfd(VirtioBusState *bus)
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    vdev = virtio_bus_get_device(bus);
 | 
					    /* Only remove our notifier if we have ownership.  */
 | 
				
			||||||
    vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
 | 
					    if (!bus->ioeventfd_grabbed) {
 | 
				
			||||||
    vdc->stop_ioeventfd(vdev);
 | 
					        vdev = virtio_bus_get_device(bus);
 | 
				
			||||||
 | 
					        vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
 | 
				
			||||||
 | 
					        vdc->stop_ioeventfd(vdev);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    bus->ioeventfd_started = false;
 | 
					    bus->ioeventfd_started = false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -211,7 +251,6 @@ int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (assign) {
 | 
					    if (assign) {
 | 
				
			||||||
        assert(!bus->ioeventfd_started);
 | 
					 | 
				
			||||||
        r = event_notifier_init(notifier, 1);
 | 
					        r = event_notifier_init(notifier, 1);
 | 
				
			||||||
        if (r < 0) {
 | 
					        if (r < 0) {
 | 
				
			||||||
            error_report("%s: unable to init event notifier: %s (%d)",
 | 
					            error_report("%s: unable to init event notifier: %s (%d)",
 | 
				
			||||||
@ -225,9 +264,6 @@ int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign)
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        if (!bus->ioeventfd_started) {
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        k->ioeventfd_assign(proxy, notifier, n, false);
 | 
					        k->ioeventfd_assign(proxy, notifier, n, false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -692,8 +692,17 @@ static void virtio_crypto_dataq_bh(void *opaque)
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    virtio_crypto_handle_dataq(vdev, q->dataq);
 | 
					    for (;;) {
 | 
				
			||||||
    virtio_queue_set_notification(q->dataq, 1);
 | 
					        virtio_crypto_handle_dataq(vdev, q->dataq);
 | 
				
			||||||
 | 
					        virtio_queue_set_notification(q->dataq, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Are we done or did the guest add more buffers? */
 | 
				
			||||||
 | 
					        if (virtio_queue_empty(q->dataq)) {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        virtio_queue_set_notification(q->dataq, 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
 | 
				
			|||||||
@ -191,7 +191,7 @@ static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
 | 
				
			|||||||
        return virtio_queue_get_addr(vdev, vdev->queue_sel)
 | 
					        return virtio_queue_get_addr(vdev, vdev->queue_sel)
 | 
				
			||||||
            >> proxy->guest_page_shift;
 | 
					            >> proxy->guest_page_shift;
 | 
				
			||||||
    case VIRTIO_MMIO_INTERRUPTSTATUS:
 | 
					    case VIRTIO_MMIO_INTERRUPTSTATUS:
 | 
				
			||||||
        return vdev->isr;
 | 
					        return atomic_read(&vdev->isr);
 | 
				
			||||||
    case VIRTIO_MMIO_STATUS:
 | 
					    case VIRTIO_MMIO_STATUS:
 | 
				
			||||||
        return vdev->status;
 | 
					        return vdev->status;
 | 
				
			||||||
    case VIRTIO_MMIO_HOSTFEATURESSEL:
 | 
					    case VIRTIO_MMIO_HOSTFEATURESSEL:
 | 
				
			||||||
@ -299,7 +299,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    case VIRTIO_MMIO_INTERRUPTACK:
 | 
					    case VIRTIO_MMIO_INTERRUPTACK:
 | 
				
			||||||
        vdev->isr &= ~value;
 | 
					        atomic_and(&vdev->isr, ~value);
 | 
				
			||||||
        virtio_update_irq(vdev);
 | 
					        virtio_update_irq(vdev);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    case VIRTIO_MMIO_STATUS:
 | 
					    case VIRTIO_MMIO_STATUS:
 | 
				
			||||||
@ -347,7 +347,7 @@ static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector)
 | 
				
			|||||||
    if (!vdev) {
 | 
					    if (!vdev) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    level = (vdev->isr != 0);
 | 
					    level = (atomic_read(&vdev->isr) != 0);
 | 
				
			||||||
    DPRINTF("virtio_mmio setting IRQ %d\n", level);
 | 
					    DPRINTF("virtio_mmio setting IRQ %d\n", level);
 | 
				
			||||||
    qemu_set_irq(proxy->irq, level);
 | 
					    qemu_set_irq(proxy->irq, level);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -73,7 +73,7 @@ static void virtio_pci_notify(DeviceState *d, uint16_t vector)
 | 
				
			|||||||
        msix_notify(&proxy->pci_dev, vector);
 | 
					        msix_notify(&proxy->pci_dev, vector);
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
        VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
 | 
					        VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
 | 
				
			||||||
        pci_set_irq(&proxy->pci_dev, vdev->isr & 1);
 | 
					        pci_set_irq(&proxy->pci_dev, atomic_read(&vdev->isr) & 1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -449,8 +449,7 @@ static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
 | 
				
			|||||||
        break;
 | 
					        break;
 | 
				
			||||||
    case VIRTIO_PCI_ISR:
 | 
					    case VIRTIO_PCI_ISR:
 | 
				
			||||||
        /* reading from the ISR also clears it. */
 | 
					        /* reading from the ISR also clears it. */
 | 
				
			||||||
        ret = vdev->isr;
 | 
					        ret = atomic_xchg(&vdev->isr, 0);
 | 
				
			||||||
        vdev->isr = 0;
 | 
					 | 
				
			||||||
        pci_irq_deassert(&proxy->pci_dev);
 | 
					        pci_irq_deassert(&proxy->pci_dev);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    case VIRTIO_MSI_CONFIG_VECTOR:
 | 
					    case VIRTIO_MSI_CONFIG_VECTOR:
 | 
				
			||||||
@ -1379,9 +1378,7 @@ static uint64_t virtio_pci_isr_read(void *opaque, hwaddr addr,
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    VirtIOPCIProxy *proxy = opaque;
 | 
					    VirtIOPCIProxy *proxy = opaque;
 | 
				
			||||||
    VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
 | 
					    VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
 | 
				
			||||||
    uint64_t val = vdev->isr;
 | 
					    uint64_t val = atomic_xchg(&vdev->isr, 0);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    vdev->isr = 0;
 | 
					 | 
				
			||||||
    pci_irq_deassert(&proxy->pci_dev);
 | 
					    pci_irq_deassert(&proxy->pci_dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return val;
 | 
					    return val;
 | 
				
			||||||
 | 
				
			|||||||
@ -945,7 +945,7 @@ void virtio_reset(void *opaque)
 | 
				
			|||||||
    vdev->guest_features = 0;
 | 
					    vdev->guest_features = 0;
 | 
				
			||||||
    vdev->queue_sel = 0;
 | 
					    vdev->queue_sel = 0;
 | 
				
			||||||
    vdev->status = 0;
 | 
					    vdev->status = 0;
 | 
				
			||||||
    vdev->isr = 0;
 | 
					    atomic_set(&vdev->isr, 0);
 | 
				
			||||||
    vdev->config_vector = VIRTIO_NO_VECTOR;
 | 
					    vdev->config_vector = VIRTIO_NO_VECTOR;
 | 
				
			||||||
    virtio_notify_vector(vdev, vdev->config_vector);
 | 
					    virtio_notify_vector(vdev, vdev->config_vector);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1318,11 +1318,16 @@ void virtio_del_queue(VirtIODevice *vdev, int n)
 | 
				
			|||||||
    vdev->vq[n].vring.num_default = 0;
 | 
					    vdev->vq[n].vring.num_default = 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void virtio_irq(VirtQueue *vq)
 | 
					static void virtio_set_isr(VirtIODevice *vdev, int value)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    trace_virtio_irq(vq);
 | 
					    uint8_t old = atomic_read(&vdev->isr);
 | 
				
			||||||
    vq->vdev->isr |= 0x01;
 | 
					
 | 
				
			||||||
    virtio_notify_vector(vq->vdev, vq->vector);
 | 
					    /* Do not write ISR if it does not change, so that its cacheline remains
 | 
				
			||||||
 | 
					     * shared in the common case where the guest does not read it.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    if ((old & value) != value) {
 | 
				
			||||||
 | 
					        atomic_or(&vdev->isr, value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq)
 | 
					bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq)
 | 
				
			||||||
@ -1348,6 +1353,33 @@ bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq)
 | 
				
			|||||||
    return !v || vring_need_event(vring_get_used_event(vq), new, old);
 | 
					    return !v || vring_need_event(vring_get_used_event(vq), new, old);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!virtio_should_notify(vdev, vq)) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    trace_virtio_notify_irqfd(vdev, vq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * virtio spec 1.0 says ISR bit 0 should be ignored with MSI, but
 | 
				
			||||||
 | 
					     * windows drivers included in virtio-win 1.8.0 (circa 2015) are
 | 
				
			||||||
 | 
					     * incorrectly polling this bit during crashdump and hibernation
 | 
				
			||||||
 | 
					     * in MSI mode, causing a hang if this bit is never updated.
 | 
				
			||||||
 | 
					     * Recent releases of Windows do not really shut down, but rather
 | 
				
			||||||
 | 
					     * log out and hibernate to make the next startup faster.  Hence,
 | 
				
			||||||
 | 
					     * this manifested as a more serious hang during shutdown with
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Next driver release from 2016 fixed this problem, so working around it
 | 
				
			||||||
 | 
					     * is not a must, but it's easy to do so let's do it here.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note: it's safe to update ISR from any thread as it was switched
 | 
				
			||||||
 | 
					     * to an atomic operation.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    virtio_set_isr(vq->vdev, 0x1);
 | 
				
			||||||
 | 
					    event_notifier_set(&vq->guest_notifier);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
 | 
					void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (!virtio_should_notify(vdev, vq)) {
 | 
					    if (!virtio_should_notify(vdev, vq)) {
 | 
				
			||||||
@ -1355,7 +1387,7 @@ void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    trace_virtio_notify(vdev, vq);
 | 
					    trace_virtio_notify(vdev, vq);
 | 
				
			||||||
    vdev->isr |= 0x01;
 | 
					    virtio_set_isr(vq->vdev, 0x1);
 | 
				
			||||||
    virtio_notify_vector(vdev, vq->vector);
 | 
					    virtio_notify_vector(vdev, vq->vector);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1364,7 +1396,7 @@ void virtio_notify_config(VirtIODevice *vdev)
 | 
				
			|||||||
    if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))
 | 
					    if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    vdev->isr |= 0x03;
 | 
					    virtio_set_isr(vdev, 0x3);
 | 
				
			||||||
    vdev->generation++;
 | 
					    vdev->generation++;
 | 
				
			||||||
    virtio_notify_vector(vdev, vdev->config_vector);
 | 
					    virtio_notify_vector(vdev, vdev->config_vector);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1895,7 +1927,7 @@ void virtio_init(VirtIODevice *vdev, const char *name,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    vdev->device_id = device_id;
 | 
					    vdev->device_id = device_id;
 | 
				
			||||||
    vdev->status = 0;
 | 
					    vdev->status = 0;
 | 
				
			||||||
    vdev->isr = 0;
 | 
					    atomic_set(&vdev->isr, 0);
 | 
				
			||||||
    vdev->queue_sel = 0;
 | 
					    vdev->queue_sel = 0;
 | 
				
			||||||
    vdev->config_vector = VIRTIO_NO_VECTOR;
 | 
					    vdev->config_vector = VIRTIO_NO_VECTOR;
 | 
				
			||||||
    vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_QUEUE_MAX);
 | 
					    vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_QUEUE_MAX);
 | 
				
			||||||
@ -1982,7 +2014,7 @@ static void virtio_queue_guest_notifier_read(EventNotifier *n)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    VirtQueue *vq = container_of(n, VirtQueue, guest_notifier);
 | 
					    VirtQueue *vq = container_of(n, VirtQueue, guest_notifier);
 | 
				
			||||||
    if (event_notifier_test_and_clear(n)) {
 | 
					    if (event_notifier_test_and_clear(n)) {
 | 
				
			||||||
        virtio_irq(vq);
 | 
					        virtio_notify_vector(vq->vdev, vq->vector);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2191,6 +2223,22 @@ void virtio_device_stop_ioeventfd(VirtIODevice *vdev)
 | 
				
			|||||||
    virtio_bus_stop_ioeventfd(vbus);
 | 
					    virtio_bus_stop_ioeventfd(vbus);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int virtio_device_grab_ioeventfd(VirtIODevice *vdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
 | 
				
			||||||
 | 
					    VirtioBusState *vbus = VIRTIO_BUS(qbus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return virtio_bus_grab_ioeventfd(vbus);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void virtio_device_release_ioeventfd(VirtIODevice *vdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
 | 
				
			||||||
 | 
					    VirtioBusState *vbus = VIRTIO_BUS(qbus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtio_bus_release_ioeventfd(vbus);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void virtio_device_class_init(ObjectClass *klass, void *data)
 | 
					static void virtio_device_class_init(ObjectClass *klass, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /* Set the default value here. */
 | 
					    /* Set the default value here. */
 | 
				
			||||||
 | 
				
			|||||||
@ -97,6 +97,16 @@ struct VirtioBusState {
 | 
				
			|||||||
     * Set if ioeventfd has been started.
 | 
					     * Set if ioeventfd has been started.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    bool ioeventfd_started;
 | 
					    bool ioeventfd_started;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * Set if ioeventfd has been grabbed by vhost.  When ioeventfd
 | 
				
			||||||
 | 
					     * is grabbed by vhost, we track its started/stopped state (which
 | 
				
			||||||
 | 
					     * depends in turn on the virtio status register), but do not
 | 
				
			||||||
 | 
					     * register a handler for the ioeventfd.  When ioeventfd is
 | 
				
			||||||
 | 
					     * released, if ioeventfd_started is true we finally register
 | 
				
			||||||
 | 
					     * the handler so that QEMU's device model can use ioeventfd.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int ioeventfd_grabbed;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp);
 | 
					void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp);
 | 
				
			||||||
@ -131,6 +141,10 @@ bool virtio_bus_ioeventfd_enabled(VirtioBusState *bus);
 | 
				
			|||||||
int virtio_bus_start_ioeventfd(VirtioBusState *bus);
 | 
					int virtio_bus_start_ioeventfd(VirtioBusState *bus);
 | 
				
			||||||
/* Stop the ioeventfd. */
 | 
					/* Stop the ioeventfd. */
 | 
				
			||||||
void virtio_bus_stop_ioeventfd(VirtioBusState *bus);
 | 
					void virtio_bus_stop_ioeventfd(VirtioBusState *bus);
 | 
				
			||||||
 | 
					/* Tell the bus that vhost is grabbing the ioeventfd. */
 | 
				
			||||||
 | 
					int virtio_bus_grab_ioeventfd(VirtioBusState *bus);
 | 
				
			||||||
 | 
					/* bus that vhost is not using the ioeventfd anymore. */
 | 
				
			||||||
 | 
					void virtio_bus_release_ioeventfd(VirtioBusState *bus);
 | 
				
			||||||
/* Switch from/to the generic ioeventfd handler */
 | 
					/* Switch from/to the generic ioeventfd handler */
 | 
				
			||||||
int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign);
 | 
					int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -137,6 +137,5 @@ void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
 | 
				
			|||||||
void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp);
 | 
					void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp);
 | 
				
			||||||
int virtio_scsi_dataplane_start(VirtIODevice *s);
 | 
					int virtio_scsi_dataplane_start(VirtIODevice *s);
 | 
				
			||||||
void virtio_scsi_dataplane_stop(VirtIODevice *s);
 | 
					void virtio_scsi_dataplane_stop(VirtIODevice *s);
 | 
				
			||||||
void virtio_scsi_dataplane_notify(VirtIODevice *vdev, VirtIOSCSIReq *req);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* QEMU_VIRTIO_SCSI_H */
 | 
					#endif /* QEMU_VIRTIO_SCSI_H */
 | 
				
			||||||
 | 
				
			|||||||
@ -181,6 +181,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
 | 
				
			|||||||
                               unsigned max_in_bytes, unsigned max_out_bytes);
 | 
					                               unsigned max_in_bytes, unsigned max_out_bytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq);
 | 
					bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq);
 | 
				
			||||||
 | 
					void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq);
 | 
				
			||||||
void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);
 | 
					void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void virtio_save(VirtIODevice *vdev, QEMUFile *f);
 | 
					void virtio_save(VirtIODevice *vdev, QEMUFile *f);
 | 
				
			||||||
@ -272,13 +273,14 @@ void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign,
 | 
				
			|||||||
                                                bool with_irqfd);
 | 
					                                                bool with_irqfd);
 | 
				
			||||||
int virtio_device_start_ioeventfd(VirtIODevice *vdev);
 | 
					int virtio_device_start_ioeventfd(VirtIODevice *vdev);
 | 
				
			||||||
void virtio_device_stop_ioeventfd(VirtIODevice *vdev);
 | 
					void virtio_device_stop_ioeventfd(VirtIODevice *vdev);
 | 
				
			||||||
 | 
					int virtio_device_grab_ioeventfd(VirtIODevice *vdev);
 | 
				
			||||||
 | 
					void virtio_device_release_ioeventfd(VirtIODevice *vdev);
 | 
				
			||||||
bool virtio_device_ioeventfd_enabled(VirtIODevice *vdev);
 | 
					bool virtio_device_ioeventfd_enabled(VirtIODevice *vdev);
 | 
				
			||||||
EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
 | 
					EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
 | 
				
			||||||
void virtio_queue_host_notifier_read(EventNotifier *n);
 | 
					void virtio_queue_host_notifier_read(EventNotifier *n);
 | 
				
			||||||
void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx,
 | 
					void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx,
 | 
				
			||||||
                                                void (*fn)(VirtIODevice *,
 | 
					                                                void (*fn)(VirtIODevice *,
 | 
				
			||||||
                                                           VirtQueue *));
 | 
					                                                           VirtQueue *));
 | 
				
			||||||
void virtio_irq(VirtQueue *vq);
 | 
					 | 
				
			||||||
VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector);
 | 
					VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector);
 | 
				
			||||||
VirtQueue *virtio_vector_next_queue(VirtQueue *vq);
 | 
					VirtQueue *virtio_vector_next_queue(VirtQueue *vq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user