[PATCH 3/3] tools/virtio: add --features-array switch
"rom.wang" <[email protected]> Tue, 4 Aug 2026 16:01:25 +0800
| Newsgroups | org.kernel.vger.kvm,dev.linux.lists.virtualization,org.kernel.vger.linux-kernel,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
From: Yufeng Wang <[email protected]> Add a --features-array command-line switch that exercises the array-variant feature ioctls, VHOST_SET_FEATURES_ARRAY and VHOST_GET_FEATURES_ARRAY, instead of the legacy VHOST_SET_FEATURES. The legacy path is used when the switch is omitted, so both code paths stay covered. SET: features are programmed via VHOST_SET_FEATURES_ARRAY. GET: VHOST_GET_FEATURES_ARRAY is issued twice with a count above VIRTIO_FEATURES_U64S, once into a zero-initialized buffer and once into a sentinel-filled one; the two reads must match. This verifies the kernel fills the prefix and clear_user()s the trailing entries, and stays correct as the kernel's feature-word count grows. Signed-off-by: Yufeng Wang <[email protected]> --- tools/virtio/vhost_net_test.c | 70 +++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/tools/virtio/vhost_net_test.c b/tools/virtio/vhost_net_test.c index 389d99a6d7c7..77191e60a7a3 100644 --- a/tools/virtio/vhost_net_test.c +++ b/tools/virtio/vhost_net_test.c @@ -26,6 +26,12 @@ #define TEST_PTYPE ETH_P_LOOPBACK #define DESC_NUM 256 +/* Number of u64 entries that the kernel uses to store all currently-defined + * virtio features. Must stay in sync with the kernel's VIRTIO_FEATURES_U64S. */ +#define FEATURES_U64S 2 +/* Above VIRTIO_FEATURES_U64S so trailing entries hit the clear_user() tail. */ +#define GET_FEATURES_ARRAY_COUNT 8 + /* Used by implementation of kmalloc() in tools/virtio/linux/kernel.h */ void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end; @@ -205,8 +211,10 @@ static void vq_info_add(struct vdev_info *dev, int idx, int num, int fd) assert(!r); } -static void vdev_info_init(struct vdev_info *dev, unsigned long long features) +static void vdev_info_init(struct vdev_info *dev, unsigned long long features, + bool features_array) { + struct vhost_features_array *fa; struct ether_header *eh; int i, r; @@ -248,8 +256,20 @@ static void vdev_info_init(struct vdev_info *dev, unsigned long long features) r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem); assert(r >= 0); - r = ioctl(dev->control, VHOST_SET_FEATURES, &features); - assert(r >= 0); + if (features_array) { + fa = calloc(1, struct_size(fa, features, FEATURES_U64S)); + assert(fa); + fa->count = FEATURES_U64S; + fa->features[0] = features; + + r = ioctl(dev->control, VHOST_SET_FEATURES_ARRAY, fa); + assert(r >= 0); + + free(fa); + } else { + r = ioctl(dev->control, VHOST_SET_FEATURES, &features); + assert(r >= 0); + } dev->nvqs = 2; } @@ -455,6 +475,10 @@ static const struct option longopts[] = { .val = 'b', .has_arg = required_argument, }, + { + .name = "features-array", + .val = 'F', + }, { } }; @@ -467,11 +491,43 @@ static void help(int status) " [--no-virtio-1]" " [--delayed-interrupt]" " [--buf-num]" + " [--features-array]" "\n"); exit(status); } +static void verify_get_features_array(struct vdev_info *dev) +{ + struct vhost_features_array *clean, *dirty; + int r, i; + + clean = calloc(1, struct_size(clean, features, GET_FEATURES_ARRAY_COUNT)); + assert(clean); + clean->count = GET_FEATURES_ARRAY_COUNT; + r = ioctl(dev->control, VHOST_GET_FEATURES_ARRAY, clean); + assert(r >= 0); + + /* Sentinel-fill so the kernel's clear_user() of the tail is visible. */ + dirty = calloc(1, struct_size(dirty, features, GET_FEATURES_ARRAY_COUNT)); + assert(dirty); + dirty->count = GET_FEATURES_ARRAY_COUNT; + memset(dirty->features, 0xa5, GET_FEATURES_ARRAY_COUNT * sizeof(dirty->features[0])); + r = ioctl(dev->control, VHOST_GET_FEATURES_ARRAY, dirty); + assert(r >= 0); + + /* Must match; a wrong clear_user() start would leave them unequal. */ + for (i = 0; i < GET_FEATURES_ARRAY_COUNT; i++) + assert(clean->features[i] == dirty->features[i]); + + printf("GET_FEATURES_ARRAY: 0x%llx 0x%llx (tail zeroed) OK\n", + (unsigned long long)clean->features[0], + (unsigned long long)clean->features[1]); + + free(clean); + free(dirty); +} + int main(int argc, char **argv) { unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) | @@ -480,6 +536,7 @@ int main(int argc, char **argv) long nbufs = 0x100000; struct vdev_info dev; bool delayed = false; + bool features_array = false; int o, fd; for (;;) { @@ -503,6 +560,9 @@ int main(int argc, char **argv) case 'D': delayed = true; break; + case 'F': + features_array = true; + break; case 'n': nbufs = strtol(optarg, NULL, 10); assert(nbufs > 0); @@ -520,7 +580,9 @@ int main(int argc, char **argv) fd = tun_alloc(&dev, tun_name); assert(fd >= 0); - vdev_info_init(&dev, features); + vdev_info_init(&dev, features, features_array); + if (features_array) + verify_get_features_array(&dev); vq_info_add(&dev, 0, DESC_NUM, fd); vq_info_add(&dev, 1, DESC_NUM, fd); vdev_create_socket(&dev, tun_name); -- 2.34.1