diff options
author | Peter Maydell | 2017-01-10 11:46:21 +0100 |
---|---|---|
committer | Peter Maydell | 2017-01-10 11:46:21 +0100 |
commit | 41a0e54756a9ae6b60be34bb33302a7e085fdb07 (patch) | |
tree | 7eef18d5eefcc5a515cd0100ad19724b2601fbc7 /hw/virtio | |
parent | Merge remote-tracking branch 'remotes/rth/tags/pull-tcg-20170108' into staging (diff) | |
parent | acpi-test: update expected files (diff) | |
download | qemu-41a0e54756a9ae6b60be34bb33302a7e085fdb07.tar.gz qemu-41a0e54756a9ae6b60be34bb33302a7e085fdb07.tar.xz qemu-41a0e54756a9ae6b60be34bb33302a7e085fdb07.zip |
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
virtio, vhost, pc: fixes, features
beginnings of iotlb support for vhost
acpi hotplug rework
vhost net tx flush on link down
passing mtu to guests
hotplug for virtio crypto
fixes and cleanups all over the place
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
# gpg: Signature made Tue 10 Jan 2017 05:37:48 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
* remotes/mst/tags/for_upstream: (41 commits)
acpi-test: update expected files
memhp: move DIMM devices into dedicated scope with related common methods
memhp: don't generate memory hotplug AML if it's not enabled/supported
memhp: move memory hotplug only defines to memory_hotplug.c
memhp: move GPE handler_E03 into build_memory_hotplug_aml()
memhp: merge build_memory_devices() into build_memory_hotplug_aml()
memhp: consolidate scattered MHPD device declaration
memhp: move build_memory_devices() into memory_hotplug.c
memhp: move build_memory_hotplug_aml() into memory_hotplug.c
tests: pc: add memory hotplug acpi tables tests
virtio-net: Add MTU feature support
vhost-net: Notify the backend about the host MTU
vhost-user: Add MTU protocol feature and op
net: virtio-net discards TX data after link down
virtio: Introduce virtqueue_drop_all procedure
net: vhost stop updates virtio queue state
net: Add virtio queue interface to update used index from vring state
balloon: Don't balloon roms
virtio: fix vq->inuse recalc after migr
pcie_aer: support configurable AER capa version
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/virtio')
-rw-r--r-- | hw/virtio/trace-events | 2 | ||||
-rw-r--r-- | hw/virtio/vhost-user.c | 34 | ||||
-rw-r--r-- | hw/virtio/vhost.c | 1 | ||||
-rw-r--r-- | hw/virtio/virtio-balloon.c | 7 | ||||
-rw-r--r-- | hw/virtio/virtio-bus.c | 8 | ||||
-rw-r--r-- | hw/virtio/virtio-crypto-pci.c | 6 | ||||
-rw-r--r-- | hw/virtio/virtio-crypto.c | 41 | ||||
-rw-r--r-- | hw/virtio/virtio-pci.c | 21 | ||||
-rw-r--r-- | hw/virtio/virtio-pci.h | 4 | ||||
-rw-r--r-- | hw/virtio/virtio.c | 108 |
10 files changed, 200 insertions, 32 deletions
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 7b6f55e70e..6926eedd3f 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -15,6 +15,8 @@ virtio_rng_pushed(void *rng, size_t len) "rng %p: %zd bytes pushed" virtio_rng_request(void *rng, size_t size, unsigned quota) "rng %p: %zd bytes requested, %u bytes quota left" # hw/virtio/virtio-balloon.c +# +virtio_balloon_bad_addr(uint64_t gpa) "%"PRIx64 virtio_balloon_handle_output(const char *name, uint64_t gpa) "section name: %s gpa: %"PRIx64 virtio_balloon_get_config(uint32_t num_pages, uint32_t actual) "num_pages: %d actual: %d" virtio_balloon_set_config(uint32_t actual, uint32_t oldactual) "actual: %d oldactual: %d" diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 7ee92b32c5..9334a8ae22 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -32,6 +32,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, VHOST_USER_PROTOCOL_F_RARP = 2, VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, + VHOST_USER_PROTOCOL_F_NET_MTU = 4, VHOST_USER_PROTOCOL_F_MAX }; @@ -59,6 +60,7 @@ typedef enum VhostUserRequest { VHOST_USER_GET_QUEUE_NUM = 17, VHOST_USER_SET_VRING_ENABLE = 18, VHOST_USER_SEND_RARP = 19, + VHOST_USER_NET_SET_MTU = 20, VHOST_USER_MAX } VhostUserRequest; @@ -186,6 +188,7 @@ static bool vhost_user_one_time_request(VhostUserRequest request) case VHOST_USER_RESET_OWNER: case VHOST_USER_SET_MEM_TABLE: case VHOST_USER_GET_QUEUE_NUM: + case VHOST_USER_NET_SET_MTU: return true; default: return false; @@ -685,6 +688,36 @@ static bool vhost_user_can_merge(struct vhost_dev *dev, return mfd == rfd; } +static int vhost_user_net_set_mtu(struct vhost_dev *dev, uint16_t mtu) +{ + VhostUserMsg msg; + bool reply_supported = virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_REPLY_ACK); + + if (!(dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_NET_MTU))) { + return 0; + } + + msg.request = VHOST_USER_NET_SET_MTU; + msg.payload.u64 = mtu; + msg.size = sizeof(msg.payload.u64); + msg.flags = VHOST_USER_VERSION; + if (reply_supported) { + msg.flags |= VHOST_USER_NEED_REPLY_MASK; + } + + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } + + /* If reply_ack supported, slave has to ack specified MTU is valid */ + if (reply_supported) { + return process_message_reply(dev, msg.request); + } + + return 0; +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, .vhost_backend_init = vhost_user_init, @@ -708,4 +741,5 @@ const VhostOps user_ops = { .vhost_requires_shm_log = vhost_user_requires_shm_log, .vhost_migration_done = vhost_user_migration_done, .vhost_backend_can_merge = vhost_user_can_merge, + .vhost_net_set_mtu = vhost_user_net_set_mtu, }; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index f7f70237db..d396b22531 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -993,6 +993,7 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, virtio_queue_set_last_avail_idx(vdev, idx, state.num); } virtio_queue_invalidate_signalled_used(vdev, idx); + virtio_queue_update_used_idx(vdev, idx); /* In the cross-endian case, we need to reset the vring endianness to * native as legacy devices expect so by default. diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 884570a57d..a705e0ec55 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -228,8 +228,13 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) /* FIXME: remove get_system_memory(), but how? */ section = memory_region_find(get_system_memory(), pa, 1); - if (!int128_nz(section.size) || !memory_region_is_ram(section.mr)) + if (!int128_nz(section.size) || + !memory_region_is_ram(section.mr) || + memory_region_is_rom(section.mr) || + memory_region_is_romd(section.mr)) { + trace_virtio_balloon_bad_addr(pa); continue; + } trace_virtio_balloon_handle_output(memory_region_name(section.mr), pa); diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index d6c0c72bd2..d31cc00e83 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -28,6 +28,7 @@ #include "hw/qdev.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio.h" +#include "exec/address-spaces.h" /* #define DEBUG_VIRTIO_BUS */ @@ -61,6 +62,13 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) if (klass->device_plugged != NULL) { klass->device_plugged(qbus->parent, errp); } + + if (klass->get_dma_as != NULL && + virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { + vdev->dma_as = klass->get_dma_as(qbus->parent); + } else { + vdev->dma_as = &address_space_memory; + } } /* Reset the virtio_bus */ diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index a1b09064c0..422aca3a98 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -31,6 +31,11 @@ static void virtio_crypto_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) VirtIOCryptoPCI *vcrypto = VIRTIO_CRYPTO_PCI(vpci_dev); DeviceState *vdev = DEVICE(&vcrypto->vdev); + if (vcrypto->vdev.conf.cryptodev == NULL) { + error_setg(errp, "'cryptodev' parameter expects a valid object"); + return; + } + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); virtio_pci_force_virtio_1(vpci_dev); object_property_set_bool(OBJECT(vdev), true, "realized", errp); @@ -48,7 +53,6 @@ static void virtio_crypto_pci_class_init(ObjectClass *klass, void *data) k->realize = virtio_crypto_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->props = virtio_crypto_pci_properties; - dc->hotpluggable = false; pcidev_k->class_id = PCI_CLASS_OTHERS; } diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 2f2467e859..296472fc6e 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -337,7 +337,18 @@ static void virtio_crypto_free_request(VirtIOCryptoReq *req) { if (req) { if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { - g_free(req->u.sym_op_info); + size_t max_len; + CryptoDevBackendSymOpInfo *op_info = req->u.sym_op_info; + + max_len = op_info->iv_len + + op_info->aad_len + + op_info->src_len + + op_info->dst_len + + op_info->digest_result_len; + + /* Zeroize and free request data structure */ + memset(op_info, 0, sizeof(*op_info) + max_len); + g_free(op_info); } g_free(req); } @@ -355,7 +366,7 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev, return; } - len = sym_op_info->dst_len; + len = sym_op_info->src_len; /* Save the cipher result */ s = iov_from_buf(req->in_iov, req->in_num, 0, sym_op_info->dst, len); if (s != len) { @@ -416,7 +427,7 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev, uint32_t hash_start_src_offset = 0, len_to_hash = 0; uint32_t cipher_start_src_offset = 0, len_to_cipher = 0; - size_t max_len, curr_size = 0; + uint64_t max_len, curr_size = 0; size_t s; /* Plain cipher */ @@ -441,7 +452,7 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev, return NULL; } - max_len = iv_len + aad_len + src_len + dst_len + hash_result_len; + max_len = (uint64_t)iv_len + aad_len + src_len + dst_len + hash_result_len; if (unlikely(max_len > vcrypto->conf.max_size)) { virtio_error(vdev, "virtio-crypto too big length"); return NULL; @@ -732,7 +743,7 @@ static void virtio_crypto_reset(VirtIODevice *vdev) VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); /* multiqueue is disabled by default */ vcrypto->curr_queues = 1; - if (!vcrypto->cryptodev->ready) { + if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) { vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY; } else { vcrypto->status |= VIRTIO_CRYPTO_S_HW_READY; @@ -792,13 +803,14 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp) } vcrypto->ctrl_vq = virtio_add_queue(vdev, 64, virtio_crypto_handle_ctrl); - if (!vcrypto->cryptodev->ready) { + if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) { vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY; } else { vcrypto->status |= VIRTIO_CRYPTO_S_HW_READY; } virtio_crypto_init_config(vdev); + cryptodev_backend_set_used(vcrypto->cryptodev, true); } static void virtio_crypto_device_unrealize(DeviceState *dev, Error **errp) @@ -818,6 +830,7 @@ static void virtio_crypto_device_unrealize(DeviceState *dev, Error **errp) g_free(vcrypto->vqs); virtio_cleanup(vdev); + cryptodev_backend_set_used(vcrypto->cryptodev, false); } static const VMStateDescription vmstate_virtio_crypto = { @@ -875,6 +888,20 @@ static void virtio_crypto_class_init(ObjectClass *klass, void *data) vdc->reset = virtio_crypto_reset; } +static void +virtio_crypto_check_cryptodev_is_used(Object *obj, const char *name, + Object *val, Error **errp) +{ + if (cryptodev_backend_is_used(CRYPTODEV_BACKEND(val))) { + char *path = object_get_canonical_path_component(val); + error_setg(errp, + "can't use already used cryptodev backend: %s", path); + g_free(path); + } else { + qdev_prop_allow_set_link_before_realize(obj, name, val, errp); + } +} + static void virtio_crypto_instance_init(Object *obj) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(obj); @@ -888,7 +915,7 @@ static void virtio_crypto_instance_init(Object *obj) object_property_add_link(obj, "cryptodev", TYPE_CRYPTODEV_BACKEND, (Object **)&vcrypto->conf.cryptodev, - qdev_prop_allow_set_link_before_realize, + virtio_crypto_check_cryptodev_is_used, OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 21c2b9dbfc..854b8f22bf 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1144,6 +1144,14 @@ static int virtio_pci_query_nvectors(DeviceState *d) return proxy->nvectors; } +static AddressSpace *virtio_pci_get_dma_as(DeviceState *d) +{ + VirtIOPCIProxy *proxy = VIRTIO_PCI(d); + PCIDevice *dev = &proxy->pci_dev; + + return pci_get_address_space(dev); +} + static int virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy, struct virtio_pci_cap *cap) { @@ -1601,6 +1609,11 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) } if (legacy) { + if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { + error_setg(errp, "VIRTIO_F_IOMMU_PLATFORM was supported by" + "neither legacy nor transitional device."); + return ; + } /* legacy and transitional */ pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID, pci_get_word(config + PCI_VENDOR_ID)); @@ -1802,6 +1815,11 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) * PCI Power Management Interface Specification. */ pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3); + + if (proxy->flags & VIRTIO_PCI_FLAG_ATS) { + pcie_ats_init(pci_dev, 256); + } + } else { /* * make future invocations of pci_is_express() return false @@ -1855,6 +1873,8 @@ static Property virtio_pci_properties[] = { VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT, false), DEFINE_PROP_BOOL("x-ignore-backend-features", VirtIOPCIProxy, ignore_backend_features, false), + DEFINE_PROP_BIT("ats", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_ATS_BIT, false), DEFINE_PROP_END_OF_LIST(), }; @@ -2520,6 +2540,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) k->query_nvectors = virtio_pci_query_nvectors; k->ioeventfd_enabled = virtio_pci_ioeventfd_enabled; k->ioeventfd_assign = virtio_pci_ioeventfd_assign; + k->get_dma_as = virtio_pci_get_dma_as; } static const TypeInfo virtio_pci_bus_info = { diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index 5e078866c4..d00064cc0c 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -72,6 +72,7 @@ enum { VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT, + VIRTIO_PCI_FLAG_ATS_BIT, }; /* Need to activate work-arounds for buggy guests at vmstate load. */ @@ -96,6 +97,9 @@ enum { #define VIRTIO_PCI_FLAG_PAGE_PER_VQ \ (1 << VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT) +/* address space translation service */ +#define VIRTIO_PCI_FLAG_ATS (1 << VIRTIO_PCI_FLAG_ATS_BIT) + typedef struct { MSIMessage msg; int virq; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index d40711a31d..aa4f38f50a 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -23,6 +23,7 @@ #include "hw/virtio/virtio-bus.h" #include "migration/migration.h" #include "hw/virtio/virtio-access.h" +#include "sysemu/dma.h" /* * The alignment to use between consumer and producer parts of vring. @@ -92,7 +93,7 @@ struct VirtQueue uint16_t queue_index; - int inuse; + unsigned int inuse; uint16_t vector; VirtIOHandleOutput handle_output; @@ -121,7 +122,7 @@ void virtio_queue_update_rings(VirtIODevice *vdev, int n) static void vring_desc_read(VirtIODevice *vdev, VRingDesc *desc, hwaddr desc_pa, int i) { - address_space_read(&address_space_memory, desc_pa + i * sizeof(VRingDesc), + address_space_read(vdev->dma_as, desc_pa + i * sizeof(VRingDesc), MEMTXATTRS_UNSPECIFIED, (void *)desc, sizeof(VRingDesc)); virtio_tswap64s(vdev, &desc->addr); virtio_tswap32s(vdev, &desc->len); @@ -163,7 +164,7 @@ static inline void vring_used_write(VirtQueue *vq, VRingUsedElem *uelem, virtio_tswap32s(vq->vdev, &uelem->id); virtio_tswap32s(vq->vdev, &uelem->len); pa = vq->vring.used + offsetof(VRingUsed, ring[i]); - address_space_write(&address_space_memory, pa, MEMTXATTRS_UNSPECIFIED, + address_space_write(vq->vdev->dma_as, pa, MEMTXATTRS_UNSPECIFIED, (void *)uelem, sizeof(VRingUsedElem)); } @@ -249,6 +250,7 @@ int virtio_queue_empty(VirtQueue *vq) static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len) { + AddressSpace *dma_as = vq->vdev->dma_as; unsigned int offset; int i; @@ -256,17 +258,18 @@ static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, for (i = 0; i < elem->in_num; i++) { size_t size = MIN(len - offset, elem->in_sg[i].iov_len); - cpu_physical_memory_unmap(elem->in_sg[i].iov_base, - elem->in_sg[i].iov_len, - 1, size); + dma_memory_unmap(dma_as, elem->in_sg[i].iov_base, + elem->in_sg[i].iov_len, + DMA_DIRECTION_FROM_DEVICE, size); offset += size; } for (i = 0; i < elem->out_num; i++) - cpu_physical_memory_unmap(elem->out_sg[i].iov_base, - elem->out_sg[i].iov_len, - 0, elem->out_sg[i].iov_len); + dma_memory_unmap(dma_as, elem->out_sg[i].iov_base, + elem->out_sg[i].iov_len, + DMA_DIRECTION_TO_DEVICE, + elem->out_sg[i].iov_len); } /* virtqueue_detach_element: @@ -560,7 +563,10 @@ static bool virtqueue_map_desc(VirtIODevice *vdev, unsigned int *p_num_sg, goto out; } - iov[num_sg].iov_base = cpu_physical_memory_map(pa, &len, is_write); + iov[num_sg].iov_base = dma_memory_map(vdev->dma_as, pa, &len, + is_write ? + DMA_DIRECTION_FROM_DEVICE : + DMA_DIRECTION_TO_DEVICE); if (!iov[num_sg].iov_base) { virtio_error(vdev, "virtio: bogus descriptor or out of resources"); goto out; @@ -597,9 +603,9 @@ static void virtqueue_undo_map_desc(unsigned int out_num, unsigned int in_num, } } -static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr, - unsigned int *num_sg, unsigned int max_size, - int is_write) +static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg, + hwaddr *addr, unsigned int *num_sg, + unsigned int max_size, int is_write) { unsigned int i; hwaddr len; @@ -618,7 +624,10 @@ static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr, for (i = 0; i < *num_sg; i++) { len = sg[i].iov_len; - sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write); + sg[i].iov_base = dma_memory_map(vdev->dma_as, + addr[i], &len, is_write ? + DMA_DIRECTION_FROM_DEVICE : + DMA_DIRECTION_TO_DEVICE); if (!sg[i].iov_base) { error_report("virtio: error trying to map MMIO memory"); exit(1); @@ -630,12 +639,15 @@ static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr, } } -void virtqueue_map(VirtQueueElement *elem) +void virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem) { - virtqueue_map_iovec(elem->in_sg, elem->in_addr, &elem->in_num, - VIRTQUEUE_MAX_SIZE, 1); - virtqueue_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num, - VIRTQUEUE_MAX_SIZE, 0); + virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, &elem->in_num, + MIN(ARRAY_SIZE(elem->in_sg), ARRAY_SIZE(elem->in_addr)), + 1); + virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, &elem->out_num, + MIN(ARRAY_SIZE(elem->out_sg), + ARRAY_SIZE(elem->out_addr)), + 0); } static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num) @@ -771,6 +783,44 @@ err_undo_map: return NULL; } +/* virtqueue_drop_all: + * @vq: The #VirtQueue + * Drops all queued buffers and indicates them to the guest + * as if they are done. Useful when buffers can not be + * processed but must be returned to the guest. + */ +unsigned int virtqueue_drop_all(VirtQueue *vq) +{ + unsigned int dropped = 0; + VirtQueueElement elem = {}; + VirtIODevice *vdev = vq->vdev; + bool fEventIdx = virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX); + + if (unlikely(vdev->broken)) { + return 0; + } + + while (!virtio_queue_empty(vq) && vq->inuse < vq->vring.num) { + /* works similar to virtqueue_pop but does not map buffers + * and does not allocate any memory */ + smp_rmb(); + if (!virtqueue_get_head(vq, vq->last_avail_idx, &elem.index)) { + break; + } + vq->inuse++; + vq->last_avail_idx++; + if (fEventIdx) { + vring_set_avail_event(vq, vq->last_avail_idx); + } + /* immediately push the element, nothing to unmap + * as both in_num and out_num are set to 0 */ + virtqueue_push(vq, &elem, 0); + dropped++; + } + + return dropped; +} + /* Reading and writing a structure directly to QEMUFile is *awful*, but * it is what QEMU has always done by mistake. We can change it sooner * or later by bumping the version number of the affected vm states. @@ -788,7 +838,7 @@ typedef struct VirtQueueElementOld { struct iovec out_sg[VIRTQUEUE_MAX_SIZE]; } VirtQueueElementOld; -void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz) +void *qemu_get_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, size_t sz) { VirtQueueElement *elem; VirtQueueElementOld data; @@ -819,7 +869,7 @@ void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz) elem->out_sg[i].iov_len = data.out_sg[i].iov_len; } - virtqueue_map(elem); + virtqueue_map(vdev, elem); return elem; } @@ -878,6 +928,11 @@ static int virtio_validate_features(VirtIODevice *vdev) { VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM) && + !virtio_vdev_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { + return -EFAULT; + } + if (k->validate_features) { return k->validate_features(vdev); } else { @@ -1861,9 +1916,11 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) /* * Some devices migrate VirtQueueElements that have been popped * from the avail ring but not yet returned to the used ring. + * Since max ring size < UINT16_MAX it's safe to use modulo + * UINT16_MAX + 1 subtraction. */ - vdev->vq[i].inuse = vdev->vq[i].last_avail_idx - - vdev->vq[i].used_idx; + vdev->vq[i].inuse = (uint16_t)(vdev->vq[i].last_avail_idx - + vdev->vq[i].used_idx); if (vdev->vq[i].inuse > vdev->vq[i].vring.num) { error_report("VQ %d size 0x%x < last_avail_idx 0x%x - " "used_idx 0x%x", @@ -2001,6 +2058,11 @@ void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx) vdev->vq[n].shadow_avail_idx = idx; } +void virtio_queue_update_used_idx(VirtIODevice *vdev, int n) +{ + vdev->vq[n].used_idx = vring_used_idx(&vdev->vq[n]); +} + void virtio_queue_invalidate_signalled_used(VirtIODevice *vdev, int n) { vdev->vq[n].signalled_used_valid = false; |