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


Reply via email to