[PATCH 4/4] tools/virtio: Add virtqueue_use_wrap_counter support

Cristian Marussi cristian.marussi at arm.com
Tue Feb 8 07:25:20 PST 2022


Added simplified missing headers for VRING_POLL_* macros and a definition
of virtqueue_use_wrap_counter API.

Add --wrap-counters option to virtio_test to be able to test the new
virtqueue_use_wrap_counter API.

Signed-off-by: Cristian Marussi <cristian.marussi at arm.com>
---
 tools/virtio/linux/bitfield.h | 88 +++++++++++++++++++++++++++++++++++
 tools/virtio/linux/bits.h     | 36 ++++++++++++++
 tools/virtio/linux/virtio.h   | 33 +++++++++++++
 tools/virtio/virtio_test.c    | 35 ++++++++++++--
 4 files changed, 189 insertions(+), 3 deletions(-)
 create mode 100644 tools/virtio/linux/bitfield.h
 create mode 100644 tools/virtio/linux/bits.h

diff --git a/tools/virtio/linux/bitfield.h b/tools/virtio/linux/bitfield.h
new file mode 100644
index 000000000000..43421844eecc
--- /dev/null
+++ b/tools/virtio/linux/bitfield.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _LINUX_BITFIELD_H
+#define _LINUX_BITFIELD_H
+
+#include <asm/byteorder.h>
+
+/*
+ * Bitfield access macros
+ *
+ * FIELD_{GET,PREP} macros take as first parameter shifted mask
+ * from which they extract the base mask and shift amount.
+ * Mask must be a compilation time constant.
+ *
+ * Example:
+ *
+ *  #define REG_FIELD_A  GENMASK(6, 0)
+ *  #define REG_FIELD_B  BIT(7)
+ *  #define REG_FIELD_C  GENMASK(15, 8)
+ *  #define REG_FIELD_D  GENMASK(31, 16)
+ *
+ * Get:
+ *  a = FIELD_GET(REG_FIELD_A, reg);
+ *  b = FIELD_GET(REG_FIELD_B, reg);
+ *
+ * Set:
+ *  reg = FIELD_PREP(REG_FIELD_A, 1) |
+ *	  FIELD_PREP(REG_FIELD_B, 0) |
+ *	  FIELD_PREP(REG_FIELD_C, c) |
+ *	  FIELD_PREP(REG_FIELD_D, 0x40);
+ *
+ * Modify:
+ *  reg &= ~REG_FIELD_C;
+ *  reg |= FIELD_PREP(REG_FIELD_C, c);
+ */
+
+#define __bf_shf(x) (__builtin_ffsll(x) - 1)
+
+/**
+ * FIELD_MAX() - produce the maximum value representable by a field
+ * @_mask: shifted mask defining the field's length and position
+ *
+ * FIELD_MAX() returns the maximum value that can be held in the field
+ * specified by @_mask.
+ */
+#define FIELD_MAX(_mask)						\
+	({								\
+		(typeof(_mask))((_mask) >> __bf_shf(_mask));		\
+	})
+
+/**
+ * FIELD_FIT() - check if value fits in the field
+ * @_mask: shifted mask defining the field's length and position
+ * @_val:  value to test against the field
+ *
+ * Return: true if @_val can fit inside @_mask, false if @_val is too big.
+ */
+#define FIELD_FIT(_mask, _val)						\
+	({								\
+		!((((typeof(_mask))_val) << __bf_shf(_mask)) & ~(_mask)); \
+	})
+
+/**
+ * FIELD_PREP() - prepare a bitfield element
+ * @_mask: shifted mask defining the field's length and position
+ * @_val:  value to put in the field
+ *
+ * FIELD_PREP() masks and shifts up the value.  The result should
+ * be combined with other fields of the bitfield using logical OR.
+ */
+#define FIELD_PREP(_mask, _val)						\
+	({								\
+		((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask);	\
+	})
+
+/**
+ * FIELD_GET() - extract a bitfield element
+ * @_mask: shifted mask defining the field's length and position
+ * @_reg:  value of entire bitfield
+ *
+ * FIELD_GET() extracts the field specified by @_mask from the
+ * bitfield passed in as @_reg by masking and shifting it down.
+ */
+#define FIELD_GET(_mask, _reg)						\
+	({								\
+		(typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask));	\
+	})
+
+#endif
diff --git a/tools/virtio/linux/bits.h b/tools/virtio/linux/bits.h
new file mode 100644
index 000000000000..16f8fe32b0b3
--- /dev/null
+++ b/tools/virtio/linux/bits.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_BITS_H
+#define __LINUX_BITS_H
+
+#include <linux/const.h>
+#include <vdso/bits.h>
+#include <asm/bitsperlong.h>
+
+#define BIT_ULL(nr)		(ULL(1) << (nr))
+#define BIT_MASK(nr)		(UL(1) << ((nr) % BITS_PER_LONG))
+#define BIT_WORD(nr)		((nr) / BITS_PER_LONG)
+#define BIT_ULL_MASK(nr)	(ULL(1) << ((nr) % BITS_PER_LONG_LONG))
+#define BIT_ULL_WORD(nr)	((nr) / BITS_PER_LONG_LONG)
+#define BITS_PER_BYTE		8
+
+/*
+ * Create a contiguous bitmask starting at bit position @l and ending at
+ * position @h. For example
+ * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
+ */
+
+#define GENMASK_INPUT_CHECK(h, l) 0
+
+#define __GENMASK(h, l) \
+	(((~UL(0)) - (UL(1) << (l)) + 1) & \
+	 (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
+#define GENMASK(h, l) \
+	(GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
+
+#define __GENMASK_ULL(h, l) \
+	(((~ULL(0)) - (ULL(1) << (l)) + 1) & \
+	 (~ULL(0) >> (BITS_PER_LONG_LONG - 1 - (h))))
+#define GENMASK_ULL(h, l) \
+	(GENMASK_INPUT_CHECK(h, l) + __GENMASK_ULL(h, l))
+
+#endif	/* __LINUX_BITS_H */
diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h
index 9021187a66d9..0556e093f223 100644
--- a/tools/virtio/linux/virtio.h
+++ b/tools/virtio/linux/virtio.h
@@ -5,6 +5,8 @@
 #include <linux/kernel.h>
 #include <linux/spinlock.h>
 
+#define VIRTIO_CONFIG_S_DRIVER	2
+
 typedef struct pm_message {
 	int event;
 } pm_message_t;
@@ -48,9 +50,12 @@ struct device {
 	struct device_driver *driver;
 };
 
+struct virtio_config_ops;
+
 struct virtio_device {
 	struct device dev;
 	u64 features;
+	const struct virtio_config_ops *config;
 	struct list_head vqs;
 	spinlock_t vqs_list_lock;
 };
@@ -65,6 +70,32 @@ struct virtqueue {
 	void *priv;
 };
 
+struct virtio_config_ops {
+	void (*enable_cbs)(struct virtio_device *vdev);
+	void (*get)(struct virtio_device *vdev, unsigned int offset,
+		    void *buf, unsigned int len);
+	void (*set)(struct virtio_device *vdev, unsigned int offset,
+		    const void *buf, unsigned int len);
+	u32 (*generation)(struct virtio_device *vdev);
+	u8 (*get_status)(struct virtio_device *vdev);
+	void (*set_status)(struct virtio_device *vdev, u8 status);
+	void (*reset)(struct virtio_device *vdev);
+	int (*find_vqs)(struct virtio_device *vdev, unsigned int nvqs,
+			struct virtqueue *vqs[], void *callbacks[],
+			const char * const names[], const bool *ctx,
+			void *desc);
+	void (*del_vqs)(struct virtio_device *vdev);
+	u64 (*get_features)(struct virtio_device *vdev);
+	int (*finalize_features)(struct virtio_device *vdev);
+	const char *(*bus_name)(struct virtio_device *vdev);
+	int (*set_vq_affinity)(struct virtqueue *vq,
+			       const void *cpu_mask);
+	const void *(*get_vq_affinity)(struct virtio_device *vdev,
+			int index);
+	bool (*get_shm_region)(struct virtio_device *vdev,
+			       void *region, u8 id);
+};
+
 struct virtio_driver {
 	struct device_driver driver;
 	const struct virtio_device_id *id_table;
@@ -111,6 +142,8 @@ void virtqueue_disable_cb(struct virtqueue *vq);
 bool virtqueue_enable_cb(struct virtqueue *vq);
 bool virtqueue_enable_cb_delayed(struct virtqueue *vq);
 
+int virtqueue_use_wrap_counter(struct virtqueue *vq);
+
 void *virtqueue_detach_unused_buf(struct virtqueue *vq);
 struct virtqueue *vring_new_virtqueue(unsigned int index,
 				      unsigned int num,
diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c
index d9cb3d22d52f..deccd44e4739 100644
--- a/tools/virtio/virtio_test.c
+++ b/tools/virtio/virtio_test.c
@@ -21,6 +21,8 @@
 
 #define RANDOM_BATCH -1
 
+static u8 virtio_test_device_status;
+
 /* Unused */
 void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end;
 
@@ -109,7 +111,7 @@ static void vq_reset(struct vq_info *info, int num, struct virtio_device *vdev)
 	info->vq->priv = info;
 }
 
-static void vq_info_add(struct vdev_info *dev, int num)
+static void vq_info_add(struct vdev_info *dev, int num, bool use_wrap_counters)
 {
 	struct vq_info *info = &dev->vqs[dev->nvqs];
 	int r;
@@ -119,12 +121,25 @@ static void vq_info_add(struct vdev_info *dev, int num)
 	r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
 	assert(r >= 0);
 	vq_reset(info, num, &dev->vdev);
+	if (use_wrap_counters) {
+		r = virtqueue_use_wrap_counter(info->vq);
+		assert(r == 0);
+	}
 	vhost_vq_setup(dev, info);
 	dev->fds[info->idx].fd = info->call;
 	dev->fds[info->idx].events = POLLIN;
 	dev->nvqs++;
 }
 
+static u8 virtio_test_get_status(struct virtio_device *vdev)
+{
+	return virtio_test_device_status;
+}
+
+struct virtio_config_ops virtio_config_ops = {
+	.get_status = virtio_test_get_status,
+};
+
 static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
 {
 	int r;
@@ -132,6 +147,8 @@ static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
 	dev->vdriver.suppress_used_validation = false;
 	dev->vdev.dev.driver = &dev->vdriver.driver;
 	dev->vdev.features = features;
+	virtio_test_device_status = VIRTIO_CONFIG_S_DRIVER;
+	dev->vdev.config = &virtio_config_ops;
 	INIT_LIST_HEAD(&dev->vdev.vqs);
 	spin_lock_init(&dev->vdev.vqs_list_lock);
 	dev->buf_size = 1024;
@@ -316,6 +333,14 @@ const struct option longopts[] = {
 		.name = "no-delayed-interrupt",
 		.val = 'd',
 	},
+	{
+		.name = "wrap-counters",
+		.val = 'W',
+	},
+	{
+		.name = "no-wrap-counters",
+		.val = 'w',
+	},
 	{
 		.name = "batch",
 		.val = 'b',
@@ -337,6 +362,7 @@ static void help(void)
 		" [--no-event-idx]"
 		" [--no-virtio-1]"
 		" [--delayed-interrupt]"
+		" [--wrap-counters]"
 		" [--batch=random/N]"
 		" [--reset=N]"
 		"\n");
@@ -349,7 +375,7 @@ int main(int argc, char **argv)
 		(1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1);
 	long batch = 1, reset = 0;
 	int o;
-	bool delayed = false;
+	bool delayed = false, use_wrap_counters = false;
 
 	for (;;) {
 		o = getopt_long(argc, argv, optstring, longopts, NULL);
@@ -374,6 +400,9 @@ int main(int argc, char **argv)
 		case 'D':
 			delayed = true;
 			break;
+		case 'W':
+			use_wrap_counters = true;
+			break;
 		case 'b':
 			if (0 == strcmp(optarg, "random")) {
 				batch = RANDOM_BATCH;
@@ -400,7 +429,7 @@ int main(int argc, char **argv)
 
 done:
 	vdev_info_init(&dev, features);
-	vq_info_add(&dev, 256);
+	vq_info_add(&dev, 256, use_wrap_counters);
 	run_test(&dev, &dev.vqs[0], delayed, batch, reset, 0x100000);
 	return 0;
 }
-- 
2.17.1




More information about the linux-arm-kernel mailing list