From: Laurent Vivier <lviv...@redhat.com> This new command shows the information of a VirtQueue element.
Signed-off-by: Jonah Palmer <jonah.pal...@oracle.com> --- hw/virtio/virtio-stub.c | 9 +++ hw/virtio/virtio.c | 154 +++++++++++++++++++++++++++++++++++++++++ qapi/virtio.json | 177 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 340 insertions(+) diff --git a/hw/virtio/virtio-stub.c b/hw/virtio/virtio-stub.c index 3484b1f..810317c 100644 --- a/hw/virtio/virtio-stub.c +++ b/hw/virtio/virtio-stub.c @@ -31,3 +31,12 @@ VirtQueueStatus *qmp_x_query_virtio_queue_status(const char *path, { return qmp_virtio_unsupported(errp); } + +VirtioQueueElement *qmp_x_query_virtio_queue_element(const char *path, + uint16_t queue, + bool has_index, + uint16_t index, + Error **errp) +{ + return qmp_virtio_unsupported(errp); +} diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 160cc90..6df6a85 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -480,6 +480,19 @@ static inline void vring_used_write(VirtQueue *vq, VRingUsedElem *uelem, address_space_cache_invalidate(&caches->used, pa, sizeof(VRingUsedElem)); } +/* Called within rcu_read_lock(). */ +static inline uint16_t vring_used_flags(VirtQueue *vq) +{ + VRingMemoryRegionCaches *caches = vring_get_region_caches(vq); + hwaddr pa = offsetof(VRingUsed, flags); + + if (!caches) { + return 0; + } + + return virtio_lduw_phys_cached(vq->vdev, &caches->used, pa); +} + /* Called within rcu_read_lock(). */ static uint16_t vring_used_idx(VirtQueue *vq) { @@ -4392,6 +4405,147 @@ VirtQueueStatus *qmp_x_query_virtio_queue_status(const char *path, return status; } +static VirtioRingDescFlagsList *qmp_decode_vring_desc_flags(uint16_t flags) +{ + VirtioRingDescFlagsList *list = NULL; + VirtioRingDescFlagsList *node; + int i; + + struct { + uint16_t flag; + VirtioRingDescFlags value; + } map[] = { + { VRING_DESC_F_NEXT, VIRTIO_RING_DESC_FLAGS_NEXT }, + { VRING_DESC_F_WRITE, VIRTIO_RING_DESC_FLAGS_WRITE }, + { VRING_DESC_F_INDIRECT, VIRTIO_RING_DESC_FLAGS_INDIRECT }, + { 1 << VRING_PACKED_DESC_F_AVAIL, VIRTIO_RING_DESC_FLAGS_AVAIL }, + { 1 << VRING_PACKED_DESC_F_USED, VIRTIO_RING_DESC_FLAGS_USED }, + { 0, -1 } + }; + + for (i = 0; map[i].flag; i++) { + if ((map[i].flag & flags) == 0) { + continue; + } + node = g_malloc0(sizeof(VirtioRingDescFlagsList)); + node->value = map[i].value; + node->next = list; + list = node; + } + + return list; +} + +VirtioQueueElement *qmp_x_query_virtio_queue_element(const char *path, + uint16_t queue, + bool has_index, + uint16_t index, + Error **errp) +{ + VirtIODevice *vdev; + VirtQueue *vq; + VirtioQueueElement *element = NULL; + + vdev = virtio_device_find(path); + if (vdev == NULL) { + error_setg(errp, "Path %s is not a VirtIO device", path); + return NULL; + } + + if (queue >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, queue)) { + error_setg(errp, "Invalid virtqueue number %d", queue); + return NULL; + } + vq = &vdev->vq[queue]; + + if (virtio_vdev_has_feature(vdev, VIRTIO_F_RING_PACKED)) { + error_setg(errp, "Packed ring not supported"); + return NULL; + } else { + unsigned int head, i, max; + VRingMemoryRegionCaches *caches; + MemoryRegionCache indirect_desc_cache = MEMORY_REGION_CACHE_INVALID; + MemoryRegionCache *desc_cache; + VRingDesc desc; + VirtioRingDescList *list = NULL; + VirtioRingDescList *node; + int rc; + + RCU_READ_LOCK_GUARD(); + + max = vq->vring.num; + + if (!has_index) { + head = vring_avail_ring(vq, vq->last_avail_idx % vq->vring.num); + } else { + head = vring_avail_ring(vq, index % vq->vring.num); + } + i = head; + + caches = vring_get_region_caches(vq); + if (!caches) { + error_setg(errp, "Region caches not initialized"); + return NULL; + } + if (caches->desc.len < max * sizeof(VRingDesc)) { + error_setg(errp, "Cannot map descriptor ring"); + return NULL; + } + + desc_cache = &caches->desc; + vring_split_desc_read(vdev, &desc, desc_cache, i); + if (desc.flags & VRING_DESC_F_INDIRECT) { + int64_t len; + len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as, + desc.addr, desc.len, false); + desc_cache = &indirect_desc_cache; + if (len < desc.len) { + error_setg(errp, "Cannot map indirect buffer"); + goto done; + } + + max = desc.len / sizeof(VRingDesc); + i = 0; + vring_split_desc_read(vdev, &desc, desc_cache, i); + } + + element = g_new0(VirtioQueueElement, 1); + element->avail = g_new0(VirtioRingAvail, 1); + element->used = g_new0(VirtioRingUsed, 1); + element->device_name = g_strdup(vdev->name); + element->index = head; + element->ndescs = 0; + element->avail->flags = vring_avail_flags(vq); + element->avail->idx = vring_avail_idx(vq); + element->avail->ring = head; + element->used->flags = vring_used_flags(vq); + element->used->idx = vring_used_idx(vq); + + do { + /* A buggy driver may produce an infinite loop */ + if (element->ndescs >= max) { + break; + } + node = g_new0(VirtioRingDescList, 1); + node->value = g_new0(VirtioRingDesc, 1); + node->value->addr = desc.addr; + node->value->len = desc.len; + node->value->flags = qmp_decode_vring_desc_flags(desc.flags); + node->next = list; + list = node; + + element->ndescs++; + rc = virtqueue_split_read_next_desc(vdev, &desc, desc_cache, + max, &i); + } while (rc == VIRTQUEUE_READ_DESC_MORE); + element->descs = list; +done: + address_space_cache_destroy(&indirect_desc_cache); + } + + return element; +} + static const TypeInfo virtio_device_info = { .name = TYPE_VIRTIO_DEVICE, .parent = TYPE_DEVICE, diff --git a/qapi/virtio.json b/qapi/virtio.json index 0f65044..c57fbc5 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -1061,3 +1061,180 @@ { 'command': 'x-query-virtio-vhost-queue-status', 'data': { 'path': 'str', 'queue': 'uint16' }, 'returns': 'VirtVhostQueueStatus', 'features': [ 'unstable' ] } + +## +# @VirtioRingDescFlags: +# +# An enumeration of the virtio ring descriptor flags +# +# Since: 6.3 +# +## + +{ 'enum': 'VirtioRingDescFlags', + 'data': [ 'next', 'write', 'indirect', 'avail', 'used' ] +} + +## +# @VirtioRingDesc: +# +# Information regarding the VRing descriptor area +# +# @addr: guest physical address of the descriptor data +# +# @len: length of the descriptor data +# +# @flags: list of descriptor flags +# +# Since: 6.3 +# +## + +{ 'struct': 'VirtioRingDesc', + 'data': { 'addr': 'uint64', + 'len': 'uint32', + 'flags': [ 'VirtioRingDescFlags' ] } } + +## +# @VirtioRingAvail: +# +# Information regarding the avail VRing (also known as the driver +# area) +# +# @flags: VRingAvail flags +# +# @idx: VRingAvail index +# +# @ring: VRingAvail ring[] entry at provided index +# +# Since: 6.3 +# +## + +{ 'struct': 'VirtioRingAvail', + 'data': { 'flags': 'uint16', + 'idx': 'uint16', + 'ring': 'uint16' } } + +## +# @VirtioRingUsed: +# +# Information regarding the used VRing (also known as the device +# area) +# +# @flags: VRingUsed flags +# +# @idx: VRingUsed index +# +# Since: 6.3 +# +## + +{ 'struct': 'VirtioRingUsed', + 'data': { 'flags': 'uint16', + 'idx': 'uint16' } } + +## +# @VirtioQueueElement: +# +# Information regarding a VirtQueue VirtQueueElement including +# descriptor, driver, and device areas +# +# @device-name: name of the VirtIODevice which this VirtQueue belongs +# to (for reference) +# +# @index: index of the element in the queue +# +# @ndescs: number of descriptors +# +# @descs: list of the descriptors +# +# @avail: VRingAvail info +# +# @used: VRingUsed info +# +# Since: 6.3 +# +## + +{ 'struct': 'VirtioQueueElement', + 'data': { 'device-name': 'str', + 'index': 'uint32', + 'ndescs': 'uint32', + 'descs': [ 'VirtioRingDesc' ], + 'avail': 'VirtioRingAvail', + 'used': 'VirtioRingUsed' } } + +## +# @x-query-virtio-queue-element: +# +# Return the information about a VirtQueue VirtQueueElement (by +# default looks at the head of the queue) +# +# @path: VirtIODevice canonical QOM path +# +# @queue: VirtQueue index to examine +# +# @index: the index in the queue, by default head +# +# Features: +# @unstable: This command is meant for debugging. +# +# Returns: VirtioQueueElement information +# +# Since: 6.3 +# +# Examples: +# +# 1. Introspect on virtio-net virtqueue 0 at index 5 +# +# -> { "execute": "x-query-virtio-queue-element", +# "arguments": { "path": "/machine/peripheral-anon/device[1]/virtio-backend", +# "queue": 0, +# "index": 5 } +# } +# <- { "return": { +# "index": 5, +# "ndescs": 1, +# "device-name": "virtio-net", +# "descs": [ { "flags": ["write"], "len": 1536, "addr": 5257305600 } ], +# "avail": { "idx": 256, "flags": 0, "ring": 5 }, +# "used": { "idx": 13, "flags": 0 } } +# } +# +# 2. Introspect on virtio-crypto virtqueue 1 at head +# +# -> { "execute": "x-query-virtio-queue-element", +# "arguments": { "path": "/machine/peripheral/crypto0/virtio-backend", +# "queue": 1 } +# } +# <- { "return": { +# "index": 0, +# "ndescs": 1, +# "device-name": "virtio-crypto", +# "descs": [ { "flags": [], "len": 0, "addr": 8080268923184214134 } ], +# "avail": { "idx": 280, "flags": 0, "ring": 0 }, +# "used": { "idx": 280, "flags": 0 } } +# } +# +# 3. Introspect on virtio-scsi virtqueue 2 at head +# +# -> { "execute": "x-query-virtio-queue-element", +# "arguments": { "path": "/machine/peripheral-anon/device[2]/virtio-backend", +# "queue": 2 } +# } +# <- { "return": { +# "index": 19, +# "ndescs": 1, +# "device-name": "virtio-scsi", +# "descs": [ { "flags": ["used", "indirect", "write"], "len": 4099327944, +# "addr": 12055409292258155293 } ], +# "avail": { "idx": 1147, "flags": 0, "ring": 19 }, +# "used": { "idx": 1147, "flags": 0 } } +# } +# +## + +{ 'command': 'x-query-virtio-queue-element', + 'data': { 'path': 'str', 'queue': 'uint16', '*index': 'uint16' }, + 'returns': 'VirtioQueueElement', 'features': [ 'unstable' ] } -- 1.8.3.1