[RFC PATCH 5/6] virtio_test: support vhost-vdpa device

From: Stefano Garzarella
Date: Mon Jul 04 2022 - 13:18:03 EST


The new --vdpa parameter can be used to run virtio_test with
the new vdpa_sim_test.ko that implements the device.

The main differences with vhost_test are:
- control of status register
- dma map messages
- VHOST_SET_MEM_TABLE not supported by vhost-vdpa
- VHOST_TEST_RUN not supported by vhost-vdpa

The --reset option is not supported for now when using vhost-vdpa.

Signed-off-by: Stefano Garzarella <sgarzare@xxxxxxxxxx>
---
tools/virtio/virtio_test.c | 127 +++++++++++++++++++++++++++++++------
1 file changed, 109 insertions(+), 18 deletions(-)

diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c
index 2d8a3e881637..91f983266d86 100644
--- a/tools/virtio/virtio_test.c
+++ b/tools/virtio/virtio_test.c
@@ -44,6 +44,8 @@ struct vdev_info {
void *buf;
size_t buf_size;
struct vhost_memory *mem;
+ bool vdpa;
+ uint64_t backend_features;
};

static const struct vhost_vring_file no_backend = { .fd = -1 },
@@ -64,6 +66,36 @@ void vq_callback(struct virtqueue *vq)
{
}

+static void vdpa_add_status(struct vdev_info *dev, uint8_t status)
+{
+ uint8_t current_status;
+ int r;
+
+ r = ioctl(dev->control, VHOST_VDPA_GET_STATUS, &current_status);
+ assert(r >= 0);
+ current_status |= status;
+ r = ioctl(dev->control, VHOST_VDPA_SET_STATUS, &current_status);
+ assert(r >= 0);
+ r = ioctl(dev->control, VHOST_VDPA_GET_STATUS, &current_status);
+ assert(r >= 0);
+ assert((current_status & status) == status);
+}
+
+static void vdpa_dma_map(struct vdev_info *dev, uint64_t iova, uint64_t size,
+ uint64_t uaddr, uint8_t perm)
+{
+ struct vhost_msg_v2 msg = {};
+ int r;
+
+ msg.type = VHOST_IOTLB_MSG_V2;
+ msg.iotlb.iova = iova;
+ msg.iotlb.size = size;
+ msg.iotlb.uaddr = uaddr;
+ msg.iotlb.perm = perm;
+ msg.iotlb.type = VHOST_IOTLB_UPDATE;
+ r = write(dev->control, &msg, sizeof(msg));
+ assert(r == sizeof(msg));
+}

void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
{
@@ -76,6 +108,12 @@ void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
.used_user_addr = (uint64_t)(unsigned long)info->vring.used,
};
int r;
+ if (dev->vdpa) {
+ vdpa_dma_map(dev, (uint64_t)(unsigned long)info->ring,
+ vring_size(info->vring.num, 4096),
+ (uint64_t)(unsigned long)info->ring,
+ VHOST_ACCESS_RW);
+ }
state.num = info->vring.num;
r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
assert(r >= 0);
@@ -90,6 +128,11 @@ void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
file.fd = info->call;
r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file);
assert(r >= 0);
+ if (dev->vdpa) {
+ state.num = 1;
+ r = ioctl(dev->control, VHOST_VDPA_SET_VRING_ENABLE, &state);
+ assert(r >= 0);
+ }
}

static void vq_reset(struct vq_info *info, int num, struct virtio_device *vdev)
@@ -121,33 +164,61 @@ static void vq_info_add(struct vdev_info *dev, int num)
dev->nvqs++;
}

-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,
+ char *vdpa_dev)
{
+ char *vhost_dev = "/dev/vhost-test";
int r;
memset(dev, 0, sizeof *dev);
dev->vdev.features = features;
+ if (vdpa_dev) {
+ dev->vdpa = true;
+ vhost_dev = vdpa_dev;
+ }
INIT_LIST_HEAD(&dev->vdev.vqs);
spin_lock_init(&dev->vdev.vqs_list_lock);
dev->buf_size = 1024;
dev->buf = malloc(dev->buf_size);
assert(dev->buf);
- dev->control = open("/dev/vhost-test", O_RDWR);
+ dev->control = open(vhost_dev, O_RDWR);
assert(dev->control >= 0);
r = ioctl(dev->control, VHOST_SET_OWNER, NULL);
assert(r >= 0);
- dev->mem = malloc(offsetof(struct vhost_memory, regions) +
- sizeof dev->mem->regions[0]);
- assert(dev->mem);
- memset(dev->mem, 0, offsetof(struct vhost_memory, regions) +
- sizeof dev->mem->regions[0]);
- dev->mem->nregions = 1;
- dev->mem->regions[0].guest_phys_addr = (long)dev->buf;
- dev->mem->regions[0].userspace_addr = (long)dev->buf;
- dev->mem->regions[0].memory_size = dev->buf_size;
- r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
- assert(r >= 0);
+ if (!dev->vdpa) {
+ dev->mem = malloc(offsetof(struct vhost_memory, regions) +
+ sizeof(dev->mem->regions[0]));
+ assert(dev->mem);
+ memset(dev->mem, 0, offsetof(struct vhost_memory, regions) +
+ sizeof(dev->mem->regions[0]));
+ dev->mem->nregions = 1;
+ dev->mem->regions[0].guest_phys_addr = (long)dev->buf;
+ dev->mem->regions[0].userspace_addr = (long)dev->buf;
+ dev->mem->regions[0].memory_size = dev->buf_size;
+ r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
+ assert(r >= 0);
+ } else {
+ uint8_t status = 0;
+
+ r = ioctl(dev->control, VHOST_GET_BACKEND_FEATURES,
+ &dev->backend_features);
+ assert(r >= 0);
+ dev->backend_features &= 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2;
+ assert(dev->backend_features);
+ r = ioctl(dev->control, VHOST_SET_BACKEND_FEATURES,
+ &dev->backend_features);
+ assert(r >= 0);
+ r = ioctl(dev->control, VHOST_VDPA_SET_STATUS, &status);
+ assert(r >= 0);
+ vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
+ VIRTIO_CONFIG_S_DRIVER);
+ vdpa_dma_map(dev, (uint64_t)(unsigned long)dev->buf,
+ dev->buf_size, (uint64_t)(unsigned long)dev->buf,
+ VHOST_ACCESS_RW);
+ }
r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
assert(r >= 0);
+ if (dev->vdpa)
+ vdpa_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
}

/* TODO: this is pretty bad: we get a cache line bounce
@@ -177,8 +248,13 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq,
const bool random_batch = batch == RANDOM_BATCH;

__virtio_unbreak_device(&dev->vdev);
- r = ioctl(dev->control, VHOST_TEST_RUN, &test);
- assert(r >= 0);
+
+ if (!dev->vdpa) {
+ r = ioctl(dev->control, VHOST_TEST_RUN, &test);
+ assert(r >= 0);
+ } else {
+ vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
+ }
if (!reset_n) {
next_reset = INT_MAX;
}
@@ -268,8 +344,10 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq,
}
}
test = 0;
- r = ioctl(dev->control, VHOST_TEST_RUN, &test);
- assert(r >= 0);
+ if (!dev->vdpa) {
+ r = ioctl(dev->control, VHOST_TEST_RUN, &test);
+ assert(r >= 0);
+ }
fprintf(stderr,
"spurious wakeups: 0x%llx started=0x%lx completed=0x%lx\n",
spurious, started, completed);
@@ -323,6 +401,11 @@ const struct option longopts[] = {
.val = 'r',
.has_arg = optional_argument,
},
+ {
+ .name = "vdpa",
+ .val = 'V',
+ .has_arg = required_argument,
+ },
{
}
};
@@ -336,6 +419,7 @@ static void help(void)
" [--delayed-interrupt]"
" [--batch=random/N]"
" [--reset=N]"
+ " [--vdpa=/dev/vhost-vdpa-N]"
"\n");
}

@@ -347,6 +431,7 @@ int main(int argc, char **argv)
long batch = 1, reset = 0;
int o;
bool delayed = false;
+ char *vdpa_dev = NULL;

for (;;) {
o = getopt_long(argc, argv, optstring, longopts, NULL);
@@ -389,6 +474,10 @@ int main(int argc, char **argv)
assert(reset < (long)INT_MAX + 1);
}
break;
+ case 'V':
+ vdpa_dev = optarg;
+ features |= 1ULL << VIRTIO_F_ACCESS_PLATFORM;
+ break;
default:
assert(0);
break;
@@ -396,7 +485,9 @@ int main(int argc, char **argv)
}

done:
- vdev_info_init(&dev, features);
+ //TODO: support reset for vdpa
+ assert(vdpa_dev && !reset);
+ vdev_info_init(&dev, features, vdpa_dev);
vq_info_add(&dev, 256);
run_test(&dev, &dev.vqs[0], delayed, batch, reset, 0x100000);
return 0;
--
2.36.1