[PATCH 3/3] tools/virtio: add --features-array switch
From: rom.wang
Date: Tue Aug 04 2026 - 04:13:59 EST
From: Yufeng Wang <wangyufeng@xxxxxxxxxx>
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 <wangyufeng@xxxxxxxxxx>
---
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