[PATCH 3/3] tools/virtio: add --features-array switch

"rom.wang" <[email protected]>
Newsgroups gmane.linux.kernel,gmane.comp.emulators.kvm.devel,gmane.linux.network
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.