Skip to contentSkip to conversation

RFC: PCI using capabilitities

PATCH In review Rusty Russell 8 participants 105 messages Dec 8, 2011 – May 30, 2013

Subsystems (7)
lore
Share & tools
lore
#0 lore raw

cover RFC: PCI using capabilitities

Here's the patch series I ended up with. I haven't coded up the QEMU side yet, so no idea if the new driver works.

Questions:
(1) Do we win from separating ISR, NOTIFY and COMMON?
(2) I used a "u8 bar"; should I use a bir and pack it instead? BIR seems a little obscure (noone else in the kernel source seems to refer to it).

Cheers,
Rusty.

#1 lore raw

RFC 1/11 virtio: use u32, not bitmap for struct virtio_device's features

It seemed like a good idea, but it's actually a pain when we get more than 32 feature bits. Just change it to a u32 for now.

---
 drivers/char/virtio_console.c  |    2 +-
 drivers/lguest/lguest_device.c |    2 +-
 drivers/s390/kvm/kvm_virtio.c  |    2 +-
 drivers/virtio/virtio.c        |   10 +++++-----
 drivers/virtio/virtio_mmio.c   |    8 ++------
 drivers/virtio/virtio_pci.c    |    3 +--
 drivers/virtio/virtio_ring.c   |    2 +-
 include/linux/virtio.h         |    3 +--
 include/linux/virtio_config.h  |    2 +-
 tools/virtio/linux/virtio.h    |   18 ++----------------
 tools/virtio/virtio_test.c     |    5 ++---
 11 files changed, 18 insertions(+), 39 deletions(-)
drivers/char/virtio_console.c +1 -1
@@ -331,7 +331,7 @@ static inline bool use_multiport(struct 
 	 */
 	if (!portdev->vdev)
 		return 0;
-	return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
+	return portdev->vdev->features & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
 }
 
 static void free_buf(struct port_buffer *buf)
drivers/lguest/lguest_device.c +1 -1
@@ -144,7 +144,7 @@ static void lg_finalize_features(struct 
 	memset(out_features, 0, desc->feature_len);
 	bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8;
 	for (i = 0; i < bits; i++) {
-		if (test_bit(i, vdev->features))
+		if (vdev->features & (1 << i))
 			out_features[i / 8] |= (1 << (i % 8));
 	}
 
drivers/s390/kvm/kvm_virtio.c +1 -1
@@ -105,7 +105,7 @@ static void kvm_finalize_features(struct
 	memset(out_features, 0, desc->feature_len);
 	bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8;
 	for (i = 0; i < bits; i++) {
-		if (test_bit(i, vdev->features))
+		if (vdev->features & (1 << i))
 			out_features[i / 8] |= (1 << (i % 8));
 	}
 }
drivers/virtio/virtio.c +5 -5
@@ -41,9 +41,9 @@ static ssize_t features_show(struct devi
 
 	/* We actually represent this as a bitstring, as it could be
 	 * arbitrary length in future. */
-	for (i = 0; i < ARRAY_SIZE(dev->features)*BITS_PER_LONG; i++)
+	for (i = 0; i < sizeof(dev->features)*8; i++)
 		len += sprintf(buf+len, "%c",
-			       test_bit(i, dev->features) ? '1' : '0');
+			       dev->features & (1ULL << i) ? '1' : '0');
 	len += sprintf(buf+len, "\n");
 	return len;
 }
@@ -122,18 +122,18 @@ static int virtio_dev_probe(struct devic
 	device_features = dev->config->get_features(dev);
 
 	/* Features supported by both device and driver into dev->features. */
-	memset(dev->features, 0, sizeof(dev->features));
+	dev->features = 0;
 	for (i = 0; i < drv->feature_table_size; i++) {
 		unsigned int f = drv->feature_table[i];
 		BUG_ON(f >= 32);
 		if (device_features & (1 << f))
-			set_bit(f, dev->features);
+			dev->features |= (1 << f);
 	}
 
 	/* Transport features always preserved to pass to finalize_features. */
 	for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
 		if (device_features & (1 << i))
-			set_bit(i, dev->features);
+			dev->features |= (1 << i);
 
 	dev->config->finalize_features(dev);
 
drivers/virtio/virtio_mmio.c +2 -6
@@ -112,16 +112,12 @@ static u32 vm_get_features(struct virtio
 static void vm_finalize_features(struct virtio_device *vdev)
 {
 	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
-	int i;
 
 	/* Give virtio_ring a chance to accept features. */
 	vring_transport_features(vdev);
 
-	for (i = 0; i < ARRAY_SIZE(vdev->features); i++) {
-		writel(i, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES_SEL);
-		writel(vdev->features[i],
-				vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES);
-	}
+	writel(0, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES_SEL);
+	writel(vdev->features, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES);
 }
 
 static void vm_get(struct virtio_device *vdev, unsigned offset,
drivers/virtio/virtio_pci.c +1 -2
@@ -121,8 +121,7 @@ static void vp_finalize_features(struct 
 	vring_transport_features(vdev);
 
 	/* We only support 32 feature bits. */
-	BUILD_BUG_ON(ARRAY_SIZE(vdev->features) != 1);
-	iowrite32(vdev->features[0], vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES);
+	iowrite32(vdev->features, vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES);
 }
 
 /* virtio config->get() implementation */
drivers/virtio/virtio_ring.c +1 -1
@@ -685,7 +685,7 @@ void vring_transport_features(struct vir
 			break;
 		default:
 			/* We don't understand this bit. */
-			clear_bit(i, vdev->features);
+			vdev->features &= ~(1 << i);
 		}
 	}
 }
include/linux/virtio.h +1 -2
@@ -66,8 +66,7 @@ struct virtio_device {
 	struct virtio_device_id id;
 	struct virtio_config_ops *config;
 	struct list_head vqs;
-	/* Note that this is a Linux set_bit-style bitmap. */
-	unsigned long features[1];
+	u32 features;
 	void *priv;
 };
 
include/linux/virtio_config.h +1 -1
@@ -142,7 +142,7 @@ static inline bool virtio_has_feature(co
 	if (fbit < VIRTIO_TRANSPORT_F_START)
 		virtio_check_driver_offered_feature(vdev, fbit);
 
-	return test_bit(fbit, vdev->features);
+	return vdev->features & (1 << fbit);
 }
 
 /**
tools/virtio/linux/virtio.h +2 -16
@@ -131,29 +131,15 @@ static inline void kfree(void *p)
 #define BITS_PER_BYTE		8
 #define BITS_PER_LONG (sizeof(long) * BITS_PER_BYTE)
 #define BIT_MASK(nr)		(1UL << ((nr) % BITS_PER_LONG))
-/* TODO: Not atomic as it should be:
- * we don't use this for anything important. */
-static inline void clear_bit(int nr, volatile unsigned long *addr)
-{
-	unsigned long mask = BIT_MASK(nr);
-	unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
-
-	*p &= ~mask;
-}
-
-static inline int test_bit(int nr, const volatile unsigned long *addr)
-{
-        return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
-}
 
 /* The only feature we care to support */
 #define virtio_has_feature(dev, feature) \
-	test_bit((feature), (dev)->features)
+	((dev)->features & (1 << (feature)))
 /* end of stubs */
 
 struct virtio_device {
 	void *dev;
-	unsigned long features[1];
+	u32 features;
 };
 
 struct virtqueue {
tools/virtio/virtio_test.c +2 -3
@@ -55,7 +55,7 @@ void vhost_vq_setup(struct vdev_info *de
 {
 	struct vhost_vring_state state = { .index = info->idx };
 	struct vhost_vring_file file = { .index = info->idx };
-	unsigned long long features = dev->vdev.features[0];
+	unsigned long long features = dev->vdev.features;
 	struct vhost_vring_addr addr = {
 		.index = info->idx,
 		.desc_user_addr = (uint64_t)(unsigned long)info->vring.desc,
@@ -106,8 +106,7 @@ static void vdev_info_init(struct vdev_i
 {
 	int r;
 	memset(dev, 0, sizeof *dev);
-	dev->vdev.features[0] = features;
-	dev->vdev.features[1] = features >> 32;
+	dev->vdev.features = features;
 	dev->buf_size = 1024;
 	dev->buf = malloc(dev->buf_size);
 	assert(dev->buf);
#2 lore raw

RFC 2/11 virtio: add support for 64 bit features.

In reply to Rusty Russell

Change the u32 to a u64, and make sure to use 1ULL everywhere!

---
 drivers/char/virtio_console.c  |    2 +-
 drivers/lguest/lguest_device.c |   10 +++++-----
 drivers/s390/kvm/kvm_virtio.c  |   10 +++++-----
 drivers/virtio/virtio.c        |   12 ++++++------
 drivers/virtio/virtio_mmio.c   |   14 +++++++++-----
 drivers/virtio/virtio_pci.c    |    5 ++---
 drivers/virtio/virtio_ring.c   |    2 +-
 include/linux/virtio.h         |    2 +-
 include/linux/virtio_config.h  |    8 ++++----
 tools/virtio/linux/virtio.h    |    4 ++--
 10 files changed, 36 insertions(+), 33 deletions(-)
drivers/char/virtio_console.c +1 -1
@@ -331,7 +331,7 @@ static inline bool use_multiport(struct 
 	 */
 	if (!portdev->vdev)
 		return 0;
-	return portdev->vdev->features & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
+	return portdev->vdev->features & (1ULL << VIRTIO_CONSOLE_F_MULTIPORT);
 }
 
 static void free_buf(struct port_buffer *buf)
drivers/lguest/lguest_device.c +5 -5
@@ -94,17 +94,17 @@ static unsigned desc_size(const struct l
 }
 
 /* This gets the device's feature bits. */
-static u32 lg_get_features(struct virtio_device *vdev)
+static u64 lg_get_features(struct virtio_device *vdev)
 {
 	unsigned int i;
-	u32 features = 0;
+	u64 features = 0;
 	struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
 	u8 *in_features = lg_features(desc);
 
 	/* We do this the slow but generic way. */
-	for (i = 0; i < min(desc->feature_len * 8, 32); i++)
+	for (i = 0; i < min(desc->feature_len * 8, 64); i++)
 		if (in_features[i / 8] & (1 << (i % 8)))
-			features |= (1 << i);
+			features |= (1ULL << i);
 
 	return features;
 }
@@ -144,7 +144,7 @@ static void lg_finalize_features(struct 
 	memset(out_features, 0, desc->feature_len);
 	bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8;
 	for (i = 0; i < bits; i++) {
-		if (vdev->features & (1 << i))
+		if (vdev->features & (1ULL << i))
 			out_features[i / 8] |= (1 << (i % 8));
 	}
 
drivers/s390/kvm/kvm_virtio.c +5 -5
@@ -79,16 +79,16 @@ static unsigned desc_size(const struct k
 }
 
 /* This gets the device's feature bits. */
-static u32 kvm_get_features(struct virtio_device *vdev)
+static u64 kvm_get_features(struct virtio_device *vdev)
 {
 	unsigned int i;
-	u32 features = 0;
+	u64 features = 0;
 	struct kvm_device_desc *desc = to_kvmdev(vdev)->desc;
 	u8 *in_features = kvm_vq_features(desc);
 
-	for (i = 0; i < min(desc->feature_len * 8, 32); i++)
+	for (i = 0; i < min(desc->feature_len * 8, 64); i++)
 		if (in_features[i / 8] & (1 << (i % 8)))
-			features |= (1 << i);
+			features |= (1ULL << i);
 	return features;
 }
 
@@ -105,7 +105,7 @@ static void kvm_finalize_features(struct
 	memset(out_features, 0, desc->feature_len);
 	bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8;
 	for (i = 0; i < bits; i++) {
-		if (vdev->features & (1 << i))
+		if (vdev->features & (1ULL << i))
 			out_features[i / 8] |= (1 << (i % 8));
 	}
 }
drivers/virtio/virtio.c +6 -6
@@ -113,7 +113,7 @@ static int virtio_dev_probe(struct devic
 	struct virtio_device *dev = container_of(_d,struct virtio_device,dev);
 	struct virtio_driver *drv = container_of(dev->dev.driver,
 						 struct virtio_driver, driver);
-	u32 device_features;
+	u64 device_features;
 
 	/* We have a driver! */
 	add_status(dev, VIRTIO_CONFIG_S_DRIVER);
@@ -125,15 +125,15 @@ static int virtio_dev_probe(struct devic
 	dev->features = 0;
 	for (i = 0; i < drv->feature_table_size; i++) {
 		unsigned int f = drv->feature_table[i];
-		BUG_ON(f >= 32);
-		if (device_features & (1 << f))
-			dev->features |= (1 << f);
+		BUG_ON(f >= 64);
+		if (device_features & (1ULL << f))
+			dev->features |= (1ULL << f);
 	}
 
 	/* Transport features always preserved to pass to finalize_features. */
 	for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
-		if (device_features & (1 << i))
-			dev->features |= (1 << i);
+		if (device_features & (1ULL << i))
+			dev->features |= (1ULL << i);
 
 	dev->config->finalize_features(dev);
 
drivers/virtio/virtio_mmio.c +9 -5
@@ -99,14 +99,16 @@ struct virtio_mmio_vq_info {
 
 /* Configuration interface */
 
-static u32 vm_get_features(struct virtio_device *vdev)
+static u64 vm_get_features(struct virtio_device *vdev)
 {
 	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+	u64 features;
 
-	/* TODO: Features > 32 bits */
 	writel(0, vm_dev->base + VIRTIO_MMIO_HOST_FEATURES_SEL);
-
-	return readl(vm_dev->base + VIRTIO_MMIO_HOST_FEATURES);
+	features = readl(vm_dev->base + VIRTIO_MMIO_HOST_FEATURES);
+	writel(1, vm_dev->base + VIRTIO_MMIO_HOST_FEATURES_SEL);
+	features |= ((u64)readl(vm_dev->base + VIRTIO_MMIO_HOST_FEATURES) << 32);
+	return features;
 }
 
 static void vm_finalize_features(struct virtio_device *vdev)
@@ -117,7 +119,9 @@ static void vm_finalize_features(struct 
 	vring_transport_features(vdev);
 
 	writel(0, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES_SEL);
-	writel(vdev->features, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES);
+	writel((u32)vdev->features, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES);
+	writel(1, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES_SEL);
+	writel(vdev->features >> 32, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES);
 }
 
 static void vm_get(struct virtio_device *vdev, unsigned offset,
drivers/virtio/virtio_pci.c +2 -3
@@ -103,12 +103,11 @@ static struct virtio_pci_device *to_vp_d
 }
 
 /* virtio config->get_features() implementation */
-static u32 vp_get_features(struct virtio_device *vdev)
+static u64 vp_get_features(struct virtio_device *vdev)
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 
-	/* When someone needs more than 32 feature bits, we'll need to
-	 * steal a bit to indicate that the rest are somewhere else. */
+	/* We only support 32 feature bits. */
 	return ioread32(vp_dev->ioaddr + VIRTIO_PCI_HOST_FEATURES);
 }
 
drivers/virtio/virtio_ring.c +1 -1
@@ -685,7 +685,7 @@ void vring_transport_features(struct vir
 			break;
 		default:
 			/* We don't understand this bit. */
-			vdev->features &= ~(1 << i);
+			vdev->features &= ~(1ULL << i);
 		}
 	}
 }
include/linux/virtio.h +1 -1
@@ -66,7 +66,7 @@ struct virtio_device {
 	struct virtio_device_id id;
 	struct virtio_config_ops *config;
 	struct list_head vqs;
-	u32 features;
+	u64 features;
 	void *priv;
 };
 
include/linux/virtio_config.h +4 -4
@@ -117,7 +117,7 @@ struct virtio_config_ops {
 			vq_callback_t *callbacks[],
 			const char *names[]);
 	void (*del_vqs)(struct virtio_device *);
-	u32 (*get_features)(struct virtio_device *vdev);
+	u64 (*get_features)(struct virtio_device *vdev);
 	void (*finalize_features)(struct virtio_device *vdev);
 };
 
@@ -135,14 +135,14 @@ static inline bool virtio_has_feature(co
 {
 	/* Did you forget to fix assumptions on max features? */
 	if (__builtin_constant_p(fbit))
-		BUILD_BUG_ON(fbit >= 32);
+		BUILD_BUG_ON(fbit >= 64);
 	else
-		BUG_ON(fbit >= 32);
+		BUG_ON(fbit >= 64);
 
 	if (fbit < VIRTIO_TRANSPORT_F_START)
 		virtio_check_driver_offered_feature(vdev, fbit);
 
-	return vdev->features & (1 << fbit);
+	return vdev->features & (1ULL << fbit);
 }
 
 /**
tools/virtio/linux/virtio.h +2 -2
@@ -134,12 +134,12 @@ static inline void kfree(void *p)
 
 /* The only feature we care to support */
 #define virtio_has_feature(dev, feature) \
-	((dev)->features & (1 << (feature)))
+	((dev)->features & (1ULL << (feature)))
 /* end of stubs */
 
 struct virtio_device {
 	void *dev;
-	u32 features;
+	u64 features;
 };
 
 struct virtqueue {
#3 lore raw
In reply to Rusty Russell

Rusty, I can't find the actual patches, could you verify that they were indeed sent?

Quoted 5 lines · Rusty Russell

Here's the patch series I ended up with. I haven't coded up the QEMU side yet, so no idea if the new driver works.

Questions:
(1) Do we win from separating ISR, NOTIFY and COMMON?

By separating ISR, NOTIFY and COMMON we can place ISR and NOTIFY in PIO and COMMON in MMIO. This gives us the benefit of having the small data path use fast PIO, while big config path can use MMIO.

Quoted 3 lines

(2) I used a "u8 bar"; should I use a bir and pack it instead? BIR seems a little obscure (noone else in the kernel source seems to refer to it).

BIR is a concept from the PCI spec, but it was only used for MSI-X. I don't expect to see it all around the kernel source.

Signature · 1 line
Sasha.
#4 lore raw

RFC 3/11 pci: add pci_iomap_range

In reply to Rusty Russell

From: Michael S Tsirkin <[email protected]>

Virtio drivers should map the part of the range they need, not necessarily all of it.

---
 include/asm-generic/io.h    |    4 ++++
 include/asm-generic/iomap.h |   11 +++++++++++
 lib/iomap.c                 |   41 ++++++++++++++++++++++++++++++++++++-----
 3 files changed, 51 insertions(+), 5 deletions(-)
include/asm-generic/io.h +4 -0
index 9120887..3cf1787 100644
@@ -286,6 +286,10 @@ static inline void writesb(const void __iomem *addr, const void *buf, int len)
 /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */
 struct pci_dev;
 extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
+extern void __iomem *pci_iomap_range(struct pci_dev *dev, int bar,
+				     unsigned offset,
+				     unsigned long minlen,
+				     unsigned long maxlen);
 static inline void pci_iounmap(struct pci_dev *dev, void __iomem *p)
 {
 }
include/asm-generic/iomap.h +11 -0
index 98dcd76..6f192d4 100644
@@ -70,8 +70,19 @@ extern void ioport_unmap(void __iomem *);
 /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */
 struct pci_dev;
 extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
+extern void __iomem *pci_iomap_range(struct pci_dev *dev, int bar,
+				     unsigned offset,
+				     unsigned long minlen,
+				     unsigned long maxlen);
 extern void pci_iounmap(struct pci_dev *dev, void __iomem *);
 #else
+static inline void __iomem *pci_iomap_range(struct pci_dev *dev, int bar,
+					    unsigned offset,
+					    unsigned long minlen,
+					    unsigned long maxlen)
+{
+	return NULL;
+}
 struct pci_dev;
 static inline void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max)
 {
lib/iomap.c +36 -5

72 lines not on this page · raw message

#5 lore raw

Re: [RFC 3/11] pci: add pci_iomap_range

Quoted 7 lines · Rusty Russell

From: Michael S Tsirkin <[email protected]>

Virtio drivers should map the part of the range they need, not necessarily all of it.

I think that we should add a forcenocache flag. This will let devices put the cap structure in the prefetcheable BAR. That has an advantage that it can be located anywhere in the 2^64 space, while non-prefetcheable BARs are limited to lower 4G for devices behind a PCI-to-PCI bridge.

Quoted 123 lines
---
 include/asm-generic/io.h    |    4 ++++
 include/asm-generic/iomap.h |   11 +++++++++++
 lib/iomap.c                 |   41 ++++++++++++++++++++++++++++++++++++-----
 3 files changed, 51 insertions(+), 5 deletions(-)
include/asm-generic/io.h +4 -0
index 9120887..3cf1787 100644
@@ -286,6 +286,10 @@ static inline void writesb(const void __iomem *addr, const void *buf, int len)
 /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */
 struct pci_dev;
 extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
+extern void __iomem *pci_iomap_range(struct pci_dev *dev, int bar,
+				     unsigned offset,
+				     unsigned long minlen,
+				     unsigned long maxlen);
 static inline void pci_iounmap(struct pci_dev *dev, void __iomem *p)
 {
 }
include/asm-generic/iomap.h +11 -0
index 98dcd76..6f192d4 100644
@@ -70,8 +70,19 @@ extern void ioport_unmap(void __iomem *);
 /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */
 struct pci_dev;
 extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
+extern void __iomem *pci_iomap_range(struct pci_dev *dev, int bar,
+				     unsigned offset,
+				     unsigned long minlen,
+				     unsigned long maxlen);
 extern void pci_iounmap(struct pci_dev *dev, void __iomem *);
 #else
+static inline void __iomem *pci_iomap_range(struct pci_dev *dev, int bar,
+					    unsigned offset,
+					    unsigned long minlen,
+					    unsigned long maxlen)
+{
+	return NULL;
+}
 struct pci_dev;
 static inline void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max)
 {
lib/iomap.c +36 -5
index 5dbcb4b..93ae915 100644
@@ -243,26 +243,37 @@ EXPORT_SYMBOL(ioport_unmap);
 
 #ifdef CONFIG_PCI
 /**
- * pci_iomap - create a virtual mapping cookie for a PCI BAR
+ * pci_iomap_range - create a virtual mapping cookie for a PCI BAR
  * @dev: PCI device that owns the BAR
  * @bar: BAR number
- * @maxlen: length of the memory to map
+ * @offset: map memory at the given offset in BAR
+ * @minlen: min length of the memory to map
+ * @maxlen: max length of the memory to map
  *
  * Using this function you will get a __iomem address to your device BAR.
  * You can access it using ioread*() and iowrite*(). These functions hide
  * the details if this is a MMIO or PIO address space and will just do what
  * you expect from them in the correct way.
  *
+ * @minlen specifies the minimum length to map. We check that BAR is
+ * large enough.
  * @maxlen specifies the maximum length to map. If you want to get access to
- * the complete BAR without checking for its length first, pass %0 here.
+ * the complete BAR from offset to the end, pass %0 here.
  * */
-void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
+void __iomem *pci_iomap_range(struct pci_dev *dev, int bar,
+			      unsigned offset,
+			      unsigned long minlen,
+			      unsigned long maxlen)
 {
 	resource_size_t start = pci_resource_start(dev, bar);
 	resource_size_t len = pci_resource_len(dev, bar);
 	unsigned long flags = pci_resource_flags(dev, bar);
 
-	if (!len || !start)
+	if (len <= offset || !start)
+		return NULL;
+	len -= offset;
+	start += offset;
+	if (len < minlen)
 		return NULL;
 	if (maxlen && len > maxlen)
 		len = maxlen;
@@ -277,10 +288,30 @@ void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
 	return NULL;
 }
 
+/**
+ * pci_iomap - create a virtual mapping cookie for a PCI BAR
+ * @dev: PCI device that owns the BAR
+ * @bar: BAR number
+ * @maxlen: length of the memory to map
+ *
+ * Using this function you will get a __iomem address to your device BAR.
+ * You can access it using ioread*() and iowrite*(). These functions hide
+ * the details if this is a MMIO or PIO address space and will just do what
+ * you expect from them in the correct way.
+ *
+ * @maxlen specifies the maximum length to map. If you want to get access to
+ * the complete BAR without checking for its length first, pass %0 here.
+ * */
+void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
+{
+	return pci_iomap_range(dev, bar, 0, 0, maxlen);
+}
+
 void pci_iounmap(struct pci_dev *dev, void __iomem * addr)
 {
 	IO_COND(addr, /* nothing */, iounmap(addr));
 }
 EXPORT_SYMBOL(pci_iomap);
+EXPORT_SYMBOL(pci_iomap_range);
 EXPORT_SYMBOL(pci_iounmap);
 #endif /* CONFIG_PCI */
#6 lore raw

Re: [RFC 3/11] pci: add pci_iomap_range

Quoted 10 lines · Michael S. Tsirkin
Quoted 7 lines · Rusty Russell

From: Michael S Tsirkin <[email protected]>

Virtio drivers should map the part of the range they need, not necessarily all of it.

Quoted 6 lines

I think that we should add a forcenocache flag. This will let devices put the cap structure in the prefetcheable BAR. That has an advantage that it can be located anywhere in the 2^64 space, while non-prefetcheable BARs are limited to lower 4G for devices behind a PCI-to-PCI bridge.

OK, want to respin that patch (or patch on top and I'll fold?)

Thanks,
Rusty.

#7 lore raw

RFC PATCH v2 1/1 pci: add pci_iomap_range

Virtio drivers should map the part of the range they need, not necessarily all of it. They also need non-cacheable mapping even for prefetchable BARs.

---

Quoted 8 lines
Quoted 6 lines · Rusty Russell

I think that we should add a forcenocache flag. This will let devices put the cap structure in the prefetcheable BAR. That has an advantage that it can be located anywhere in the 2^64 space, while non-prefetcheable BARs are limited to lower 4G for devices behind a PCI-to-PCI bridge.

OK, want to respin that patch (or patch on top and I'll fold?)

Here comes (warning - untested). This is on top of master. As I'm moving this function for portability into a separate file, it might be a good idea for this to be rebased on top of my linux-next tree?

The whole mmio rework is not 3.3 material yes, is it?

 include/asm-generic/io.h    |    5 ++++
 include/asm-generic/iomap.h |   13 ++++++++++++
 lib/iomap.c                 |   46 +++++++++++++++++++++++++++++++++++++-----
 3 files changed, 58 insertions(+), 6 deletions(-)
include/asm-generic/io.h +5 -0
index 9120887..78aa159 100644
@@ -286,6 +286,11 @@ static inline void writesb(const void __iomem *addr, const void *buf, int len)
 /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */
 struct pci_dev;
 extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
+extern void __iomem *pci_iomap_range(struct pci_dev *dev, int bar,
+				     unsigned offset,
+				     unsigned long minlen,
+				     unsigned long maxlen,
+				     bool force_nocache);
 static inline void pci_iounmap(struct pci_dev *dev, void __iomem *p)
 {
 }
include/asm-generic/iomap.h +13 -0
index 98dcd76..584a2b6 100644
@@ -70,8 +70,21 @@ extern void ioport_unmap(void __iomem *);
 /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */
 struct pci_dev;
 extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
+extern void __iomem *pci_iomap_range(struct pci_dev *dev, int bar,
+				     unsigned offset,
+				     unsigned long minlen,
+				     unsigned long maxlen,
+				     bool force_nocache);
 extern void pci_iounmap(struct pci_dev *dev, void __iomem *);
 #else
+static inline void __iomem *pci_iomap_range(struct pci_dev *dev, int bar,
+					    unsigned offset,
+					    unsigned long minlen,
+					    unsigned long maxlen,
+					    bool force_nocache);
+{
+	return NULL;
+}
 struct pci_dev;
 static inline void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max)
 {
lib/iomap.c +40 -6

83 lines not on this page · raw message

Signature · 1 line
1.7.8.382.g3daff
#8 lore raw

RFC 4/11 virtio-pci: define layout for virtio vendor-specific capabilities.

In reply to Rusty Russell

Based on patch by Michael S. Tsirkin <[email protected]>, but I found it hard to follow so changed to use structures which are more self-documenting.

---
 include/linux/virtio_pci.h |   41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)
include/linux/virtio_pci.h +41 -0
@@ -92,4 +92,45 @@
 /* The alignment to use between consumer and producer parts of vring.
  * x86 pagesize again. */
 #define VIRTIO_PCI_VRING_ALIGN		4096
+
+/* IDs for different capabilities.  Must all exist. */
+/* FIXME: Do we win from separating ISR, NOTIFY and COMMON? */
+/* Common configuration */
+#define VIRTIO_PCI_CAP_COMMON_CFG	1
+/* Notifications */
+#define VIRTIO_PCI_CAP_NOTIFY_CFG	2
+/* ISR access */
+#define VIRTIO_PCI_CAP_ISR_CFG		3
+/* Device specific confiuration */
+#define VIRTIO_PCI_CAP_DEVICE_CFG	4
+
+/* This is the PCI capability header: */
+struct virtio_pci_cap {
+	u8 cap_vndr;	/* Generic PCI field: PCI_CAP_ID_VNDR */
+	u8 cap_next;	/* Generic PCI field: next ptr. */
+	u8 cfg_type;	/* One of the VIRTIO_PCI_CAP_*_CFG. */
+/* FIXME: Should we use a bir, instead of raw bar number? */
+	u8 bar;		/* Where to find it. */
+	__le32 offset;	/* Offset within bar. */
+	__le32 length;	/* Length. */
+};
+
+/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */
+struct virtio_pci_common_cfg {
+	/* About the whole device. */
+	__le32 device_feature_select;	/* read-write */
+	__le32 device_feature;		/* read-only */
+	__le32 guest_feature_select;	/* read-write */
+	__le32 guest_feature;		/* read-only */
+	__le16 msix_config;		/* read-write */
+	__u8 device_status;		/* read-write */
+	__u8 unused;
+
+	/* About a specific virtqueue. */
+	__le16 queue_select;	/* read-write */
+	__le16 queue_align;	/* read-write, power of 2. */
+	__le16 queue_size;	/* read-write, power of 2. */
+	__le16 queue_msix_vector;/* read-write */
+	__le64 queue_address;	/* read-write: 0xFFFFFFFFFFFFFFFF == DNE. */
+};
 #endif
#9 lore raw

Re: [RFC 4/11] virtio-pci: define layout for virtio vendor-specific capabilities.

Quoted 22 lines · Rusty Russell

Based on patch by Michael S. Tsirkin <[email protected]>, but I found it hard to follow so changed to use structures which are more self-documenting.

---
 include/linux/virtio_pci.h |   41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)
include/linux/virtio_pci.h +6 -0
@@ -92,4 +92,45 @@
 /* The alignment to use between consumer and producer parts of vring.
  * x86 pagesize again. */
 #define VIRTIO_PCI_VRING_ALIGN		4096
+
+/* IDs for different capabilities.  Must all exist. */
+/* FIXME: Do we win from separating ISR, NOTIFY and COMMON? */
+/* Common configuration */
+#define VIRTIO_PCI_CAP_COMMON_CFG	1
+/* Notifications */

include/linux/virtio_pci.h line 110

Quoted 10 lines
include/linux/virtio_pci.h +10 -0
+#define VIRTIO_PCI_CAP_NOTIFY_CFG	2
+/* ISR access */
+#define VIRTIO_PCI_CAP_ISR_CFG		3
+/* Device specific confiuration */
+#define VIRTIO_PCI_CAP_DEVICE_CFG	4
+
+/* This is the PCI capability header: */
+struct virtio_pci_cap {
+	u8 cap_vndr;	/* Generic PCI field: PCI_CAP_ID_VNDR */
+	u8 cap_next;	/* Generic PCI field: next ptr. */

There should be a cap_len field here, which is mandatory for PCI_CAP_ID_VNDR capabilities.

Quoted 14 lines
include/linux/virtio_pci.h +14 -0
+	u8 cfg_type;	/* One of the VIRTIO_PCI_CAP_*_CFG. */
+/* FIXME: Should we use a bir, instead of raw bar number? */
+	u8 bar;		/* Where to find it. */
+	__le32 offset;	/* Offset within bar. */
+	__le32 length;	/* Length. */
+};
+
+/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */
+struct virtio_pci_common_cfg {
+	/* About the whole device. */
+	__le32 device_feature_select;	/* read-write */
+	__le32 device_feature;		/* read-only */
+	__le32 guest_feature_select;	/* read-write */
+	__le32 guest_feature;		/* read-only */

include/linux/virtio_pci.h line 134

Quoted 10 lines
include/linux/virtio_pci.h +10 -0
+	__le16 msix_config;		/* read-write */
+	__u8 device_status;		/* read-write */
+	__u8 unused;
+
+	/* About a specific virtqueue. */
+	__le16 queue_select;	/* read-write */
+	__le16 queue_align;	/* read-write, power of 2. */
+	__le16 queue_size;	/* read-write, power of 2. */
+	__le16 queue_msix_vector;/* read-write */
+	__le64 queue_address;	/* read-write: 0xFFFFFFFFFFFFFFFF == DNE. */

This is now a 64bit address, but we can't do an atomic iowrite64(). We should make it clear to the device when it should initialize the vq.

Signature · 1 line
Sasha.
#10 lore raw

RFC 6/11 virtio_pci: move old defines to legacy, introduce new structure.

In reply to Rusty Russell

We don't *remove* the old ones, unless VIRTIO_PCI_NO_LEGACY is defined, but they get a friendly #warning about the change.

Note that config option is not promted; we always enable it for now.

---
 drivers/virtio/Kconfig             |   12 +++++++
 drivers/virtio/Makefile            |    2 -
 drivers/virtio/virtio_pci_legacy.c |   16 ++++-----
 include/linux/virtio_pci.h         |   63 ++++++++++++++++++++++++-------------
 4 files changed, 62 insertions(+), 31 deletions(-)
drivers/virtio/Kconfig +12 -0
@@ -25,6 +25,18 @@ config VIRTIO_PCI
 
 	  If unsure, say M.
 
+config VIRTIO_PCI_LEGACY
+	bool
+	default y
+	depends on VIRTIO_PCI
+	---help---
+	  The old BAR0 virtio pci layout was deprecated early 2012.
+
+	  So look out into your driveway.  Do you have a flying car?  If
+	  so, you can happily disable this option and virtio will not
+	  break.  Otherwise, leave it set.  Unless you're testing what
+	  life will be like in The Future.
+
 config VIRTIO_BALLOON
 	tristate "Virtio balloon driver (EXPERIMENTAL)"
 	select VIRTIO
drivers/virtio/Makefile +1 -1
@@ -1,5 +1,5 @@
 obj-$(CONFIG_VIRTIO) += virtio.o
 obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
 obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
-obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
+obj-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
 obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
drivers/virtio/virtio_pci_legacy.c renamed from drivers/virtio/virtio_pci.c +8 -8
rename from drivers/virtio/virtio_pci.c
rename to drivers/virtio/virtio_pci_legacy.c
@@ -1,5 +1,5 @@
 /*
- * Virtio PCI driver
+ * Virtio PCI driver (legacy mode)
  *
  * This module allows virtio devices to be used over a virtual PCI device.
  * This can be used with QEMU based VMMs like KVM or Xen.
@@ -27,7 +27,7 @@
 #include <linux/spinlock.h>
 
 MODULE_AUTHOR("Anthony Liguori <[email protected]>");
-MODULE_DESCRIPTION("virtio-pci");
+MODULE_DESCRIPTION("virtio-pci-legacy");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("1");
 
@@ -629,7 +629,7 @@ static int __devinit virtio_pci_probe(st
 		return -ENODEV;
 
 	if (pci_dev->revision != VIRTIO_PCI_ABI_VERSION) {
-		printk(KERN_ERR "virtio_pci: expected ABI version %d, got %d\n",
+		printk(KERN_ERR "virtio_pci_legacy: expected ABI version %d, got %d\n",
 		       VIRTIO_PCI_ABI_VERSION, pci_dev->revision);
 		return -ENODEV;
 	}
@@ -654,7 +654,7 @@ static int __devinit virtio_pci_probe(st
 	if (err)
 		goto out;
 
-	err = pci_request_regions(pci_dev, "virtio-pci");
+	err = pci_request_regions(pci_dev, "virtio-pci-legacy");
 	if (err)
 		goto out_enable_device;
 
@@ -721,8 +721,8 @@ static int virtio_pci_resume(struct pci_
 }
 #endif
 
-static struct pci_driver virtio_pci_driver = {
-	.name		= "virtio-pci",
+static struct pci_driver virtio_pci_driver_legacy = {
+	.name		= "virtio-pci-legacy",
 	.id_table	= virtio_pci_id_table,
 	.probe		= virtio_pci_probe,
 	.remove		= __devexit_p(virtio_pci_remove),
@@ -734,14 +734,14 @@ static struct pci_driver virtio_pci_driv
 
 static int __init virtio_pci_init(void)
 {
-	return pci_register_driver(&virtio_pci_driver);
+	return pci_register_driver(&virtio_pci_driver_legacy);
 }
 
 module_init(virtio_pci_init);
 
 static void __exit virtio_pci_exit(void)
 {
-	pci_unregister_driver(&virtio_pci_driver);
+	pci_unregister_driver(&virtio_pci_driver_legacy);
 }
 
 module_exit(virtio_pci_exit);
include/linux/virtio_pci.h +41 -22

97 lines not on this page · raw message

#11 lore raw

RFC 6/11 virtio_pci: don't use the legacy driver if we find the new PCI capabilities.

In reply to Rusty Russell

With module option to override. I assume I can call pci_find_capability() before pci_request_regions?

---
 drivers/virtio/virtio_pci_legacy.c |   20 +++++++++++++++++++-
 include/linux/virtio_pci.h         |   19 +++++++++++++++++++
 2 files changed, 38 insertions(+), 1 deletion(-)
drivers/virtio/virtio_pci_legacy.c +20 -1
@@ -26,6 +26,10 @@
 #include <linux/highmem.h>
 #include <linux/spinlock.h>
 
+static bool force_nonlegacy;
+module_param(force_nonlegacy, bool, 0644);
+MODULE_PARM_DESC(force_nonlegacy, "Take over non-legacy virtio devices too");
+
 MODULE_AUTHOR("Anthony Liguori <[email protected]>");
 MODULE_DESCRIPTION("virtio-pci-legacy");
 MODULE_LICENSE("GPL");
@@ -622,7 +626,7 @@ static int __devinit virtio_pci_probe(st
 				      const struct pci_device_id *id)
 {
 	struct virtio_pci_device *vp_dev;
-	int err;
+	int err, cap;
 
 	/* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */
 	if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f)
@@ -654,6 +658,21 @@ static int __devinit virtio_pci_probe(st
 	if (err)
 		goto out;
 
+	/* We leave modern virtio-pci for the modern driver. */
+	cap = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_COMMON_CFG);
+	if (cap) {
+		if (force_nonlegacy)
+			dev_info(&pci_dev->dev,
+				 "virtio_pci_legacy: forcing legacy mode!\n");
+		else {
+			dev_info(&pci_dev->dev,
+				 "virtio_pci_legacy: leaving to"
+				 " non-legacy driver\n");
+			err = -ENODEV;
+			goto out_enable_device;
+		}
+	}
+
 	err = pci_request_regions(pci_dev, "virtio-pci-legacy");
 	if (err)
 		goto out_enable_device;
include/linux/virtio_pci.h +19 -0
@@ -152,4 +152,23 @@ struct virtio_pci_common_cfg {
 	__le16 queue_msix_vector;/* read-write */
 	__le64 queue_address;	/* read-write: 0xFFFFFFFFFFFFFFFF == DNE. */
 };
+
+#ifdef __KERNEL__
+/* Returns offset of the capability, or 0. */
+static inline int virtio_pci_find_capability(struct pci_dev *dev, u8 cfg_type)
+{
+	int pos;
+
+	for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR);
+	     pos > 0;
+	     pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) {
+		u8 type;
+		pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap,
+							 cfg_type), &type);
+		if (type == cfg_type)
+			return pos;
+	}
+	return 0;
+}
+#endif /* __KERNEL__ */
 #endif
#12 lore raw

Re: [RFC 6/11] virtio_pci: don't use the legacy driver if we find the new PCI capabilities.

Quoted 2 lines · Rusty Russell

With module option to override. I assume I can call pci_find_capability() before pci_request_regions?

I don't think thats safe.

Signature · 1 line
Sasha.
#13 lore raw

Re: [RFC 6/11] virtio_pci: don't use the legacy driver if we find the new PCI capabilities.

Quoted 5 lines · Sasha Levin
Quoted 2 lines · Rusty Russell

With module option to override. I assume I can call pci_find_capability() before pci_request_regions?

I don't think thats safe.

Actually, a number of drivers do it first thing in probe() (eg. drivers/char/agp/intel-agp.c).

So I think it's easiest right at the start. I've changed that.

Thanks,
Rusty.

#14 lore raw

Re: [RFC 6/11] virtio_pci: don't use the legacy driver if we find the new PCI capabilities.

In reply to Sasha Levin
Quoted 5 lines · Sasha Levin
Quoted 2 lines · Rusty Russell

With module option to override. I assume I can call pci_find_capability() before pci_request_regions?

I don't think thats safe.

Why wouldn't it be? pci_find_capability is only
using config cycles, it never needs any regions.

Quoted 3 lines
Signature · 1 line
Sasha.
#15 lore raw

RFC 7/11 virtio_pci: new, capability-aware driver.

In reply to Rusty Russell

Differences:
1) Uses 4 pci capabilities to demark common, irq, notify and dev-specific areas.
2) Guest sets queue size, using host-provided maximum.
3) Guest sets queue alignment, rather than ABI-defined 4096.
4) More than 32 feature bits (a lot more!).

---
 drivers/virtio/Makefile     |    1
 drivers/virtio/virtio_pci.c |  868 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 869 insertions(+)
drivers/virtio/Makefile +1 -0
@@ -1,5 +1,6 @@
 obj-$(CONFIG_VIRTIO) += virtio.o
 obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
 obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
+obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
 obj-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
 obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
drivers/virtio/virtio_pci.c new file +869 -0

869 lines not on this page · raw message

#16 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

drivers/virtio/virtio_pci.c line 117

Quoted 7 lines · Rusty Russell
drivers/virtio/virtio_pci.c new file +7 -0
+/* There is no iowrite64.  We use two 32-bit ops. */
+static void iowrite64(u64 val, const __le64 *addr)
+{
+	iowrite32((u32)val, (__le32 *)addr);
+	iowrite32(val >> 32, (__le32 *)addr + 1);
+}
+

Let's put addr_lo/addr_hi in the structure then, to make the fact this field is not atomic explicit?

#17 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 11 lines · Michael S. Tsirkin

drivers/virtio/virtio_pci.c line 117

Quoted 7 lines · Rusty Russell
drivers/virtio/virtio_pci.c new file +7 -0
+/* There is no iowrite64.  We use two 32-bit ops. */
+static void iowrite64(u64 val, const __le64 *addr)
+{
+	iowrite32((u32)val, (__le32 *)addr);
+	iowrite32(val >> 32, (__le32 *)addr + 1);
+}
+

Let's put addr_lo/addr_hi in the structure then, to make the fact this field is not atomic explicit?

Good point, assuming I haven't missed something.

Are 64-bit accesses actually unknown in PCI-land? Or is this a limited availability thing?

Thanks,
Rusty.

#18 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 13 lines · Rusty Russell
Quoted 11 lines · Michael S. Tsirkin

drivers/virtio/virtio_pci.c line 117

Quoted 7 lines · Rusty Russell
drivers/virtio/virtio_pci.c new file +7 -0
+/* There is no iowrite64.  We use two 32-bit ops. */
+static void iowrite64(u64 val, const __le64 *addr)
+{
+	iowrite32((u32)val, (__le32 *)addr);
+	iowrite32(val >> 32, (__le32 *)addr + 1);
+}
+

Let's put addr_lo/addr_hi in the structure then, to make the fact this field is not atomic explicit?

Quoted 7 lines

Good point, assuming I haven't missed something.

Are 64-bit accesses actually unknown in PCI-land? Or is this a limited availability thing?

Thanks,
Rusty.

I think PCI can optionally support atomic 64 bit accesses, but not all architectures can generate them.

Signature · 1 line
MST
#19 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 16 lines · Michael S. Tsirkin
Quoted 15 lines · Rusty Russell
Quoted 11 lines · Michael S. Tsirkin

drivers/virtio/virtio_pci.c line 117

Quoted 7 lines · Rusty Russell
drivers/virtio/virtio_pci.c new file +7 -0
+/* There is no iowrite64.  We use two 32-bit ops. */
+static void iowrite64(u64 val, const __le64 *addr)
+{
+	iowrite32((u32)val, (__le32 *)addr);
+	iowrite32(val>>  32, (__le32 *)addr + 1);
+}
+

Let's put addr_lo/addr_hi in the structure then, to make the fact this field is not atomic explicit?

Good point, assuming I haven't missed something.

Quoted 9 lines
Quoted 5 lines

Are 64-bit accesses actually unknown in PCI-land? Or is this a limited availability thing?

Thanks,
Rusty.

I think PCI can optionally support atomic 64 bit accesses, but not all architectures can generate them.

yes. PCI(e) support atomic 64-bit ops; it's dependent on CPU & chipset interface to PCI that determines ability to generate a 64-bit length xaction.

#20 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 20 lines · Don Dutile
Quoted 19 lines · Michael S. Tsirkin
Quoted 18 lines · Rusty Russell
Quoted 11 lines · Michael S. Tsirkin

drivers/virtio/virtio_pci.c line 117

Quoted 7 lines · Rusty Russell
drivers/virtio/virtio_pci.c new file +7 -0
+/* There is no iowrite64.  We use two 32-bit ops. */
+static void iowrite64(u64 val, const __le64 *addr)
+{
+	iowrite32((u32)val, (__le32 *)addr);
+	iowrite32(val>>  32, (__le32 *)addr + 1);
+}
+

Let's put addr_lo/addr_hi in the structure then, to make the fact this field is not atomic explicit?

Good point, assuming I haven't missed something.

Are 64-bit accesses actually unknown in PCI-land? Or is this a limited availability thing?

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

Thanks,
Rusty.

I think PCI can optionally support atomic 64 bit accesses, but not all architectures can generate them.

yes. PCI(e) support atomic 64-bit ops; it's dependent on CPU & chipset interface to PCI that determines ability to generate a 64-bit length xaction.

Does this mean it's possible to detect inside Linux? I'd like to use it if we can, but if everyone is really going to do two 32 bit writes, then we should just define it that way.

Thanks,
Rusty.

#21 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Michael S. Tsirkin
Quoted 16 lines · Michael S. Tsirkin
Quoted 15 lines · Rusty Russell
Quoted 11 lines · Michael S. Tsirkin

drivers/virtio/virtio_pci.c line 117

Quoted 7 lines · Rusty Russell
drivers/virtio/virtio_pci.c new file +7 -0
+/* There is no iowrite64.  We use two 32-bit ops. */
+static void iowrite64(u64 val, const __le64 *addr)
+{
+	iowrite32((u32)val, (__le32 *)addr);
+	iowrite32(val >> 32, (__le32 *)addr + 1);
+}
+

Let's put addr_lo/addr_hi in the structure then, to make the fact this field is not atomic explicit?

Good point, assuming I haven't missed something.

Quoted 8 lines
Quoted 5 lines

Are 64-bit accesses actually unknown in PCI-land? Or is this a limited availability thing?

Thanks,
Rusty.

I think PCI can optionally support atomic 64 bit accesses, but not all architectures can generate them.

Ping. Going to change this in the layout struct?

Quoted 2 lines
Signature · 1 line
MST
#22 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 20 lines · Michael S. Tsirkin
Quoted 19 lines · Michael S. Tsirkin
Quoted 18 lines · Rusty Russell
Quoted 11 lines · Michael S. Tsirkin

drivers/virtio/virtio_pci.c line 117

Quoted 7 lines · Rusty Russell
drivers/virtio/virtio_pci.c new file +7 -0
+/* There is no iowrite64.  We use two 32-bit ops. */
+static void iowrite64(u64 val, const __le64 *addr)
+{
+	iowrite32((u32)val, (__le32 *)addr);
+	iowrite32(val >> 32, (__le32 *)addr + 1);
+}
+

Let's put addr_lo/addr_hi in the structure then, to make the fact this field is not atomic explicit?

Good point, assuming I haven't missed something.

Are 64-bit accesses actually unknown in PCI-land? Or is this a limited availability thing?

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

Thanks,
Rusty.

I think PCI can optionally support atomic 64 bit accesses, but not all architectures can generate them.

Ping. Going to change this in the layout struct?

Not sure what you mean.... We use a queue_enable field, so it doesn't matter if the accesses aren't atomic for that.

Cheers,
Rusty.

#23 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 28 lines · Rusty Russell
Quoted 26 lines · Michael S. Tsirkin
Quoted 24 lines · Michael S. Tsirkin
Quoted 13 lines · Rusty Russell
Quoted 11 lines · Michael S. Tsirkin

drivers/virtio/virtio_pci.c line 117

Quoted 7 lines · Rusty Russell
drivers/virtio/virtio_pci.c new file +7 -0
+/* There is no iowrite64.  We use two 32-bit ops. */
+static void iowrite64(u64 val, const __le64 *addr)
+{
+	iowrite32((u32)val, (__le32 *)addr);
+	iowrite32(val >> 32, (__le32 *)addr + 1);
+}
+

Let's put addr_lo/addr_hi in the structure then, to make the fact this field is not atomic explicit?

Quoted 7 lines

Good point, assuming I haven't missed something.

Are 64-bit accesses actually unknown in PCI-land? Or is this a limited availability thing?

Thanks,
Rusty.

I think PCI can optionally support atomic 64 bit accesses, but not all architectures can generate them.

Quoted 7 lines
Quoted 1 line

Ping. Going to change this in the layout struct?

Not sure what you mean.... We use a queue_enable field, so it doesn't matter if the accesses aren't atomic for that.

Cheers,
Rusty.

I mean the struct should have separate _lo and _hi fields.

Otherwise I have to do:

+ case offsetof(struct virtio_pci_common_cfg, queue_desc):
+ assert(size == 4);
+ return virtio_queue_get_desc_addr(vdev, vdev->queue_sel) & low;
+ case offsetof(struct virtio_pci_common_cfg, queue_desc) + 4:
+ assert(size == 4);
+ return virtio_queue_get_desc_addr(vdev, vdev->queue_sel) >> 32;

Would be nicer as:

+ case offsetof(struct virtio_pci_common_cfg, queue_desc_lo):
+ assert(size == sizeof cfg.queue_desc_lo);
+ return virtio_queue_get_desc_addr(vdev, vdev->queue_sel) & low;
+ case offsetof(struct virtio_pci_common_cfg, queue_desc_hi):
+ assert(size == sizeof cfg.queue_desc_hi);
+ return virtio_queue_get_desc_addr(vdev, vdev->queue_sel) >> 32;

Agree?

Signature · 1 line
MST
#24 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 57 lines · Michael S. Tsirkin
Quoted 28 lines · Rusty Russell
Quoted 26 lines · Michael S. Tsirkin
Quoted 24 lines · Michael S. Tsirkin
Quoted 13 lines · Rusty Russell
Quoted 11 lines · Michael S. Tsirkin

Review

Quoted 7 lines · Rusty Russell
(quoted diff) +7 -0
+/* There is no iowrite64.  We use two 32-bit ops. */
+static void iowrite64(u64 val, const __le64 *addr)
+{
+	iowrite32((u32)val, (__le32 *)addr);
+	iowrite32(val >> 32, (__le32 *)addr + 1);
+}
+

Let's put addr_lo/addr_hi in the structure then, to make the fact this field is not atomic explicit?

Quoted 7 lines

Good point, assuming I haven't missed something.

Are 64-bit accesses actually unknown in PCI-land? Or is this a limited availability thing?

Thanks,
Rusty.

I think PCI can optionally support atomic 64 bit accesses, but not all architectures can generate them.

Quoted 7 lines
Quoted 1 line

Ping. Going to change this in the layout struct?

Not sure what you mean.... We use a queue_enable field, so it doesn't matter if the accesses aren't atomic for that.

Cheers,
Rusty.

I mean the struct should have separate _lo and _hi fields.

Otherwise I have to do:

(quoted diff) +6 -0
+    case offsetof(struct virtio_pci_common_cfg, queue_desc):
+	 assert(size == 4);
+        return virtio_queue_get_desc_addr(vdev, vdev->queue_sel) & low;
+    case offsetof(struct virtio_pci_common_cfg, queue_desc) + 4:
+	 assert(size == 4);
+        return virtio_queue_get_desc_addr(vdev, vdev->queue_sel) >> 32;

Would be nicer as:

(quoted diff) +6 -0
+    case offsetof(struct virtio_pci_common_cfg, queue_desc_lo):
+	 assert(size == sizeof cfg.queue_desc_lo);
+        return virtio_queue_get_desc_addr(vdev, vdev->queue_sel) & low;
+    case offsetof(struct virtio_pci_common_cfg, queue_desc_hi):
+	 assert(size ==  sizeof cfg.queue_desc_hi);
+        return virtio_queue_get_desc_addr(vdev, vdev->queue_sel) >> 32;
Quoted 1 line

Agree?

Indeed. Sorry for being obtuse!

Split into _lo and _hi fields:

Subject: virtio_pci: split u64 fields into u32.

MST points out that since we write them as two u32s, and the backend has to handle that, let's not fool ourselves and just make it two u32s.

drivers/virtio/virtio_pci_modern.c +25 -23
index 8b35c2e..b5bb348 100644
@@ -37,17 +37,16 @@ static DEFINE_PCI_DEVICE_TABLE(virtio_pci_id_table) = {
 
 MODULE_DEVICE_TABLE(pci, virtio_pci_id_table);
 
-/* There is no general iowrite64.  We use two 32-bit ops. */
-static void iowrite64_twopart(u64 val, const __le64 *addr)
+static void iowrite64_twopart(u64 val, __le32 *lo, __le32 *hi)
 {
-	iowrite32((u32)val, (__le32 *)addr);
-	iowrite32(val >> 32, (__le32 *)addr + 1);
+	iowrite32((u32)val, lo);
+	iowrite32(val >> 32, hi);
 }
 
 /* There is no ioread64.  We use two 32-bit ops. */
-static u64 ioread64_twopart(__le64 *addr)
+static u64 ioread64_twopart(__le32 *lo, __le32 *hi)
 {
-	return ioread32(addr) | ((u64)ioread32((__le32 *)addr + 1) << 32);
+	return ioread32(lo) | ((u64)ioread32(hi) << 32);
 }
 
 /* config->{get,set}_status() implementations */
@@ -145,14 +144,16 @@ static u64 vp_get64(struct virtio_device *vdev, unsigned offset)
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 
-	return ioread64_twopart(vp_dev->device + offset);
+	return ioread64_twopart(vp_dev->device + offset,
+				vp_dev->device + offset + 4);
 }
 
 static void vp_set64(struct virtio_device *vdev, unsigned offset, u64 val)
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 
-	iowrite64_twopart(val, vp_dev->device + offset);
+	iowrite64_twopart(val, vp_dev->device + offset,
+			  vp_dev->device + offset + 4);
 }
 
 static void vp_reset(struct virtio_device *vdev)
@@ -198,17 +199,18 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
 {
 	struct virtio_pci_vq_info *info;
 	struct virtqueue *vq;
+	struct virtio_pci_common_cfg __iomem *cfg = vp_dev->common;
 	u16 num, off;
 	int err;
 
-	if (index >= ioread16(&vp_dev->common->num_queues))
+	if (index >= ioread16(&cfg->num_queues))
 		return ERR_PTR(-ENOENT);
 
 	/* Select the queue we're interested in */
-	iowrite16(index, &vp_dev->common->queue_select);
+	iowrite16(index, &cfg->queue_select);
 
 	/* Sanity check */
-	switch (ioread64_twopart(&vp_dev->common->queue_desc)) {
+	switch (ioread64_twopart(&cfg->queue_desc_lo, &cfg->queue_desc_hi)) {
 		/* Uninitialized.  Excellent. */
 		break;
 	default:
@@ -217,7 +219,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
 	}
 
 	/* Maximum size must be a power of 2. */
-	num = ioread16(&vp_dev->common->queue_size);
+	num = ioread16(&cfg->queue_size);
 	if (num & (num - 1)) {
 		dev_warn(&vp_dev->pci_dev->dev, "bad queue size %u", num);
 		return ERR_PTR(-EINVAL);
@@ -233,7 +235,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
 	info->desired_num = num;
 
 	/* get offset of notification word for this vq (shouldn't wrap) */
-	off = ioread16(&vp_dev->common->queue_notify_off);
+	off = ioread16(&cfg->queue_notify_off);
 	if ((u64)off * vp_dev->notify_offset_multiplier + 2
 	    > vp_dev->notify_len) {
 		dev_warn(&vp_dev->pci_dev->dev,
@@ -264,8 +266,8 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
 	info->vq = vq;
 
 	if (msix_vec != VIRTIO_MSI_NO_VECTOR) {
-		iowrite16(msix_vec, &vp_dev->common->queue_msix_vector);
-		msix_vec = ioread16(&vp_dev->common->queue_msix_vector);
+		iowrite16(msix_vec, &cfg->queue_msix_vector);
+		msix_vec = ioread16(&cfg->queue_msix_vector);
 		if (msix_vec == VIRTIO_MSI_NO_VECTOR) {
 			err = -EBUSY;
 			goto out_new_virtqueue;
@@ -282,14 +284,14 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
 	}
 
 	/* Activate the queue. */
-	iowrite16(num, &vp_dev->common->queue_size);
+	iowrite16(num, &cfg->queue_size);
 	iowrite64_twopart(virt_to_phys(vq->vring.desc),
-			  &vp_dev->common->queue_desc);
+			  &cfg->queue_desc_lo, &cfg->queue_desc_hi);
 	iowrite64_twopart(virt_to_phys(vq->vring.avail),
-			  &vp_dev->common->queue_avail);
+			  &cfg->queue_avail_lo, &cfg->queue_avail_hi);
 	iowrite64_twopart(virt_to_phys(vq->vring.used),
-			  &vp_dev->common->queue_used);
-	iowrite8(1, &vp_dev->common->queue_enable);
+			  &cfg->queue_used_lo, &cfg->queue_used_hi);
+	iowrite8(1, &cfg->queue_enable);
 
 	return vq;
 
@@ -352,9 +354,9 @@ static void del_vq(struct virtqueue *vq)
 
 	/* This is for our own benefit, not the device's! */
 	iowrite16(info->desired_num, &vp_dev->common->queue_size);
-	iowrite64_twopart(0, &vp_dev->common->queue_desc);
-	iowrite64_twopart(0, &vp_dev->common->queue_avail);
-	iowrite64_twopart(0, &vp_dev->common->queue_used);
+	iowrite64_twopart(0, &vp_dev->common->queue_desc_lo, &vp_dev->common->queue_desc_hi);
+	iowrite64_twopart(0, &vp_dev->common->queue_avail_lo, &vp_dev->common->queue_avail_hi);
+	iowrite64_twopart(0, &vp_dev->common->queue_used_lo, &vp_dev->common->queue_used_hi);
 
 	free_pages_exact(info->queue, size);
 	kfree(info);
include/uapi/linux/virtio_pci.h +6 -3
index 591eb0e5..a23226b 100644
@@ -159,8 +159,11 @@ struct virtio_pci_common_cfg {
 	__le16 queue_msix_vector;	/* read-write */
 	__le16 queue_enable;		/* read-write */
 	__le16 queue_notify_off;	/* read-only */
-	__le64 queue_desc;		/* read-write */
-	__le64 queue_avail;		/* read-write */
-	__le64 queue_used;		/* read-write */
+	__le32 queue_desc_lo;		/* read-write */
+	__le32 queue_desc_hi;		/* read-write */
+	__le32 queue_avail_lo;		/* read-write */
+	__le32 queue_avail_hi;		/* read-write */
+	__le32 queue_used_lo;		/* read-write */
+	__le32 queue_used_hi;		/* read-write */
 };
 #endif /* _UAPI_LINUX_VIRTIO_PCI_H */
#25 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Rusty Russell
Quoted 13 lines · Rusty Russell
Quoted 11 lines · Michael S. Tsirkin

drivers/virtio/virtio_pci.c line 117

Quoted 7 lines · Rusty Russell
drivers/virtio/virtio_pci.c new file +7 -0
+/* There is no iowrite64.  We use two 32-bit ops. */
+static void iowrite64(u64 val, const __le64 *addr)
+{
+	iowrite32((u32)val, (__le32 *)addr);
+	iowrite32(val >> 32, (__le32 *)addr + 1);
+}
+

Let's put addr_lo/addr_hi in the structure then, to make the fact this field is not atomic explicit?

Quoted 7 lines

Good point, assuming I haven't missed something.

Are 64-bit accesses actually unknown in PCI-land? Or is this a limited availability thing?

Thanks,
Rusty.

By the way, a generic question on virtio-pci: we now have:

/* virtio config->get() implementation */
static void vp_get(struct virtio_device *vdev, unsigned offset,
void *buf, unsigned len)

{
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        void __iomem *ioaddr = vp_dev->ioaddr +
                                VIRTIO_PCI_CONFIG(vp_dev) + offset;
        u8 *ptr = buf;
        int i;

        for (i = 0; i < len; i++)
                ptr[i] = ioread8(ioaddr + i);
}

This means that if configuration is read while it is changed, we might get an inconsistent state, with parts of a 64 bit field coming from old and parts from new value.

Isn't this a problem?

Signature · 1 line
MST
#26 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 16 lines · Michael S. Tsirkin

By the way, a generic question on virtio-pci: we now have:

/* virtio config->get() implementation */
static void vp_get(struct virtio_device *vdev, unsigned offset,
void *buf, unsigned len)

{
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        void __iomem *ioaddr = vp_dev->ioaddr +
                                VIRTIO_PCI_CONFIG(vp_dev) + offset;
        u8 *ptr = buf;
        int i;

        for (i = 0; i < len; i++)
                ptr[i] = ioread8(ioaddr + i);
}
Quoted 6 lines

This means that if configuration is read while it is changed, we might get an inconsistent state, with parts of a 64 bit field coming from old and parts from new value.

Isn't this a problem?

I don't think so; it's the caller's problem if they need to do locking. Is there a caller which needs this?

Or am I missing something?
Rusty.

#27 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 22 lines · Rusty Russell
Quoted 21 lines · Michael S. Tsirkin

By the way, a generic question on virtio-pci: we now have:

/* virtio config->get() implementation */
static void vp_get(struct virtio_device *vdev, unsigned offset,
void *buf, unsigned len)

{
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        void __iomem *ioaddr = vp_dev->ioaddr +
                                VIRTIO_PCI_CONFIG(vp_dev) + offset;
        u8 *ptr = buf;
        int i;

        for (i = 0; i < len; i++)
                ptr[i] = ioread8(ioaddr + i);
}

This means that if configuration is read while it is changed, we might get an inconsistent state, with parts of a 64 bit field coming from old and parts from new value.

Quoted 7 lines
Quoted 1 line

Isn't this a problem?

I don't think so; it's the caller's problem if they need to do locking. Is there a caller which needs this?

Or am I missing something?
Rusty.

I mean like this in block:

        /* Host must always specify the capacity. */
        vdev->config->get(vdev, offsetof(struct virtio_blk_config,
capacity),
                          &capacity, sizeof(capacity));

        /* If capacity is too big, truncate with warning. */
        if ((sector_t)capacity != capacity) {
                dev_warn(&vdev->dev, "Capacity %llu too large:
truncating\n",
                         (unsigned long long)capacity);
                capacity = (sector_t)-1;
        }

Now let's assume capacity field is changed from 0x8000 to 0x10000 on host. Is it possible that we read two upper bytes before the change so we see 0x0000....
and 2 lower bytes after the change
so we see 0x....0000 and resulting capacity appears
to be 0?

If no why not?

And what kind of locking can help?

Signature · 1 line
MST
#28 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 19 lines · Michael S. Tsirkin

On Tue, Dec 13, 2011 at 12:51:20PM +1030, Rusty Russell wrote:
I mean like this in block:

        /* Host must always specify the capacity. */
        vdev->config->get(vdev, offsetof(struct virtio_blk_config,
capacity),
                          &capacity, sizeof(capacity));

        /* If capacity is too big, truncate with warning. */
        if ((sector_t)capacity != capacity) {
                dev_warn(&vdev->dev, "Capacity %llu too large:
truncating\n",
                         (unsigned long long)capacity);
                capacity = (sector_t)-1;
        }
Quoted 8 lines

Now let's assume capacity field is changed from 0x8000 to 0x10000 on host. Is it possible that we read two upper bytes before the change so we see 0x0000....
and 2 lower bytes after the change
so we see 0x....0000 and resulting capacity appears
to be 0?

If no why not?

Same issue in reverse with the guest setting the MAC address in virtio_net, if the host were reading it. And virtio_balloon? We have ignored it, so far.

Perhaps a new feature VIRTIO_F_UNSTABLE? Which (unlike other features) appears and vanishes around config writes by either side? Kind of a hack though...

Cheers,
Rusty.

#29 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 33 lines · Rusty Russell
Quoted 19 lines · Michael S. Tsirkin

On Tue, Dec 13, 2011 at 12:51:20PM +1030, Rusty Russell wrote:
I mean like this in block:

        /* Host must always specify the capacity. */
        vdev->config->get(vdev, offsetof(struct virtio_blk_config,
capacity),
                          &capacity, sizeof(capacity));

        /* If capacity is too big, truncate with warning. */
        if ((sector_t)capacity != capacity) {
                dev_warn(&vdev->dev, "Capacity %llu too large:
truncating\n",
                         (unsigned long long)capacity);
                capacity = (sector_t)-1;
        }
Quoted 8 lines

Now let's assume capacity field is changed from 0x8000 to 0x10000 on host. Is it possible that we read two upper bytes before the change so we see 0x0000....
and 2 lower bytes after the change
so we see 0x....0000 and resulting capacity appears
to be 0?

If no why not?

Same issue in reverse with the guest setting the MAC address in virtio_net, if the host were reading it. And virtio_balloon? We have ignored it, so far.

Quoted 6 lines

Perhaps a new feature VIRTIO_F_UNSTABLE? Which (unlike other features) appears and vanishes around config writes by either side? Kind of a hack though...

Cheers,
Rusty.

Not sure how this can work in such a setup: when would guest check this bit to avoid races?
A separate registers also seems nicer than a flag.

Some other possible design choices:
- a flag to signal config accesses in progress by guest host would need to buffer changes and apply them in one go when flag is cleared
- a register to make host get/set config in guest memory
- use a control vq for all devices

The last two involve defining a structure specifying offset and length of device configuration affected.

The last option is preferable if other transports besides pci might have the same issue: we solve
it once and for all this way.

Signature · 1 line
MST
#30 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 9 lines · Michael S. Tsirkin
Quoted 3 lines · Rusty Russell

Perhaps a new feature VIRTIO_F_UNSTABLE? Which (unlike other features) appears and vanishes around config writes by either side? Kind of a hack though...

Not sure how this can work in such a setup: when would guest check this bit to avoid races?
A separate registers also seems nicer than a flag.

Quoted 6 lines

Some other possible design choices:
- a flag to signal config accesses in progress by guest host would need to buffer changes and apply them in one go when flag is cleared
- a register to make host get/set config in guest memory
- use a control vq for all devices

- seqlock-style generation count register(s)? Has the advantage of being a noop if things never change.

- continue to ignore it ;)

And yes, it's a more general problem than virtio_pci...

Cheers,
Rusty.

#31 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 17 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 3 lines · Rusty Russell

Perhaps a new feature VIRTIO_F_UNSTABLE? Which (unlike other features) appears and vanishes around config writes by either side? Kind of a hack though...

Not sure how this can work in such a setup: when would guest check this bit to avoid races?
A separate registers also seems nicer than a flag.

Quoted 6 lines

Some other possible design choices:
- a flag to signal config accesses in progress by guest host would need to buffer changes and apply them in one go when flag is cleared
- a register to make host get/set config in guest memory
- use a control vq for all devices

Quoted 3 lines

- seqlock-style generation count register(s)?
Has the advantage of
being a noop if things never change.

The counter can be in guest memory, right? So we don't pay extra exits.

Quoted 1 line

- continue to ignore it ;)

Since you decided on a config layout redesign it seems a good time to fix architectural problems ...

Quoted 4 lines

And yes, it's a more general problem than virtio_pci...

Cheers,
Rusty.

Signature · 1 line
MST
#32 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 18 lines · Michael S. Tsirkin
Quoted 17 lines · Rusty Russell
Quoted 15 lines · Michael S. Tsirkin
Quoted 3 lines · Rusty Russell

Perhaps a new feature VIRTIO_F_UNSTABLE? Which (unlike other features) appears and vanishes around config writes by either side? Kind of a hack though...

Not sure how this can work in such a setup: when would guest check this bit to avoid races?
A separate registers also seems nicer than a flag.

Some other possible design choices:
- a flag to signal config accesses in progress by guest host would need to buffer changes and apply them in one go when flag is cleared
- a register to make host get/set config in guest memory
- use a control vq for all devices

Quoted 3 lines
Quoted 3 lines

- seqlock-style generation count register(s)?
Has the advantage of
being a noop if things never change.

Actually, the host doesn't need anything, since it can always lock out the guest while it updates the area.

It's the guest which can't do atomic updates.

Quoted 2 lines

The counter can be in guest memory, right? So we don't pay extra exits.

Could be, but I'm not delighted about the design. What does the host do if the guest screws things up? How long do you wait for them to complete the seqlock? Or does it save the old version for use in the duration?

And we don't have any atomic guest write problems that I know of. We can solve it in future (by specifying a config queue).

Quoted 4 lines
Quoted 1 line

- continue to ignore it ;)

Since you decided on a config layout redesign it seems a good time to fix architectural problems ...

Yes, indeed.

Cheers,
Rusty.

#33 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 19 lines · Rusty Russell
Quoted 18 lines · Michael S. Tsirkin
Quoted 17 lines · Rusty Russell
Quoted 15 lines · Michael S. Tsirkin
Quoted 3 lines · Rusty Russell

Perhaps a new feature VIRTIO_F_UNSTABLE? Which (unlike other features) appears and vanishes around config writes by either side? Kind of a hack though...

Not sure how this can work in such a setup: when would guest check this bit to avoid races?
A separate registers also seems nicer than a flag.

Some other possible design choices:
- a flag to signal config accesses in progress by guest host would need to buffer changes and apply them in one go when flag is cleared
- a register to make host get/set config in guest memory
- use a control vq for all devices

Quoted 7 lines
Quoted 3 lines
Quoted 3 lines

- seqlock-style generation count register(s)?
Has the advantage of
being a noop if things never change.

Actually, the host doesn't need anything, since it can always lock out the guest while it updates the area.
It's the guest which can't do atomic updates.

There are 2 cases
- updates by guest accesses by host
- accesses by guest updates by host

Both are problematic because the guest accesses are split. Consider the example I gave at the beginning was with capacity read by guest. Host can not solve it without guest changes, right?

Quoted 4 lines
Quoted 2 lines

The counter can be in guest memory, right? So we don't pay extra exits.

Could be, but I'm not delighted about the design.

OK, so this is an argument for always using a control vq, right?

Quoted 4 lines

What does the host do
if the guest screws things up? How long do you wait for them to complete the seqlock? Or does it save the old version for use in the duration?

Yes, it will have to only apply the change when seqlock is dropped.

Quoted 2 lines

And we don't have any atomic guest write problems that I know of. We can solve it in future (by specifying a config queue).

Don't have == not reported as observed in the field? It seems clear from code that we do have a race, correct?

Quoted 9 lines
Quoted 4 lines
Quoted 1 line

- continue to ignore it ;)

Since you decided on a config layout redesign it seems a good time to fix architectural problems ...

Yes, indeed.

Cheers,
Rusty.

#34 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines · Rusty Russell

On Mon, 19 Dec 2011 11:13:26 +0200, "Michael S. Tsirkin" <[email protected]> wrote:

Actually, the host doesn't need anything, since it can always lock out the guest while it updates the area.
It's the guest which can't do atomic updates.

Quoted 7 lines

There are 2 cases
- updates by guest accesses by host
- accesses by guest updates by host

Both are problematic because the guest accesses are split. Consider the example I gave at the beginning was with capacity read by guest. Host can not solve it without guest changes, right?

Indeed, my brain fart. Let's pretend I didn't say that, and you didn't have to explain it to me in baby words :)

Quoted 6 lines
Quoted 4 lines
Quoted 2 lines

The counter can be in guest memory, right? So we don't pay extra exits.

Could be, but I'm not delighted about the design.

OK, so this is an argument for always using a control vq, right?

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Quoted 6 lines
Quoted 4 lines

What does the host do
if the guest screws things up? How long do you wait for them to complete the seqlock? Or does it save the old version for use in the duration?

Yes, it will have to only apply the change when seqlock is dropped.

If the seqlock is in normal memory, how does it get notified? It would have to poll. That's annoying, since you don't know when to give up and declare the device terminally broken.

Quoted 2 lines

Don't have == not reported as observed in the field? It seems clear from code that we do have a race, correct?

Yes, and yes.

Cheers,
Rusty.

#35 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 10 lines · Rusty Russell
Quoted 6 lines
Quoted 4 lines

What does the host do
if the guest screws things up? How long do you wait for them to complete the seqlock? Or does it save the old version for use in the duration?

Yes, it will have to only apply the change when seqlock is dropped.

If the seqlock is in normal memory, how does it get notified? It would have to poll. That's annoying, since you don't know when to give up and declare the device terminally broken.

OK, so you think all devices need a config vq then?
It actually has other benefits:
- devices don't need a design choice between configuration and vq
- minimum number of MSI vectors per device will be 1, not 2
- reduced PCI memory usage
- configuration updates become more lightweight

Signature · 1 line
MST
#36 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Rusty Russell
Quoted 24 lines · Rusty Russell
Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines · Rusty Russell

On Mon, 19 Dec 2011 11:13:26 +0200, "Michael S. Tsirkin" <[email protected]> wrote:

Actually, the host doesn't need anything, since it can always lock out the guest while it updates the area.
It's the guest which can't do atomic updates.

Quoted 7 lines

There are 2 cases
- updates by guest accesses by host
- accesses by guest updates by host

Both are problematic because the guest accesses are split. Consider the example I gave at the beginning was with capacity read by guest. Host can not solve it without guest changes, right?

Indeed, my brain fart. Let's pretend I didn't say that, and you didn't have to explain it to me in baby words :)

Quoted 5 lines
Quoted 4 lines
Quoted 2 lines

The counter can be in guest memory, right? So we don't pay extra exits.

Could be, but I'm not delighted about the design.

Quoted 8 lines
Quoted 1 line

OK, so this is an argument for always using a control vq, right?

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Okay - want me to propose an interface for that?

#37 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 9 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Okay - want me to propose an interface for that?

Had a brief chat with BenH (CC'd).

I think we should deprecate writing to the config space. Only balloon does it AFAICT, and I can't quite figure out *why* it has an 'active' field. This solves half the problem, of sync guest writes. For the other half, I suggest a generation counter; odd means inconsistent. The guest can poll.

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines.

We should also change the ring (to a single ring, I think). Descriptors to 24 bytes long (8 byte cookie, 8 byte addr, 4 byte len, 4 byte flags). We might be able to squeeze it into 20 bytes but that means packing. We should support inline, chained or indirect. Let the other side ack by setting flag, cookie and len (if written).

Moreover, I think we should make all these changes at once (at least, in the spec). That makes it a big change, and it'll take longer to develop, but makes it easy in the long run to differentiate legacy and modern virtio.

Thoughts?
Rusty.

#38 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 6 lines · Rusty Russell
Quoted 5 lines · Michael S. Tsirkin
Quoted 4 lines · Rusty Russell

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

Quoted 6 lines
Quoted 4 lines
Quoted 2 lines

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Okay - want me to propose an interface for that?

Had a brief chat with BenH (CC'd).

Thanks Rusty :-)

Quoted 5 lines

I think we should deprecate writing to the config space. Only balloon does it AFAICT, and I can't quite figure out *why* it has an 'active' field. This solves half the problem, of sync guest writes. For the other half, I suggest a generation counter; odd means inconsistent. The guest can poll.

Agreed. Commands are the way to go.

Quoted 3 lines

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines.

I have a few more "good" reasons but yes, basically it confuses things. Just see the bugs we are still fixing in qemu :-)

A fixed endian makes everything simpler, there is no need to play games when using virtio in heterogenous environment (slave CPUs), no need to handle variable endian on CPUs that support both etc... and the cost of byteswap is negligible (we do it routinely for normal PCI hardware and it's never been a hot spot).

Quoted 2 lines

We should also change the ring (to a single ring, I think). Descriptors to 24 bytes long (8 byte cookie, 8 byte addr, 4 byte len, 4 byte flags).

Do we care about space waste ? 32 bytes is a nicely round power of two and would make general math, but also debugging (visual inspection of the ring) etc... easier.

If we support immediate data, that means we'd have enough for most common commands and status straight in the ring.

Another approach that may or may not be worthwhile is to have the size of the ring elements configurable (as power of two) so that it can be adjusted to contain both immediate and a descriptor. This is done typically in network hardware to put the routing headers in the immediate part and the rest of the packet elsewhere which improves cache affinity when processing large amount of packets.

Quoted 8 lines

We might be able to squeeze it into 20 bytes but that means packing. We should support inline, chained or indirect. Let the other side ack by setting flag, cookie and len (if written).

Moreover, I think we should make all these changes at once (at least, in the spec). That makes it a big change, and it'll take longer to develop, but makes it easy in the long run to differentiate legacy and modern virtio.

Agreed,

Cheers,
Ben.

#39 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Rusty Russell
Quoted 29 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell

Yes.  The idea that we can alter fields in the device-specific config area is flawed.  There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Okay - want me to propose an interface for that?

Had a brief chat with BenH (CC'd).

I think we should deprecate writing to the config space.  Only balloon does it AFAICT, and I can't quite figure out *why* it has an 'active' field.  This solves half the problem, of sync guest writes.  For the other half, I suggest a generation counter; odd means inconsistent.  The guest can poll.

BenH also convinced me we should finally make the config space LE if we're going to change things.  Since PCI is the most common transport, guest-endian confuses people.  And it sucks for really weird machines.

We should also change the ring (to a single ring, I think).  Descriptors to 24 bytes long (8 byte cookie, 8 byte addr, 4 byte len, 4 byte flags). We might be able to squeeze it into 20 bytes but that means packing.  We should support inline, chained or indirect.  Let the other side ack by setting flag, cookie and len (if written).

Quoted 4 lines

Moreover, I think we should make all these changes at once (at least, in the spec).  That makes it a big change, and it'll take longer to develop, but makes it easy in the long run to differentiate legacy and modern virtio.

This is also an opportunity to stop using CPU physical addresses in the ring and instead perform DMA like a normal PCI device (use bus addresses).

Stefan

#40 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 4 lines · Stefan Hajnoczi

This is also an opportunity to stop using CPU physical addresses in the ring and instead perform DMA like a normal PCI device (use bus addresses).

Euh why ?

That would mean in many cases adding a layer of iommu, which will slow things down a lot ... unless we create a special virtio bus which has its own dma-ops that do a direct v:p translation that is. But anything PCI doing dma_map_sg and co will involved emulated iommu mapping & unmapping and will be a huge hit on performances.

Cheers,
Ben.

#41 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 7 lines · Benjamin Herrenschmidt
Quoted 4 lines · Stefan Hajnoczi

This is also an opportunity to stop using CPU physical addresses in the ring and instead perform DMA like a normal PCI device (use bus addresses).

Euh why ?

Because it's a paravirt hack that ends up hitting corner cases. It's not possible to do virtio-pci passthrough under nested virtualization unless we use an IOMMU. Imagine passing virtio-net from L0 into the L2 guest (i.e. PCI-passthrough). If virtio-pci is really "PCI" this should be possible but it's not when we use physical addresses instead of bus addresses.

Stefan

#42 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 10 lines · Stefan Hajnoczi
Quoted 7 lines · Benjamin Herrenschmidt
Quoted 4 lines · Stefan Hajnoczi

This is also an opportunity to stop using CPU physical addresses in the ring and instead perform DMA like a normal PCI device (use bus addresses).

Euh why ?

Quoted 8 lines

Because it's a paravirt hack that ends up hitting corner cases. It's not possible to do virtio-pci passthrough under nested virtualization unless we use an IOMMU. Imagine passing virtio-net from L0 into the L2 guest (i.e. PCI-passthrough). If virtio-pci is really "PCI" this should be possible but it's not when we use physical addresses instead of bus addresses.

Stefan

It won't be hard to show siginificant performance regression if we do this. Hard to justify for something as niche as nested virt.

Signature · 1 line
MST
#43 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 18 lines · Michael S. Tsirkin
Quoted 17 lines · Stefan Hajnoczi
Quoted 7 lines · Benjamin Herrenschmidt
Quoted 4 lines · Stefan Hajnoczi

This is also an opportunity to stop using CPU physical addresses in the ring and instead perform DMA like a normal PCI device (use bus addresses).

Euh why ?

Because it's a paravirt hack that ends up hitting corner cases.  It's not possible to do virtio-pci passthrough under nested virtualization unless we use an IOMMU.  Imagine passing virtio-net from L0 into the L2 guest (i.e. PCI-passthrough).  If virtio-pci is really "PCI" this should be possible but it's not when we use physical addresses instead of bus addresses.

Quoted 4 lines
Quoted 1 line

Stefan

It won't be hard to show siginificant performance regression if we do this. Hard to justify for something as niche as nested virt.

For x86 this should be mostly a nop. For ppc and SPARC architectures maybe you're right. I still think it's a design flaw because if virtio v2 doesn't use bus addresses then it will simply not be possible to do passthrough for nested virt and other cases we haven't hit yet.

Stefan

#44 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 19 lines · Stefan Hajnoczi
Quoted 18 lines · Michael S. Tsirkin
Quoted 17 lines · Stefan Hajnoczi
Quoted 7 lines · Benjamin Herrenschmidt
Quoted 4 lines · Stefan Hajnoczi

This is also an opportunity to stop using CPU physical addresses in the ring and instead perform DMA like a normal PCI device (use bus addresses).

Euh why ?

Because it's a paravirt hack that ends up hitting corner cases.  It's not possible to do virtio-pci passthrough under nested virtualization unless we use an IOMMU.  Imagine passing virtio-net from L0 into the L2 guest (i.e. PCI-passthrough).  If virtio-pci is really "PCI" this should be possible but it's not when we use physical addresses instead of bus addresses.

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

Stefan

It won't be hard to show siginificant performance regression if we do this. Hard to justify for something as niche as nested virt.

For x86 this should be mostly a nop.

Maybe it should, but AFAIK it isn't.

Quoted 6 lines

For ppc and SPARC architectures maybe you're right. I still think it's a design flaw because if virtio v2 doesn't use bus addresses then it will simply not be possible to do passthrough for nested virt and other cases we haven't hit yet.

Stefan

virtio-pci does not implement things like SRIOV or FLR so it won't work anyway.

If we ever fix this, and if we really want to pass through a virtio device (why?) using an emulated iommu seems silly - we probably want a PV IOMMU as well.

Signature · 1 line
MST
#45 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Stefan Hajnoczi
Quoted 4 lines · Stefan Hajnoczi
Quoted 2 lines

It won't be hard to show siginificant performance regression if we do this. Hard to justify for something as niche as nested virt.

For x86 this should be mostly a nop.

No it won't be. Or rather, it will be as long as you map your entire guests in the iommu, which means your entire guest will have to be pinned and you lost swap, ksm, yadadada...

Since the only way to have non-pinned guests today on x86 is to use virtio, you just shot yourself in the foot.

Eventually x86 will have to grow some kind of virtualized iommu or paravir iommu to overcome that at which point you will pay that price.

Quoted 5 lines

For ppc and SPARC architectures
maybe you're right. I still think it's a design flaw because if virtio v2 doesn't use bus addresses then it will simply not be possible to do passthrough for nested virt and other cases we haven't hit yet.

Then don't use virtio in those cases, use real emulated HW. Seriously, is nested virt that interesting ?

At the very least, make that use of iommu a feature or something like that so it can be negociated down when not doing nesting which is going to be 99% of your use cases.

Cheers,
Ben.

#46 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Stefan Hajnoczi
Quoted 8 lines · Stefan Hajnoczi
Quoted 6 lines · Benjamin Herrenschmidt
Quoted 4 lines · Stefan Hajnoczi

This is also an opportunity to stop using CPU physical addresses in the ring and instead perform DMA like a normal PCI device (use bus addresses).

Quoted 8 lines
Quoted 1 line

Euh why ?

Because it's a paravirt hack that ends up hitting corner cases. It's not possible to do virtio-pci passthrough under nested virtualization unless we use an IOMMU. Imagine passing virtio-net from L0 into the L2 guest (i.e. PCI-passthrough). If virtio-pci is really "PCI" this should be possible but it's not when we use physical addresses instead of bus addresses.

Is this just an academic exercise or is there any actual value in doing this ?

Using an iommu is going to slaugher your performance, so at the very least it should be kept an option.

Yes, it's a paravirt "hack" as you call it but that's what virtio is all about.... paravirt. If you prefer you can emulate a real HW device :-)

Ben.

#47 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 18 lines · Benjamin Herrenschmidt
Quoted 8 lines · Stefan Hajnoczi
Quoted 6 lines · Benjamin Herrenschmidt
Quoted 4 lines · Stefan Hajnoczi

This is also an opportunity to stop using CPU physical addresses in the ring and instead perform DMA like a normal PCI device (use bus addresses).

Quoted 8 lines
Quoted 1 line

Euh why ?

Because it's a paravirt hack that ends up hitting corner cases.  It's not possible to do virtio-pci passthrough under nested virtualization unless we use an IOMMU.  Imagine passing virtio-net from L0 into the L2 guest (i.e. PCI-passthrough).  If virtio-pci is really "PCI" this should be possible but it's not when we use physical addresses instead of bus addresses.

Quoted 2 lines

Is this just an academic exercise or is there any actual value in doing this ?

It's a corner case, the value is small. I also hit this with virtio on SPARC which is made difficult by the fact that the Solaris kernel assumes there is an IOMMU for scatter-gather and doesn't provide functions for allocating physically contiguous memory in drivers. It's another instance where this shortcut comes up against problems and behaving like a real PCI device would work fine.

Quoted 2 lines

Using an iommu is going to slaugher your performance, so at the very least it should be kept an option.

That's a good idea. By default it can continue to use physical addresses.

I guess there's no point in worrying about it until we have a real user.

Stefan

#48 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Rusty Russell
Quoted 9 lines · Rusty Russell
Quoted 8 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Quoted 7 lines
Quoted 1 line

Okay - want me to propose an interface for that?

Had a brief chat with BenH (CC'd).

I think we should deprecate writing to the config space. Only balloon does it AFAICT, and I can't quite figure out *why* it has an 'active' field.

Are you sure? I think net writes a mac address.

Quoted 3 lines

This solves half the problem, of sync guest writes. For the other half, I suggest a generation counter; odd means inconsistent. The guest can poll.

So we get the counter until it's even, get the config, if it's changed repeat? Yes it works. However, I would like to have a way to detect config change just by looking at memory. ATM we need to read ISR to know. If we used a VQ, the advantage would be that the device can work with a single MSIX vector shared by all VQs.

If we do require config VQ anyway, why not use it to notify guest of config changes? Guest could pre-post an in buffer and host uses that.

Quoted 3 lines

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines.

Are we going to keep guest endian for e.g. virtio net header? If yes the benefit of switching config space is not that big. And changes in devices would affect non-PCI transports.

Quoted 5 lines

We should also change the ring (to a single ring, I think). Descriptors to 24 bytes long (8 byte cookie, 8 byte addr, 4 byte len, 4 byte flags). We might be able to squeeze it into 20 bytes but that means packing. We should support inline, chained or indirect. Let the other side ack by setting flag, cookie and len (if written).

Quite possibly all or some of these things help performance but do we have to change the spec before we have experimental proof?

I did experiment with a single ring using tools/virtio and
I didn't see a measureable performance gain. Two rings
do have the advantage of not requiring host side copy,
which copy would surely add to cache pressure. Since
host doesn't change desriptors, we could also
preformat some descriptors in the current design.
There is a fragmentation problem in theory but it can be alleviated with a smart allocator. About inline - it can only help very small buffers. Which workloads do you have in mind exactly?

Quoted 7 lines

Moreover, I think we should make all these changes at once (at least, in the spec). That makes it a big change, and it'll take longer to develop, but makes it easy in the long run to differentiate legacy and modern virtio.

Thoughts?
Rusty.

BTW this seems to be the reverse from what you have in Mar 2001, see [email protected] :)

I am much less concerned with what we do for configuration, but I do not believe we have learned all performance lessons from virtio ring1. Is there any reason why we shouldn't be able to experiment with inline within virtio1 and see whether that gets us anything?
If we do a bunch of changes to the ring at once, we can't figure out what's right, what's wrong, or back out of mistakes later.

Since there are non PCI transports that use the ring, we really shouldn't make both the configuration and the ring changes depend on the same feature bit.

Signature · 1 line
MST
#49 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 8 lines · Michael S. Tsirkin
Quoted 4 lines

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines.

Are we going to keep guest endian for e.g. virtio net header? If yes the benefit of switching config space is not that big. And changes in devices would affect non-PCI transports.

I think the concept of "guest endian" is broken by design. What does that mean when running for example an ARM or a ppc 440 "guest" which could be either endian ? Since you can't hard code your guest endian how do you obtain/negociate it ? Also you now have to deal with dual endian in the host, makes everything trickier.

Just make everything LE.

Quoted 3 lines

Quite possibly all or some of these things help performance but do we have to change the spec before we have experimental proof?

Well, I would argue that the network driver world has proven countless times that those are good ideas :-) But by all mean, let's do a prototype implementation with virtio-net for example and bench it.

I don't think you need a single ring. For multiqueue net, you definitely want multiple rings and you do want rings to remain uni-directional.

One other thing that can be useful is to separate the completion ring from the actual ring of DMA descriptors, making the former completely read-only by the guest and the later completely read only by the host.

For example take the ehea ethernet rx model. It has 3 rx "rings" per queue. One contains the completions, it's a toggle-valid model so we never write back to clear valid, it contains infos from the parser, the tokenID of the packet and the index as to where in which ring the data is, wich is either inline in the completion ring (small packet), header inline & data in a data ring or completely in a data ring. Then you have two data rings which are simply rings of SG list entries (more or less).

We typically pre-populate the data rings with skb's for 1500 and 9000 bytes packets. Small packets come in immediately in the completion ring, and large packets via the data ring.

That's just -an- example. There's many other to take inspiration from. Network folks have beaten to death the problem of ring efficiency vs. CPU caches.

Quoted 10 lines
Quoted 7 lines

Moreover, I think we should make all these changes at once (at least, in the spec). That makes it a big change, and it'll take longer to develop, but makes it easy in the long run to differentiate legacy and modern virtio.

Thoughts?
Rusty.

BTW this seems to be the reverse from what you have in Mar 2001, see [email protected] :)

That was 10 years ago...

Quoted 3 lines

I am much less concerned with what we do for configuration, but I do not believe we have learned all performance lessons from virtio ring1.

Maybe we have learned some more since then ? :-)

Quoted 10 lines

Is there any reason why we shouldn't be
able to experiment with inline within virtio1 and see whether that gets us anything?
If we do a bunch of changes to the ring at once, we can't figure out what's right, what's wrong, or back out of mistakes later.

Since there are non PCI transports that use the ring, we really shouldn't make both the configuration and the ring changes depend on the same feature bit.

Another advantage of inline data is that it makes things a lot easier for cases where only small amount of data need to be exchanged, such as control/status rings, maybe virtio-tty (which I'm working on), etc...

Cheers,
Ben.

#50 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 10 lines · Benjamin Herrenschmidt
Quoted 8 lines · Michael S. Tsirkin
Quoted 4 lines

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines.

Are we going to keep guest endian for e.g. virtio net header? If yes the benefit of switching config space is not that big. And changes in devices would affect non-PCI transports.

Quoted 7 lines

I think the concept of "guest endian" is broken by design. What does that mean when running for example an ARM or a ppc 440 "guest" which could be either endian ? Since you can't hard code your guest endian how do you obtain/negociate it ? Also you now have to deal with dual endian in the host, makes everything trickier.

Just make everything LE.

Yea. But it's not a pure transport issue, just fixing configuration won't be enough. E.g. we have structures like virtio net header.

Quoted 6 lines
Quoted 3 lines

Quite possibly all or some of these things help performance but do we have to change the spec before we have experimental proof?

Well, I would argue that the network driver world has proven countless times that those are good ideas :-)

Below you seem to suggest that separate rings like virtio has now is better than a single ring like Rusty suggested.

Quoted 9 lines

But by all mean, let's do a
prototype implementation with virtio-net for example and bench it.

I don't think you need a single ring. For multiqueue net, you definitely want multiple rings and you do want rings to remain uni-directional.

One other thing that can be useful is to separate the completion ring from the actual ring of DMA descriptors, making the former completely read-only by the guest and the later completely read only by the host.

Are you familiar with current virtio ring structure? How is this different?

Quoted 11 lines

For example take the ehea ethernet rx model. It has 3 rx "rings" per queue. One contains the completions, it's a toggle-valid model so we never write back to clear valid, it contains infos from the parser, the tokenID of the packet and the index as to where in which ring the data is, wich is either inline in the completion ring (small packet), header inline & data in a data ring or completely in a data ring. Then you have two data rings which are simply rings of SG list entries (more or less).

We typically pre-populate the data rings with skb's for 1500 and 9000 bytes packets. Small packets come in immediately in the completion ring, and large packets via the data ring.

Won't real workloads suffer from packet reordering?

Quoted 9 lines

That's just -an- example. There's many other to take inspiration from. Network folks have beaten to death the problem of ring efficiency vs. CPU caches.

Quoted 5 lines
Quoted 5 lines

Moreover, I think we should make all these changes at once (at least, in the spec). That makes it a big change, and it'll take longer to develop, but makes it easy in the long run to differentiate legacy and modern virtio.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

Thoughts?
Rusty.

BTW this seems to be the reverse from what you have in Mar 2001, see [email protected] :)

That was 10 years ago...

Sorry, typo. It was Mar 2010 :)

Quoted 5 lines
Quoted 3 lines

I am much less concerned with what we do for configuration, but I do not believe we have learned all performance lessons from virtio ring1.

Maybe we have learned some more since then ? :-)

There was 1 change in ring layout.

Quoted 11 lines
Quoted 10 lines

Is there any reason why we shouldn't be
able to experiment with inline within virtio1 and see whether that gets us anything?
If we do a bunch of changes to the ring at once, we can't figure out what's right, what's wrong, or back out of mistakes later.

Since there are non PCI transports that use the ring, we really shouldn't make both the configuration and the ring changes depend on the same feature bit.

Quoted 6 lines

Another advantage of inline data is that it makes things a lot easier for cases where only small amount of data need to be exchanged, such as control/status rings, maybe virtio-tty (which I'm working on), etc...

Cheers,
Ben.

Is that getting you a lot of speedup? Note you want to add more code on data path for everyone. Why can't you have a fixed buffer in memory and just point to that?

Signature · 1 line
MST
#51 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 5 lines · Michael S. Tsirkin
Quoted 3 lines

We typically pre-populate the data rings with skb's for 1500 and 9000 bytes packets. Small packets come in immediately in the completion ring, and large packets via the data ring.

Won't real workloads suffer from packet reordering?

No, they aren't re-ordered. The completion ring has an entry for each packet, in order. Those entries eventually reference the entry index in the data rings if the data was put there instead of being immediate.

Cheers,
Ben.

#52 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Michael S. Tsirkin
Quoted 6 lines · Michael S. Tsirkin
Quoted 2 lines

Well, I would argue that the network driver world has proven countless times that those are good ideas :-)

Below you seem to suggest that separate rings like virtio has now is better than a single ring like Rusty suggested.

I was merely pointing at examples. My understanding (but I may have misparsed him) is that Rusty wants to collate the descriptors and the "available" ring. I personally don't think the completions (the used ring) should be merged, it should remain separate. If Rusty was hinting at doing that then I disagree with him :-)

Quoted 2 lines

Are you familiar with current virtio ring structure? How is this different?

Vaguely yes :-) It does have this uni-directional model, I didn't express myself very well or I got confused by the statements about a single ring. Yes, I think we should keep the ring at least split into two directions as it is today.

But we can certainly merge available ring and descriptors ring.

Another option is having the ring entry size be a configurable power of two. This is trivial and comes at pretty much no cost. That would allow virtio-net for example to do efficient things like putting the headers as immediate data in the ring and the data as indirect.

This means a large part of the packet processing can happen without touching additional cache lines.

Anyway, at this stage, I plan to sit down with Rusty next week and hash out some kind of proposal, which we can experiment with & benchmark.

I'd suggest doing a simple user space app that creates such a ring, forks, and produce / consume. We can then run that through perf and analyze the cache behaviour, maximum throughput, etc....

Cheers,
Ben.

#53 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 6 lines · Benjamin Herrenschmidt

I'd suggest doing a simple user space app that creates such a ring, forks, and produce / consume. We can then run that through perf and analyze the cache behaviour, maximum throughput, etc....

Cheers,
Ben.

Sure. You can also use my tools/virtio hack: this rebuilds virtio ring code in userspace, has the advantage of reusing actual kernel code, so it's up to date, has same barriers, etc.

Signature · 1 line
MST
#54 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 11 lines · Michael S. Tsirkin
Quoted 6 lines · Benjamin Herrenschmidt

I'd suggest doing a simple user space app that creates such a ring, forks, and produce / consume. We can then run that through perf and analyze the cache behaviour, maximum throughput, etc....

Cheers,
Ben.

Sure. You can also use my tools/virtio hack: this rebuilds virtio ring code in userspace, has the advantage of reusing actual kernel code, so it's up to date, has same barriers, etc.

Yes, Rusty mentioned it today :-)

We'll play around in the next couple of weeks. With LCA, I'm not sure how much I'll get done next week but heh...

I'm curious to see if we can still have a one-size-fit-all between unordered blk requests with large data/descriptor access ratio and ordered network buffers with a much smaller one.

Cheers,
Ben.

#55 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 18 lines · Benjamin Herrenschmidt
Quoted 11 lines · Michael S. Tsirkin
Quoted 6 lines · Benjamin Herrenschmidt

I'd suggest doing a simple user space app that creates such a ring, forks, and produce / consume. We can then run that through perf and analyze the cache behaviour, maximum throughput, etc....

Cheers,
Ben.

Sure. You can also use my tools/virtio hack: this rebuilds virtio ring code in userspace, has the advantage of reusing actual kernel code, so it's up to date, has same barriers, etc.

Yes, Rusty mentioned it today :-)

We'll play around in the next couple of weeks. With LCA, I'm not sure how much I'll get done next week but heh...

Quoted 7 lines

I'm curious to see if we can still have a one-size-fit-all between unordered blk requests with large data/descriptor access ratio and ordered network buffers with a much smaller one.

Cheers,
Ben.

network buffers aren't ordered when the backend is zero copy. Another idea is to add different sized bufs in the RX ring, and have the device use the size that fits best. This can improve small packet memory utilization without need for data copies, but it also means that descriptors are completed out of order in -net.

#56 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Michael S. Tsirkin
Quoted 12 lines · Michael S. Tsirkin
Quoted 11 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Okay - want me to propose an interface for that?

Quoted 7 lines
Quoted 5 lines

Had a brief chat with BenH (CC'd).

I think we should deprecate writing to the config space. Only balloon does it AFAICT, and I can't quite figure out *why* it has an 'active' field.

Are you sure? I think net writes a mac address.

True. We'll need to disable that, and come up with another mechanism if we want it back (a new feature and a VIRTIO_NET_HDR_F_SET_MAC flag in the virtio_net header? Or would that mess up vhost_net?).

Quoted 9 lines
Quoted 3 lines

This solves half the problem, of sync guest writes. For the other half, I suggest a generation counter; odd means inconsistent. The guest can poll.

So we get the counter until it's even, get the config, if it's changed repeat? Yes it works. However, I would like to have a way to detect config change just by looking at memory. ATM we need to read ISR to know. If we used a VQ, the advantage would be that the device can work with a single MSIX vector shared by all VQs.

If we use a 32-bit counter, we also get this though, right?

If counter has changed, it's a config interrupt...

Quoted 3 lines

If we do require config VQ anyway, why not use it to notify guest of config changes? Guest could pre-post an in buffer and host uses that.

We could, but it's an additional burden on each device. vqs are cheap, but not free. And the config area is so damn convenient...

Quoted 7 lines
Quoted 3 lines

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines.

Are we going to keep guest endian for e.g. virtio net header? If yes the benefit of switching config space is not that big. And changes in devices would affect non-PCI transports.

Yep. It would only make sense if we do it for everything. And yes, it'll mess up everyone who is BE, so it needs to be a feature bit for them.

Quoted 9 lines
Quoted 5 lines

We should also change the ring (to a single ring, I think). Descriptors to 24 bytes long (8 byte cookie, 8 byte addr, 4 byte len, 4 byte flags). We might be able to squeeze it into 20 bytes but that means packing. We should support inline, chained or indirect. Let the other side ack by setting flag, cookie and len (if written).

Quite possibly all or some of these things help performance but do we have to change the spec before we have experimental proof?

We change the spec last, once we know what we're doing, ideally.

Quoted 2 lines

I did experiment with a single ring using tools/virtio and I didn't see a measureable performance gain.

Interesting. It is simpler and more standard than our current design, but that's not sufficient unless there are other reasons. Needs further discussion and testing.

Quoted 2 lines

Two rings do have the advantage of not requiring host side copy, which copy would surely add to cache pressure.

Well, a simple host could process in-order and leave stuff in the ring I guess. A smarter host would copy and queue, maybe leave one queue entry in so it doesn't get flooded?

Quoted 6 lines

Since
host doesn't change desriptors, we could also
preformat some descriptors in the current design.

There is a fragmentation problem in theory but it can be alleviated with a smart allocator.

Yeah, the complexity scares me...

Quoted 2 lines

About inline - it can only help very small buffers. Which workloads do you have in mind exactly?

It was suggested by others, but I think TCP Acks are the classic one. 12 + 14 + 20 + 40 = 86 bytes with virtio_net_hdr_mrg_rxbuf at the front.

Quoted 2 lines

BTW this seems to be the reverse from what you have in Mar 2001, see [email protected] :)

(s/2001/2011). Indeed. Noone shared my optimism that having an open process for a virtio2 would bring more players on board (my original motivation). But technical requirements are mounting up, which means we're going to get there anyway.

Quoted 5 lines

I am much less concerned with what we do for configuration, but I do not believe we have learned all performance lessons from virtio ring1. Is there any reason why we shouldn't be able to experiment with inline within virtio1 and see whether that gets us anything?

Inline in the used ring is possible, but those descriptors 8 bytes, vs 24/32.

Quoted 7 lines

If we do a bunch of changes to the ring at once, we can't figure out what's right, what's wrong, or back out of mistakes later.

Since there are non PCI transports that use the ring, we really shouldn't make both the configuration and the ring changes depend on the same feature bit.

Yes, I'm thinking #define VIRTIO_F_VIRTIO2 (-1). For PCI, this gets mapped into a "are we using the new config layout?". For others, it gets mapped into a transport-specific feature.

(I'm sure you get it, but for the others) This is because I want to be draw a clear line between all the legacy stuff at the same time, not have to support part of it later because someone might not flip the feature bit.

Cheers,
Rusty.

#57 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 7 lines · Rusty Russell
Quoted 3 lines

Are we going to keep guest endian for e.g. virtio net header? If yes the benefit of switching config space is not that big. And changes in devices would affect non-PCI transports.

Yep. It would only make sense if we do it for everything. And yes, it'll mess up everyone who is BE, so it needs to be a feature bit for them.

One thing we should do (I might give it a go after LCA) is start providing sized accessors for it and start converting the guest drivers in Linux at least to use those.

That way, the accessors could then do the byteswap in the future transparently if they see the feature bit for the "fixed endian".

We'd have accessors for 8,16,32 and 64-bit quantities, and accessors for "raw" blobs (already endian neutral such as MAC addresses).

Quoted 3 lines

Interesting. It is simpler and more standard than our current design, but that's not sufficient unless there are other reasons. Needs further discussion and testing.

I think completions shall remain separate. As for having a separate "available" vs. "descriptors", well, I can find pro and cons.

As it is today, it's more complex than it should be and it would make things simpler to just have an available ring that contains descriptors like mostly everything else does.

It would also be slightly more cache friendly (cache misses are a significant part of the performance issues for things like high speed networking).

However I can see at least one advantage of what you've done :-) You never have to deal with holes in the ring.

For example, a typical network driver should always try to allocate a new skb before it "consumes" one, because otherwise, there's a chance that it fails to allocate it, leaving a hole in the ring. Many drivers do it wrong with consequences going all the way to leaving stale DMA pointers in there....

With your scheme, that problem doesn't exist, and you can batch the refill which might be more efficient under some circumstances.

But is that worth the gain and the cost in cache line accesses ? Probably not.

Quoted 6 lines
Quoted 2 lines

Two rings do have the advantage of not requiring host side copy, which copy would surely add to cache pressure.

Well, a simple host could process in-order and leave stuff in the ring I guess. A smarter host would copy and queue, maybe leave one queue entry in so it doesn't get flooded?

What's wrong with a ring of descriptors + a ring of completion, with a single toggle valid bit to indicate whether a given descriptor is valid or not (to avoid the nasty ping pong on the ring head/tails).

Quoted 4 lines
Quoted 2 lines

About inline - it can only help very small buffers. Which workloads do you have in mind exactly?

It was suggested by others, but I think TCP Acks are the classic one.

Split headers + data too, tho that means supporting immediate + indirect.

It makes a lot of sense for command rings as well if we're going to go down that route.

Quoted 23 lines

12 + 14 + 20 + 40 = 86 bytes with virtio_net_hdr_mrg_rxbuf at the front.

Quoted 2 lines

BTW this seems to be the reverse from what you have in Mar 2001, see [email protected] :)

(s/2001/2011). Indeed. Noone shared my optimism that having an open process for a virtio2 would bring more players on board (my original motivation). But technical requirements are mounting up, which means we're going to get there anyway.

Quoted 5 lines

I am much less concerned with what we do for configuration, but I do not believe we have learned all performance lessons from virtio ring1. Is there any reason why we shouldn't be able to experiment with inline within virtio1 and see whether that gets us anything?

Inline in the used ring is possible, but those descriptors 8 bytes, vs 24/32.

Quoted 4 lines

If we do a bunch of changes to the ring at once, we can't figure out what's right, what's wrong, or back out of mistakes later.

Quoted 7 lines
Quoted 3 lines

Since there are non PCI transports that use the ring, we really shouldn't make both the configuration and the ring changes depend on the same feature bit.

Yes, I'm thinking #define VIRTIO_F_VIRTIO2 (-1). For PCI, this gets mapped into a "are we using the new config layout?". For others, it gets mapped into a transport-specific feature.

Or we can use the PCI ProgIf to indicate a different programming interface, that way we also use that as an excuse to say that the first BAR can either be PIO or MMIO :-)

Quoted 4 lines

(I'm sure you get it, but for the others) This is because I want to be draw a clear line between all the legacy stuff at the same time, not have to support part of it later because someone might not flip the feature bit.

Cheers,
Ben.

#58 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 2 lines · Benjamin Herrenschmidt

However I can see at least one advantage of what you've done :-) You never have to deal with holes in the ring.

Another advantage is the design goal for that ring:
host never needs to copy even if it completes
descriptors out of order. And out of order is something that does not happen at all with hardware drivers. This is where paravirt is different.

Quoted 10 lines
Quoted 6 lines
Quoted 2 lines

Two rings do have the advantage of not requiring host side copy, which copy would surely add to cache pressure.

Well, a simple host could process in-order and leave stuff in the ring I guess. A smarter host would copy and queue, maybe leave one queue entry in so it doesn't get flooded?

What's wrong with a ring of descriptors + a ring of completion, with a single toggle valid bit to indicate whether a given descriptor is valid or not (to avoid the nasty ping pong on the ring head/tails).

First, I don't understand how a valid bit avoids ping poing on the last descriptor. Second, how do you handle out of order completions?

Quoted 10 lines
Quoted 4 lines
Quoted 2 lines

About inline - it can only help very small buffers. Which workloads do you have in mind exactly?

It was suggested by others, but I think TCP Acks are the classic one.

Split headers + data too, tho that means supporting immediate + indirect.

It makes a lot of sense for command rings as well if we're going to go down that route.

I don't see why it makes sense for commands. It's a performance optimization and commands are off the data path.

Quoted 27 lines
Quoted 27 lines

12 + 14 + 20 + 40 = 86 bytes with virtio_net_hdr_mrg_rxbuf at the front.

Quoted 2 lines

BTW this seems to be the reverse from what you have in Mar 2001, see [email protected] :)

(s/2001/2011). Indeed. Noone shared my optimism that having an open process for a virtio2 would bring more players on board (my original motivation). But technical requirements are mounting up, which means we're going to get there anyway.

Quoted 5 lines

I am much less concerned with what we do for configuration, but I do not believe we have learned all performance lessons from virtio ring1. Is there any reason why we shouldn't be able to experiment with inline within virtio1 and see whether that gets us anything?

Inline in the used ring is possible, but those descriptors 8 bytes, vs 24/32.

Quoted 7 lines

If we do a bunch of changes to the ring at once, we can't figure out what's right, what's wrong, or back out of mistakes later.

Since there are non PCI transports that use the ring, we really shouldn't make both the configuration and the ring changes depend on the same feature bit.

Quoted 7 lines
Quoted 3 lines

Yes, I'm thinking #define VIRTIO_F_VIRTIO2 (-1). For PCI, this gets mapped into a "are we using the new config layout?". For others, it gets mapped into a transport-specific feature.

Or we can use the PCI ProgIf to indicate a different programming interface, that way we also use that as an excuse to say that the first BAR can either be PIO or MMIO :-)

We can't, legal PCI ProgIf values are defined in PCI spec.

Quoted 8 lines
Quoted 4 lines

(I'm sure you get it, but for the others) This is because I want to be draw a clear line between all the legacy stuff at the same time, not have to support part of it later because someone might not flip the feature bit.

Cheers,
Ben.

#59 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 9 lines · Michael S. Tsirkin
Quoted 2 lines · Benjamin Herrenschmidt

However I can see at least one advantage of what you've done :-) You never have to deal with holes in the ring.

Another advantage is the design goal for that ring:
host never needs to copy even if it completes
descriptors out of order. And out of order is something that does not happen at all with hardware drivers. This is where paravirt is different.

Actually out of order can happen with tagged command queue for SCSI or ATA, tho I'm not 100% familiar with how things like AHCI handle this.

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines

Two rings do have the advantage of not requiring host side copy, which copy would surely add to cache pressure.

Well, a simple host could process in-order and leave stuff in the ring I guess. A smarter host would copy and queue, maybe leave one queue entry in so it doesn't get flooded?

Quoted 6 lines
Quoted 3 lines

What's wrong with a ring of descriptors + a ring of completion, with a single toggle valid bit to indicate whether a given descriptor is valid or not (to avoid the nasty ping pong on the ring head/tails).

First, I don't understand how a valid bit avoids ping poing on the last descriptor. Second, how do you handle out of order completions?

A toggle means the valid bit is never cleared, it just changes polarity every time you go around the ring. So there's never a write back to 0.

Out of order is something I hadn't thought about (I was most probably too focused on virtio-net) and is indeed a PITA. It's doable with rings but can get nasty.

I'll give that more thought in the next week, and Rusty and I shall play with userspace models based on your tool.

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 2 lines

About inline - it can only help very small buffers. Which workloads do you have in mind exactly?

It was suggested by others, but I think TCP Acks are the classic one.

Quoted 8 lines
Quoted 5 lines

Split headers + data too, tho that means supporting immediate + indirect.

It makes a lot of sense for command rings as well if we're going to go down that route.

I don't see why it makes sense for commands. It's a performance optimization and commands are off the data path.

Oh just code simplification not having to dequeue a descriptor, allocate a buffer, etc... but a lot of that can be buried in helpers indeed.

Quoted 1 line

We can't, legal PCI ProgIf values are defined in PCI spec.

Hrm, more or less yes, I suppose we should stay away from that then, do we use revision ID for anything in virtio-land ?

Cheers,
Ben.

#60 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 25 lines · Benjamin Herrenschmidt
Quoted 9 lines · Michael S. Tsirkin
Quoted 2 lines · Benjamin Herrenschmidt

However I can see at least one advantage of what you've done :-) You never have to deal with holes in the ring.

Another advantage is the design goal for that ring:
host never needs to copy even if it completes
descriptors out of order. And out of order is something that does not happen at all with hardware drivers. This is where paravirt is different.

Actually out of order can happen with tagged command queue for SCSI or ATA, tho I'm not 100% familiar with how things like AHCI handle this.

Quoted 11 lines
Quoted 10 lines
Quoted 6 lines
Quoted 2 lines

Two rings do have the advantage of not requiring host side copy, which copy would surely add to cache pressure.

Well, a simple host could process in-order and leave stuff in the ring I guess. A smarter host would copy and queue, maybe leave one queue entry in so it doesn't get flooded?

What's wrong with a ring of descriptors + a ring of completion, with a single toggle valid bit to indicate whether a given descriptor is valid or not (to avoid the nasty ping pong on the ring head/tails).

Quoted 5 lines
Quoted 2 lines

First, I don't understand how a valid bit avoids ping poing on the last descriptor. Second, how do you handle out of order completions?

A toggle means the valid bit is never cleared, it just changes polarity every time you go around the ring. So there's never a write back to 0.

Ah, OK. Still we might have a cache line bouncing back and forth as host rechecks this valid bit while guest toggles it.

This also gets nasty with e.g. inline data.

Quoted 6 lines

Out of order is something I hadn't thought about (I was most probably too focused on virtio-net) and is indeed a PITA. It's doable with rings but can get nasty.

I'll give that more thought in the next week, and Rusty and I shall play with userspace models based on your tool.

You might want to add more workloads - I have modeled stream transmit, but e.g. ping pong is also interesting.

One othe rinteresting thing is that
in practice host often runs on the same CPU as guest.
This is the fastest way to run the host for when it
does not do a lot of work as most data is cached. And there amount of sharing is less important than reducing cache consumption.

Again this is different from real hardware.

Quoted 25 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 2 lines

About inline - it can only help very small buffers. Which workloads do you have in mind exactly?

It was suggested by others, but I think TCP Acks are the classic one.

Quoted 8 lines
Quoted 5 lines

Split headers + data too, tho that means supporting immediate + indirect.

It makes a lot of sense for command rings as well if we're going to go down that route.

I don't see why it makes sense for commands. It's a performance optimization and commands are off the data path.

Oh just code simplification not having to dequeue a descriptor, allocate a buffer, etc... but a lot of that can be buried in helpers indeed.

Quoted 1 line

We can't, legal PCI ProgIf values are defined in PCI spec.

Hrm, more or less yes, I suppose we should stay away from that then, do we use revision ID for anything in virtio-land ?

Cheers,
Ben.

#61 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 2 lines · Michael S. Tsirkin

One othe rinteresting thing is that
in practice host often runs on the same CPU as guest.

That's surprising. On Power with our huge exit cost, I would think that would hurt more than anything else.

Quoted 5 lines

This is the fastest way to run the host for when it
does not do a lot of work as most data is cached. And there amount of sharing is less important than reducing cache consumption.

Again this is different from real hardware.

Yes indeed.

Cheers,
Ben.

#62 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Rusty Russell
Quoted 19 lines · Rusty Russell
Quoted 18 lines · Michael S. Tsirkin
Quoted 16 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Okay - want me to propose an interface for that?

Had a brief chat with BenH (CC'd).

I think we should deprecate writing to the config space. Only balloon does it AFAICT, and I can't quite figure out *why* it has an 'active' field.

Quoted 5 lines
Quoted 1 line

Are you sure? I think net writes a mac address.

True. We'll need to disable that, and come up with another mechanism if we want it back (a new feature and a VIRTIO_NET_HDR_F_SET_MAC flag in the virtio_net header? Or would that mess up vhost_net?).

vhost net is only datapath, so no problem. copying in host is tricky for vhost_net.

Quoted 10 lines
Quoted 9 lines
Quoted 3 lines

This solves half the problem, of sync guest writes. For the other half, I suggest a generation counter; odd means inconsistent. The guest can poll.

So we get the counter until it's even, get the config, if it's changed repeat? Yes it works. However, I would like to have a way to detect config change just by looking at memory. ATM we need to read ISR to know. If we used a VQ, the advantage would be that the device can work with a single MSIX vector shared by all VQs.

Quoted 3 lines

If we use a 32-bit counter, we also get this though, right?

If counter has changed, it's a config interrupt...

But we need an exit to read the counter. We can put the counter in memory but this looks suspiciously like a simplified VQ - so why not use a VQ then?

Quoted 6 lines
Quoted 3 lines

If we do require config VQ anyway, why not use it to notify guest of config changes? Guest could pre-post an in buffer and host uses that.

We could, but it's an additional burden on each device. vqs are cheap, but not free. And the config area is so damn convenient...

Not if you start playing with counters, checking it twice, reinvent all kind of barriers ...

Quoted 45 lines
Quoted 7 lines
Quoted 3 lines

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines.

Are we going to keep guest endian for e.g. virtio net header? If yes the benefit of switching config space is not that big. And changes in devices would affect non-PCI transports.

Yep. It would only make sense if we do it for everything. And yes, it'll mess up everyone who is BE, so it needs to be a feature bit for them.

Quoted 9 lines
Quoted 5 lines

We should also change the ring (to a single ring, I think). Descriptors to 24 bytes long (8 byte cookie, 8 byte addr, 4 byte len, 4 byte flags). We might be able to squeeze it into 20 bytes but that means packing. We should support inline, chained or indirect. Let the other side ack by setting flag, cookie and len (if written).

Quite possibly all or some of these things help performance but do we have to change the spec before we have experimental proof?

We change the spec last, once we know what we're doing, ideally.

Quoted 2 lines

I did experiment with a single ring using tools/virtio and I didn't see a measureable performance gain.

Interesting. It is simpler and more standard than our current design, but that's not sufficient unless there are other reasons. Needs further discussion and testing.

Quoted 2 lines

Two rings do have the advantage of not requiring host side copy, which copy would surely add to cache pressure.

Well, a simple host could process in-order and leave stuff in the ring I guess. A smarter host would copy and queue, maybe leave one queue entry in so it doesn't get flooded?

Quoted 6 lines

Since
host doesn't change desriptors, we could also
preformat some descriptors in the current design.

There is a fragmentation problem in theory but it can be alleviated with a smart allocator.

Quoted 7 lines

Yeah, the complexity scares me...

Quoted 2 lines

About inline - it can only help very small buffers. Which workloads do you have in mind exactly?

It was suggested by others, but I think TCP Acks are the classic one. 12 + 14 + 20 + 40 = 86 bytes with virtio_net_hdr_mrg_rxbuf at the front.

That's only the simplest IPv4, right?
Anyway, this spans multiple descriptors so this complicates allocation significantly.

Quoted 8 lines
Quoted 2 lines

BTW this seems to be the reverse from what you have in Mar 2001, see [email protected] :)

(s/2001/2011). Indeed. Noone shared my optimism that having an open process for a virtio2 would bring more players on board (my original motivation). But technical requirements are mounting up, which means we're going to get there anyway.

Quoted 8 lines
Quoted 5 lines

I am much less concerned with what we do for configuration, but I do not believe we have learned all performance lessons from virtio ring1. Is there any reason why we shouldn't be able to experiment with inline within virtio1 and see whether that gets us anything?

Inline in the used ring is possible, but those descriptors 8 bytes, vs 24/32.

Hmm, 86 > 32 anyway, right?

Quoted 12 lines
Quoted 7 lines

If we do a bunch of changes to the ring at once, we can't figure out what's right, what's wrong, or back out of mistakes later.

Since there are non PCI transports that use the ring, we really shouldn't make both the configuration and the ring changes depend on the same feature bit.

Yes, I'm thinking #define VIRTIO_F_VIRTIO2 (-1). For PCI, this gets mapped into a "are we using the new config layout?". For others, it gets mapped into a transport-specific feature.

Quoted 7 lines

(I'm sure you get it, but for the others) This is because I want to be draw a clear line between all the legacy stuff at the same time, not have to support part of it later because someone might not flip the feature bit.

Cheers,
Rusty.

So my point is, config stuff and ring are completely separate, they are different layers.

Signature · 1 line
MST
#63 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 8 lines · Michael S. Tsirkin
Quoted 3 lines · Rusty Russell

If we use a 32-bit counter, we also get this though, right?

If counter has changed, it's a config interrupt...

But we need an exit to read the counter. We can put the counter in memory but this looks suspiciously like a simplified VQ - so why not use a VQ then?

Because now a driver first gets the data from config space. But from then on, they have to get it from the vq, and ignore the config space. That's a bit weird.

Quoted 9 lines
Quoted 6 lines
Quoted 3 lines

If we do require config VQ anyway, why not use it to notify guest of config changes? Guest could pre-post an in buffer and host uses that.

We could, but it's an additional burden on each device. vqs are cheap, but not free. And the config area is so damn convenient...

Not if you start playing with counters, checking it twice, reinvent all kind of barriers ...

None of that appears inside the driver, though. And let's be honest, it's not *that* bad (very approx code):

static u32 vp_get_gen(struct virtio_pci_device *vp_dev)

{
        u32 gen;
        do {
                gen = ioread32(vp_dev->ioaddr + VIRTIO_PCI_CONFIG_GEN);
        } while (unlikely((gen & 1) == 1));

        virtio_rmb();
        return gen;
}

static bool vp_check_gen(struct virtio_pci_device *vp_dev, u32 gen)

{
        virtio_rmb();
        return ioread32(vp_dev->ioaddr + VIRTIO_PCI_CONFIG_GEN) == gen;
}

static void vp_get32(struct virtio_device *vdev, unsigned offset, u32 *buf)

{
	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        u32 gen;

        do {
                gen = vp_get_gen(vdev);
                *buf = ioread32(vp_dev->ioaddr + VIRTIO_PCI_CONFIG(vp_dev) + offset);
        } while (unlikely(!vp_check_gen(vp_dev, gen)));
}

...

Quoted 6 lines
Quoted 2 lines

It was suggested by others, but I think TCP Acks are the classic one. 12 + 14 + 20 + 40 = 86 bytes with virtio_net_hdr_mrg_rxbuf at the front.

That's only the simplest IPv4, right?
Anyway, this spans multiple descriptors so this complicates allocation significantly.

Yes, I think any general-but-useful inline will need to span multiple descriptors. That's part of the fun!

Let's get totally crazy and implement our ring in stripes, like:

00 04 08 12 01 05 09 13 02 06 10 14 03 07 11 15

That way consecutive (32-byte) descriptors don't share a cacheline! (Not serious... quiet.)

Quoted 11 lines
Quoted 8 lines

Yes, I'm thinking #define VIRTIO_F_VIRTIO2 (-1). For PCI, this gets mapped into a "are we using the new config layout?". For others, it gets mapped into a transport-specific feature.

(I'm sure you get it, but for the others) This is because I want to be draw a clear line between all the legacy stuff at the same time, not have to support part of it later because someone might not flip the feature bit.

So my point is, config stuff and ring are completely separate, they are different layers.

Absolutely, and we should analyze them separately as well as together.

*But* for maintenance is far easier if we only have to test new config+new ring and old config+old ring. They do interact, because remember, the allocation of the ring changes with new config, too...

Cheers,
Rusty.

#64 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Rusty Russell
Quoted 19 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Okay - want me to propose an interface for that?

Had a brief chat with BenH (CC'd).

I think we should deprecate writing to the config space. Only balloon does it AFAICT, and I can't quite figure out *why* it has an 'active' field. This solves half the problem, of sync guest writes. For the other half, I suggest a generation counter; odd means inconsistent. The guest can poll.

Quoted 3 lines

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines

I think the more important thing to do is require accesses to integers in the config space to always be aligned and to use the appropriate accessor. Non-integer fields should be restricted to byte access.

That limits config space entries to 32-bit but also means that there is no need for a generation counter. It's also easier to deal with endian conversion that way.

But it means the backend code ends up being much simpler to write (because it behaves more like a normal PCI device).

If we're already making the change, the endianness ought to be a feature bit.

Quoted 1 line

We should also change the ring (to a single ring, I think).

Ack.

Quoted 10 lines

Descriptors
to 24 bytes long (8 byte cookie, 8 byte addr, 4 byte len, 4 byte flags). We might be able to squeeze it into 20 bytes but that means packing. We should support inline, chained or indirect. Let the other side ack by setting flag, cookie and len (if written).

Moreover, I think we should make all these changes at once (at least, in the spec). That makes it a big change, and it'll take longer to develop, but makes it easy in the long run to differentiate legacy and modern virtio.

Ack. Long live virtio2! :-)

Regards,

Anthony Liguori

Quoted 3 lines

Thoughts?
Rusty.

#65 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 24 lines · Anthony Liguori
Quoted 19 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Okay - want me to propose an interface for that?

Had a brief chat with BenH (CC'd).

I think we should deprecate writing to the config space. Only balloon does it AFAICT, and I can't quite figure out *why* it has an 'active' field. This solves half the problem, of sync guest writes. For the other half, I suggest a generation counter; odd means inconsistent. The guest can poll.

Quoted 3 lines

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines

Quoted 8 lines

I think the more important thing to do is require accesses to integers in the config space to always be aligned and to use the appropriate accessor. Non-integer fields should be restricted to byte access.

That limits config space entries to 32-bit but also means that there is no need for a generation counter. It's also easier to deal with endian conversion that way.

This is similar to what we have now. But it's still buggy: e.g. if guest updates MAC byte by byte, we have no way to know when it's done doing so.

Quoted 29 lines

But it means the backend code ends up being much simpler to write (because it behaves more like a normal PCI device).

If we're already making the change, the endianness ought to be a feature bit.

Quoted 1 line

We should also change the ring (to a single ring, I think).

Ack.

Quoted 10 lines

Descriptors
to 24 bytes long (8 byte cookie, 8 byte addr, 4 byte len, 4 byte flags). We might be able to squeeze it into 20 bytes but that means packing. We should support inline, chained or indirect. Let the other side ack by setting flag, cookie and len (if written).

Moreover, I think we should make all these changes at once (at least, in the spec). That makes it a big change, and it'll take longer to develop, but makes it easy in the long run to differentiate legacy and modern virtio.

Ack. Long live virtio2! :-)

Regards,

Anthony Liguori

Quoted 3 lines

Thoughts?
Rusty.

#66 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 30 lines · Michael S. Tsirkin
Quoted 29 lines · Anthony Liguori
Quoted 19 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Okay - want me to propose an interface for that?

Had a brief chat with BenH (CC'd).

I think we should deprecate writing to the config space. Only balloon does it AFAICT, and I can't quite figure out *why* it has an 'active' field. This solves half the problem, of sync guest writes. For the other half, I suggest a generation counter; odd means inconsistent. The guest can poll.

Quoted 3 lines

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines

I think the more important thing to do is require accesses to integers in the config space to always be aligned and to use the appropriate accessor. Non-integer fields should be restricted to byte access.

Quoted 7 lines
Quoted 3 lines

That limits config space entries to 32-bit but also means that there is no need for a generation counter. It's also easier to deal with endian conversion that way.

This is similar to what we have now. But it's still buggy: e.g. if guest updates MAC byte by byte, we have no way to know when it's done doing so.

This is no different than a normal network card. You have to use a secondary register to trigger an update.

Regards,

Anthony Liguori

Quoted 31 lines
Quoted 31 lines

But it means the backend code ends up being much simpler to write (because it behaves more like a normal PCI device).

If we're already making the change, the endianness ought to be a feature bit.

Quoted 1 line

We should also change the ring (to a single ring, I think).

Ack.

Quoted 10 lines

Descriptors
to 24 bytes long (8 byte cookie, 8 byte addr, 4 byte len, 4 byte flags). We might be able to squeeze it into 20 bytes but that means packing. We should support inline, chained or indirect. Let the other side ack by setting flag, cookie and len (if written).

Moreover, I think we should make all these changes at once (at least, in the spec). That makes it a big change, and it'll take longer to develop, but makes it easy in the long run to differentiate legacy and modern virtio.

Ack. Long live virtio2! :-)

Regards,

Anthony Liguori

Quoted 3 lines

Thoughts?
Rusty.

#67 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 39 lines · Anthony Liguori
Quoted 30 lines · Michael S. Tsirkin
Quoted 29 lines · Anthony Liguori
Quoted 19 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell

Yes. The idea that we can alter fields in the device-specific config area is flawed. There may be cases where it doesn't matter, but as an idea it was holed to begin with.

We can reduce probability by doing a double read to check, but there are still cases where it will fail.

Okay - want me to propose an interface for that?

Had a brief chat with BenH (CC'd).

I think we should deprecate writing to the config space. Only balloon does it AFAICT, and I can't quite figure out *why* it has an 'active' field. This solves half the problem, of sync guest writes. For the other half, I suggest a generation counter; odd means inconsistent. The guest can poll.

Quoted 3 lines

BenH also convinced me we should finally make the config space LE if we're going to change things. Since PCI is the most common transport, guest-endian confuses people. And it sucks for really weird machines

I think the more important thing to do is require accesses to integers in the config space to always be aligned and to use the appropriate accessor. Non-integer fields should be restricted to byte access.

Quoted 7 lines
Quoted 3 lines

That limits config space entries to 32-bit but also means that there is no need for a generation counter. It's also easier to deal with endian conversion that way.

This is similar to what we have now. But it's still buggy: e.g. if guest updates MAC byte by byte, we have no way to know when it's done doing so.

Quoted 6 lines

This is no different than a normal network card. You have to use a secondary register to trigger an update.

Regards,

Anthony Liguori

Possible but doesn't let us layer nicely to allow unchanged drivers that work with all transports (new pci, old pci, non pci). Something like a command VQ would be a generic transport that can be hidden behind config->set(...).

Quoted 31 lines
Quoted 31 lines
Quoted 31 lines

But it means the backend code ends up being much simpler to write (because it behaves more like a normal PCI device).

If we're already making the change, the endianness ought to be a feature bit.

Quoted 1 line

We should also change the ring (to a single ring, I think).

Ack.

Quoted 10 lines

Descriptors
to 24 bytes long (8 byte cookie, 8 byte addr, 4 byte len, 4 byte flags). We might be able to squeeze it into 20 bytes but that means packing. We should support inline, chained or indirect. Let the other side ack by setting flag, cookie and len (if written).

Moreover, I think we should make all these changes at once (at least, in the spec). That makes it a big change, and it'll take longer to develop, but makes it easy in the long run to differentiate legacy and modern virtio.

Ack. Long live virtio2! :-)

Regards,

Anthony Liguori

Quoted 3 lines

Thoughts?
Rusty.

#68 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 8 lines · Michael S. Tsirkin
Quoted 7 lines · Anthony Liguori
Quoted 3 lines

This is similar to what we have now. But it's still buggy: e.g. if guest updates MAC byte by byte, we have no way to know when it's done doing so.

This is no different than a normal network card. You have to use a secondary register to trigger an update.

Quoted 6 lines
Quoted 3 lines

Regards,

Anthony Liguori

Possible but doesn't let us layer nicely to allow unchanged drivers that work with all transports (new pci, old pci, non pci).

If we declare config space LE, then we have to touch all drivers. There's no way around it because the virtio API is byte-based, not word based.

This is why I'm suggesting making the virtio API (and then the virtio-pci ABI) word based. It gives us the flexibility to make endianness a property of the transport and not a property of the devices.

Regards,

Anthony Liguori

#69 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 11 lines · Anthony Liguori
Quoted 10 lines · Michael S. Tsirkin
Quoted 9 lines · Anthony Liguori
Quoted 3 lines

This is similar to what we have now. But it's still buggy: e.g. if guest updates MAC byte by byte, we have no way to know when it's done doing so.

This is no different than a normal network card. You have to use a secondary register to trigger an update.

Regards,

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Anthony Liguori

Possible but doesn't let us layer nicely to allow unchanged drivers that work with all transports (new pci, old pci, non pci).

If we declare config space LE, then we have to touch all drivers. There's no way around it because the virtio API is byte-based, not word based.

Fine but don't we want to be compatible with old hypervisors?

Quoted 8 lines

This is why I'm suggesting making the virtio API (and then the virtio-pci ABI) word based. It gives us the flexibility to make endianness a property of the transport and not a property of the devices.

Regards,

Anthony Liguori

Some fields are 64 bit, this is still tricky to do atomically. What's the objection to using a config VQ?

#70 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 14 lines · Michael S. Tsirkin
Quoted 13 lines · Anthony Liguori
Quoted 12 lines · Michael S. Tsirkin
Quoted 10 lines · Anthony Liguori
Quoted 3 lines

This is similar to what we have now. But it's still buggy: e.g. if guest updates MAC byte by byte, we have no way to know when it's done doing so.

This is no different than a normal network card. You have to use a secondary register to trigger an update.

Regards,

Anthony Liguori

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

Possible but doesn't let us layer nicely to allow unchanged drivers that work with all transports (new pci, old pci, non pci).

If we declare config space LE, then we have to touch all drivers. There's no way around it because the virtio API is byte-based, not word based.

Fine but don't we want to be compatible with old hypervisors?

We can modify the drivers to work either with a virtio1 or virtio2 transport. If the only difference is that we move to word access instead of byte access for the config space, it's a nop because drivers don't rely on sub-word access today.

Quoted 11 lines
Quoted 8 lines

This is why I'm suggesting making the virtio API (and then the virtio-pci ABI) word based. It gives us the flexibility to make endianness a property of the transport and not a property of the devices.

Regards,

Anthony Liguori

Some fields are 64 bit, this is still tricky to do atomically. What's the objection to using a config VQ?

Then we move very far away from something that looks like a PCI device. The problem we're having here is specifically where we've deviated from what a normal PCI device would do. Fixing that by deviating even further seems counter intuitive to me.

Regards,

Anthony Liguori

Quoted 1 line
#71 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 41 lines · Anthony Liguori
Quoted 14 lines · Michael S. Tsirkin
Quoted 13 lines · Anthony Liguori
Quoted 12 lines · Michael S. Tsirkin
Quoted 10 lines · Anthony Liguori
Quoted 3 lines

This is similar to what we have now. But it's still buggy: e.g. if guest updates MAC byte by byte, we have no way to know when it's done doing so.

This is no different than a normal network card. You have to use a secondary register to trigger an update.

Regards,

Anthony Liguori

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

Possible but doesn't let us layer nicely to allow unchanged drivers that work with all transports (new pci, old pci, non pci).

If we declare config space LE, then we have to touch all drivers. There's no way around it because the virtio API is byte-based, not word based.

Fine but don't we want to be compatible with old hypervisors?

We can modify the drivers to work either with a virtio1 or virtio2 transport. If the only difference is that we move to word access instead of byte access for the config space, it's a nop because drivers don't rely on sub-word access today.

Quoted 11 lines
Quoted 8 lines

This is why I'm suggesting making the virtio API (and then the virtio-pci ABI) word based. It gives us the flexibility to make endianness a property of the transport and not a property of the devices.

Regards,

Anthony Liguori

Some fields are 64 bit, this is still tricky to do atomically. What's the objection to using a config VQ?

Quoted 8 lines

Then we move very far away from something that looks like a PCI device. The problem we're having here is specifically where we've deviated from what a normal PCI device would do. Fixing that by deviating even further seems counter intuitive to me.

Regards,

Anthony Liguori

Not sure what you mean. Using VQ is DMA which is pretty common for PCI.

Quoted 1 line
Quoted 1 line
#72 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 2 lines · Michael S. Tsirkin

Not sure what you mean. Using VQ is DMA which is pretty common for PCI.

Do you know of a network device that obtains it's mac address via a DMA transaction?

Regards,

Anthony Liguori

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
#73 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 5 lines · Anthony Liguori
Quoted 2 lines · Michael S. Tsirkin

Not sure what you mean. Using VQ is DMA which is pretty common for PCI.

Do you know of a network device that obtains it's mac address via a DMA transaction?

Sure.
See mlx4_replace_mac in drivers/net/ethernet/mellanox/mlx4/port.c

Quoted 6 lines

Regards,

Anthony Liguori

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
#74 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 9 lines · Michael S. Tsirkin
Quoted 5 lines · Anthony Liguori
Quoted 2 lines · Michael S. Tsirkin

Not sure what you mean. Using VQ is DMA which is pretty common for PCI.

Do you know of a network device that obtains it's mac address via a DMA transaction?

Sure.
See mlx4_replace_mac in drivers/net/ethernet/mellanox/mlx4/port.c

I'd say that's a special case but I see what you're getting at here.

So what about keeping the config space read-only and using control queues for everything else?

Regards,

Anthony Liguori

Quoted 7 lines
Quoted 7 lines

Regards,

Anthony Liguori

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
#75 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 6 lines · Anthony Liguori

I'd say that's a special case but I see what you're getting at here.

So what about keeping the config space read-only and using control queues for
everything else?

Which is exactly what Rusty and I are proposing :-) I would go further and eliminate the idea of a seqlock and instead of a status queue with precise messages indicating what changed.

I would couple that with the new queue format allowing immediate data in the descriptor to avoid having to use indirect buffers for these, which means no allocation, no buffer pool etc... which makes everything a lot easier to deal with as well.

We could probably have a helper library for sending control messages which could handle waiting for a ring slot to be free (practically always the case on control queues), writing the message, sending it and waiting for a status queue confirmation message.

Cheers,
Ben.

#76 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 13 lines · Benjamin Herrenschmidt
Quoted 6 lines · Anthony Liguori

I'd say that's a special case but I see what you're getting at here.

So what about keeping the config space read-only and using control queues for
everything else?

Which is exactly what Rusty and I are proposing :-)
I would go further
and eliminate the idea of a seqlock and instead of a status queue with precise messages indicating what changed.

Quoted 4 lines

I would couple that with the new queue format allowing immediate data in the descriptor to avoid having to use indirect buffers for these, which means no allocation, no buffer pool etc... which makes everything a lot easier to deal with as well.

We just need a couple of buffers outstanding. It can't be easier, and a single buf descriptors already do not use indirection.

Quoted 8 lines

We could probably have a helper library for sending control messages which could handle waiting for a ring slot to be free (practically always the case on control queues), writing the message, sending it and waiting for a status queue confirmation message.

Cheers,
Ben.

Look, we have a race currently. Let us not tie a bug fix to a huge rewrite with unclear performance benefits, please.

Signature · 1 line
MST
#77 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 4 lines · Michael S. Tsirkin
Quoted 3 lines

We could probably have a helper library for sending control messages which could handle waiting for a ring slot to be free (practically always the case on control queues), writing the message, sending it

and

Quoted 8 lines
Quoted 5 lines

waiting for a status queue confirmation message.

Cheers,
Ben.

Look, we have a race currently. Let us not tie a bug fix to a huge rewrite with unclear performance benefits, please.

Well, if we change endian, change the way config works, I think we are doing enough dramatic changes to go all the way and change the ring format too.

I don't think there's anything "unclear" about improving the rings in the direction that all network cards have been chosing so far :-) But for the control ring, performance is clearly not an issue.

I also like the simplicity of immediate data, it limits the failure path, no allocation -does- make it easier, not having to wait for the previous buffer to be complete if you pre-allocate etc...

In any case, if we do a lot of changes at the same time, we are probably better off cutting the cord and making an incompatible vfio v2 with a new set of drivers.

Having to deal with all the variants in a single driver will result in unmaintainable drivers imho.

Cheers,
Ben.

#78 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Michael S. Tsirkin
Quoted 2 lines · Michael S. Tsirkin

Look, we have a race currently. Let us not tie a bug fix to a huge rewrite with unclear performance benefits, please.

In theory, yes. In practice, we bandaid it.

I think in the short term we change ->get to get the entire sequence twice, and check it's the same. Theoretically, still racy, but it does cut the window. And we haven't seen the bug yet, either.

In the longer term, we fix it properly:

1) Make it readonly, prevents one class of problems.
2) Treat it as constant if drv->config_changed is NULL (we can do this now, in fact) and ignore the config interrupt.
3) Use a generation counter on the config, odd means wait.

Cheers,
Rusty.

#79 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 9 lines · Rusty Russell
Quoted 2 lines · Michael S. Tsirkin

Look, we have a race currently. Let us not tie a bug fix to a huge rewrite with unclear performance benefits, please.

In theory, yes. In practice, we bandaid it.

I think in the short term we change ->get to get the entire sequence twice, and check it's the same. Theoretically, still racy, but it does cut the window. And we haven't seen the bug yet, either.

I thought about this some more. Since we always get an interrupt on config changes, it seems that a rather robust method would be to just synchronize against that. Something like the below (warning - completely untested). Still need to think about memory barriers, overflow etc. What do you think?

drivers/virtio/virtio_pci.c +31 -16
index 03d1984..b5df385 100644
@@ -57,6 +57,7 @@ struct virtio_pci_device
 	unsigned msix_used_vectors;
 	/* Whether we have vector per vq */
 	bool per_vq_vectors;
+	atomic_t config_changes;
 };
 
 /* Constants for MSI-X */
@@ -125,6 +126,19 @@ static void vp_finalize_features(struct virtio_device *vdev)
 	iowrite32(vdev->features[0], vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES);
 }
 
+/* wait for pending irq handlers */
+static void vp_synchronize_vectors(struct virtio_device *vdev)
+{
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	int i;
+
+	if (vp_dev->intx_enabled)
+		synchronize_irq(vp_dev->pci_dev->irq);
+
+	for (i = 0; i < vp_dev->msix_vectors; ++i)
+		synchronize_irq(vp_dev->msix_entries[i].vector);
+}
+
 /* virtio config->get() implementation */
 static void vp_get(struct virtio_device *vdev, unsigned offset,
 		   void *buf, unsigned len)
@@ -134,9 +148,20 @@ static void vp_get(struct virtio_device *vdev, unsigned offset,
 				VIRTIO_PCI_CONFIG(vp_dev) + offset;
 	u8 *ptr = buf;
 	int i;
-
-	for (i = 0; i < len; i++)
-		ptr[i] = ioread8(ioaddr + i);
+	int uninitialized_var(c);
+	c = atomic_read(&vp_dev->config_changes);
+	/* Make sure read is done before we get the first config byte */
+	rmb();
+	do {
+		for (i = 0; i < len; i++)
+			ptr[i] = ioread8(ioaddr + i);
+		/* Synchronize with config interrupt */
+		vp_synchronize_vectors(vdev);
+		/*
+		 * For multi-byte fields, we might get a config change interrupt
+		 * between byte reads. If this happens, retry the read.
+		 */
+	} while (c != atomic_read(&vp_dev->config_changes))
 }
 
 /* the config->set() implementation.  it's symmetric to the config->get()
@@ -169,19 +194,6 @@ static void vp_set_status(struct virtio_device *vdev, u8 status)
 	iowrite8(status, vp_dev->ioaddr + VIRTIO_PCI_STATUS);
 }
 
-/* wait for pending irq handlers */
-static void vp_synchronize_vectors(struct virtio_device *vdev)
-{
-	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-	int i;
-
-	if (vp_dev->intx_enabled)
-		synchronize_irq(vp_dev->pci_dev->irq);
-
-	for (i = 0; i < vp_dev->msix_vectors; ++i)
-		synchronize_irq(vp_dev->msix_entries[i].vector);
-}
-
 static void vp_reset(struct virtio_device *vdev)
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
@@ -213,6 +225,8 @@ static irqreturn_t vp_config_changed(int irq, void *opaque)
 	drv = container_of(vp_dev->vdev.dev.driver,
 			   struct virtio_driver, driver);
 
+	atomic_inc(&vp_dev->config_changes);
+
 	if (drv && drv->config_changed)
 		drv->config_changed(&vp_dev->vdev);
 	return IRQ_HANDLED;
@@ -646,6 +660,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
 	vp_dev->vdev.config = &virtio_pci_config_ops;
 	vp_dev->pci_dev = pci_dev;
 	INIT_LIST_HEAD(&vp_dev->virtqueues);
+	atomic_set(&vp_dev->config_changes, 0);
 	spin_lock_init(&vp_dev->lock);
 
 	/* Disable MSI/MSIX to bring device to a known good state. */
#80 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 11 lines · Michael S. Tsirkin
Quoted 9 lines · Rusty Russell
Quoted 2 lines · Michael S. Tsirkin

Look, we have a race currently. Let us not tie a bug fix to a huge rewrite with unclear performance benefits, please.

In theory, yes. In practice, we bandaid it.

I think in the short term we change ->get to get the entire sequence twice, and check it's the same. Theoretically, still racy, but it does cut the window. And we haven't seen the bug yet, either.

Quoted 6 lines

I thought about this some more. Since we always get an interrupt on config changes, it seems that a rather robust method would be to just synchronize against that. Something like the below (warning - completely untested). Still need to think about memory barriers, overflow etc. What do you think?

Your interrupt may take an unpredictable amount of time to arrive, I don't see how you can use that as a guarantee.

Cheers,
Ben.

Quoted 95 lines
drivers/virtio/virtio_pci.c +31 -16
index 03d1984..b5df385 100644
@@ -57,6 +57,7 @@ struct virtio_pci_device
 	unsigned msix_used_vectors;
 	/* Whether we have vector per vq */
 	bool per_vq_vectors;
+	atomic_t config_changes;
 };
 
 /* Constants for MSI-X */
@@ -125,6 +126,19 @@ static void vp_finalize_features(struct virtio_device *vdev)
 	iowrite32(vdev->features[0], vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES);
 }
 
+/* wait for pending irq handlers */
+static void vp_synchronize_vectors(struct virtio_device *vdev)
+{
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	int i;
+
+	if (vp_dev->intx_enabled)
+		synchronize_irq(vp_dev->pci_dev->irq);
+
+	for (i = 0; i < vp_dev->msix_vectors; ++i)
+		synchronize_irq(vp_dev->msix_entries[i].vector);
+}
+
 /* virtio config->get() implementation */
 static void vp_get(struct virtio_device *vdev, unsigned offset,
 		   void *buf, unsigned len)
@@ -134,9 +148,20 @@ static void vp_get(struct virtio_device *vdev, unsigned offset,
 				VIRTIO_PCI_CONFIG(vp_dev) + offset;
 	u8 *ptr = buf;
 	int i;
-
-	for (i = 0; i < len; i++)
-		ptr[i] = ioread8(ioaddr + i);
+	int uninitialized_var(c);
+	c = atomic_read(&vp_dev->config_changes);
+	/* Make sure read is done before we get the first config byte */
+	rmb();
+	do {
+		for (i = 0; i < len; i++)
+			ptr[i] = ioread8(ioaddr + i);
+		/* Synchronize with config interrupt */
+		vp_synchronize_vectors(vdev);
+		/*
+		 * For multi-byte fields, we might get a config change interrupt
+		 * between byte reads. If this happens, retry the read.
+		 */
+	} while (c != atomic_read(&vp_dev->config_changes))
 }
 
 /* the config->set() implementation.  it's symmetric to the config->get()
@@ -169,19 +194,6 @@ static void vp_set_status(struct virtio_device *vdev, u8 status)
 	iowrite8(status, vp_dev->ioaddr + VIRTIO_PCI_STATUS);
 }
 
-/* wait for pending irq handlers */
-static void vp_synchronize_vectors(struct virtio_device *vdev)
-{
-	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-	int i;
-
-	if (vp_dev->intx_enabled)
-		synchronize_irq(vp_dev->pci_dev->irq);
-
-	for (i = 0; i < vp_dev->msix_vectors; ++i)
-		synchronize_irq(vp_dev->msix_entries[i].vector);
-}
-
 static void vp_reset(struct virtio_device *vdev)
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
@@ -213,6 +225,8 @@ static irqreturn_t vp_config_changed(int irq, void *opaque)
 	drv = container_of(vp_dev->vdev.dev.driver,
 			   struct virtio_driver, driver);
 
+	atomic_inc(&vp_dev->config_changes);
+
 	if (drv && drv->config_changed)
 		drv->config_changed(&vp_dev->vdev);
 	return IRQ_HANDLED;
@@ -646,6 +660,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
 	vp_dev->vdev.config = &virtio_pci_config_ops;
 	vp_dev->pci_dev = pci_dev;
 	INIT_LIST_HEAD(&vp_dev->virtqueues);
+	atomic_set(&vp_dev->config_changes, 0);
 	spin_lock_init(&vp_dev->lock);
 
 	/* Disable MSI/MSIX to bring device to a known good state. */
#81 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 19 lines · Benjamin Herrenschmidt
Quoted 11 lines · Michael S. Tsirkin
Quoted 9 lines · Rusty Russell
Quoted 2 lines · Michael S. Tsirkin

Look, we have a race currently. Let us not tie a bug fix to a huge rewrite with unclear performance benefits, please.

In theory, yes. In practice, we bandaid it.

I think in the short term we change ->get to get the entire sequence twice, and check it's the same. Theoretically, still racy, but it does cut the window. And we haven't seen the bug yet, either.

Quoted 6 lines

I thought about this some more. Since we always get an interrupt on config changes, it seems that a rather robust method would be to just synchronize against that. Something like the below (warning - completely untested). Still need to think about memory barriers, overflow etc. What do you think?

Quoted 5 lines

Your interrupt may take an unpredictable amount of time to arrive, I don't see how you can use that as a guarantee.

Cheers,
Ben.

In theory yes.
Practically, under current KVM, what would delay it?

Quoted 96 lines
Quoted 95 lines
drivers/virtio/virtio_pci.c +31 -16
index 03d1984..b5df385 100644
@@ -57,6 +57,7 @@ struct virtio_pci_device
 	unsigned msix_used_vectors;
 	/* Whether we have vector per vq */
 	bool per_vq_vectors;
+	atomic_t config_changes;
 };
 
 /* Constants for MSI-X */
@@ -125,6 +126,19 @@ static void vp_finalize_features(struct virtio_device *vdev)
 	iowrite32(vdev->features[0], vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES);
 }
 
+/* wait for pending irq handlers */
+static void vp_synchronize_vectors(struct virtio_device *vdev)
+{
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	int i;
+
+	if (vp_dev->intx_enabled)
+		synchronize_irq(vp_dev->pci_dev->irq);
+
+	for (i = 0; i < vp_dev->msix_vectors; ++i)
+		synchronize_irq(vp_dev->msix_entries[i].vector);
+}
+
 /* virtio config->get() implementation */
 static void vp_get(struct virtio_device *vdev, unsigned offset,
 		   void *buf, unsigned len)
@@ -134,9 +148,20 @@ static void vp_get(struct virtio_device *vdev, unsigned offset,
 				VIRTIO_PCI_CONFIG(vp_dev) + offset;
 	u8 *ptr = buf;
 	int i;
-
-	for (i = 0; i < len; i++)
-		ptr[i] = ioread8(ioaddr + i);
+	int uninitialized_var(c);
+	c = atomic_read(&vp_dev->config_changes);
+	/* Make sure read is done before we get the first config byte */
+	rmb();
+	do {
+		for (i = 0; i < len; i++)
+			ptr[i] = ioread8(ioaddr + i);
+		/* Synchronize with config interrupt */
+		vp_synchronize_vectors(vdev);
+		/*
+		 * For multi-byte fields, we might get a config change interrupt
+		 * between byte reads. If this happens, retry the read.
+		 */
+	} while (c != atomic_read(&vp_dev->config_changes))
 }
 
 /* the config->set() implementation.  it's symmetric to the config->get()
@@ -169,19 +194,6 @@ static void vp_set_status(struct virtio_device *vdev, u8 status)
 	iowrite8(status, vp_dev->ioaddr + VIRTIO_PCI_STATUS);
 }
 
-/* wait for pending irq handlers */
-static void vp_synchronize_vectors(struct virtio_device *vdev)
-{
-	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-	int i;
-
-	if (vp_dev->intx_enabled)
-		synchronize_irq(vp_dev->pci_dev->irq);
-
-	for (i = 0; i < vp_dev->msix_vectors; ++i)
-		synchronize_irq(vp_dev->msix_entries[i].vector);
-}
-
 static void vp_reset(struct virtio_device *vdev)
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
@@ -213,6 +225,8 @@ static irqreturn_t vp_config_changed(int irq, void *opaque)
 	drv = container_of(vp_dev->vdev.dev.driver,
 			   struct virtio_driver, driver);
 
+	atomic_inc(&vp_dev->config_changes);
+
 	if (drv && drv->config_changed)
 		drv->config_changed(&vp_dev->vdev);
 	return IRQ_HANDLED;
@@ -646,6 +660,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
 	vp_dev->vdev.config = &virtio_pci_config_ops;
 	vp_dev->pci_dev = pci_dev;
 	INIT_LIST_HEAD(&vp_dev->virtqueues);
+	atomic_set(&vp_dev->config_changes, 0);
 	spin_lock_init(&vp_dev->lock);
 
 	/* Disable MSI/MSIX to bring device to a known good state. */
#82 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

Quoted 6 lines · Michael S. Tsirkin
Quoted 2 lines · Benjamin Herrenschmidt

Your interrupt may take an unpredictable amount of time to arrive, I don't see how you can use that as a guarantee.

In theory yes.
Practically, under current KVM, what would delay it?

Indeed, if we're talking about a bandaid, this is fine. For the config change, I do think we need something more robust.

Thanks,
Rusty.

#83 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Michael S. Tsirkin
Quoted 11 lines · Michael S. Tsirkin
Quoted 9 lines · Rusty Russell
Quoted 2 lines · Michael S. Tsirkin

Look, we have a race currently. Let us not tie a bug fix to a huge rewrite with unclear performance benefits, please.

In theory, yes. In practice, we bandaid it.

I think in the short term we change ->get to get the entire sequence twice, and check it's the same. Theoretically, still racy, but it does cut the window. And we haven't seen the bug yet, either.

Quoted 6 lines

I thought about this some more. Since we always get an interrupt on config changes, it seems that a rather robust method would be to just synchronize against that. Something like the below (warning - completely untested). Still need to think about memory barriers, overflow etc. What do you think?

Ben's point about arbitrary delays in irq delivery still applies. If qemu changes config space the injects, there's a window there, too.

But in practice, like my suggested double-read, it probably slows things down enough that it would be hard to hit. Testing required...

Obviously, I'd like to fix this properly, but for legacy this might work fine.

Cheers,
Rusty.

#84 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Anthony Liguori
Quoted 8 lines · Anthony Liguori
Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines · Anthony Liguori
Quoted 2 lines · Michael S. Tsirkin

Not sure what you mean. Using VQ is DMA which is pretty common for PCI.

Do you know of a network device that obtains it's mac address via a DMA transaction?

Quoted 7 lines
Quoted 2 lines

Sure.
See mlx4_replace_mac in drivers/net/ethernet/mellanox/mlx4/port.c

I'd say that's a special case but I see what you're getting at here.

So what about keeping the config space read-only and using control queues for everything else?

Not just read-only - constant.

Quoted 11 lines

Regards,

Anthony Liguori

Quoted 7 lines
Quoted 7 lines

Regards,

Anthony Liguori

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
#85 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Anthony Liguori
Quoted 5 lines · Anthony Liguori
Quoted 2 lines · Michael S. Tsirkin

Not sure what you mean. Using VQ is DMA which is pretty common for PCI.

Do you know of a network device that obtains it's mac address via a DMA transaction?

I wouldn't be surprised if we could find one, but even if we don't why is that an issue ?

Cheers,
Ben.

#86 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Michael S. Tsirkin
Quoted 5 lines · Michael S. Tsirkin

Possible but doesn't let us layer nicely to allow unchanged drivers that work with all transports (new pci, old pci, non pci). Something like a command VQ would be a generic transport that can be hidden behind config->set(...).

I agree, a command VQ (And possibly a status VQ) would be generally useful.

Cheers,
Ben.

#87 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Michael S. Tsirkin
Quoted 3 lines · Michael S. Tsirkin

This is similar to what we have now. But it's still buggy: e.g. if guest updates MAC byte by byte, we have no way to know when it's done doing so.

Do like real HW, there's plenty of options:

- (better) Have a command "update MAC" sent to a ring. A command ring
would be generally useful and could replace anything you do via writing to config space today. The advantage of having a read-only config space is that you significantly remove the need for synchronization. You could also have an event ring and avoid the seqlock for reading. It's MUCH better to have a fine granularity of what actually changed that having a generic "something is changing in the config space".

With a new ring format allowing direct data in the ring descriptor that would be trivial.

- If you really don't like a command ring, you can have a command
"register", write the new MAC and send a command to make it "apply", but that's not fantastic as there's going to be a possible discrepancy between what's in the config and what's actually used.

- Have a separate "MAC write" register set with the "hot" byte beeing
the low order byte. writing to it updates the MAC.

Etc etc...

Cheers,
Ben.

#88 lore raw

Re: [RFC 7/11] virtio_pci: new, capability-aware driver.

In reply to Anthony Liguori
Quoted 6 lines · Anthony Liguori

I think the more important thing to do is require accesses to integers in the config space to always be aligned and to use the appropriate accessor. Non-integer fields should be restricted to byte access.

That limits config space entries to 32-bit but also means that there is no need for a generation counter.

Unfortunately not, see virtio_blk. capacity is 64 bits, and geometry is multiple fields.

Quoted 1 line

If we're already making the change, the endianness ought to be a feature bit.

I'd rather tie it to the new PCI config layout (and ring format). Then we can simply have two backends, legacy and modern.

For non-PCI, we would need a feature bit.

Cheers,
Rusty.

#89 lore raw

RFC 8/11 virtio_pci: share structure between legacy and modern.

In reply to Rusty Russell

They're almost identical: we add a "legacy" ioregion (what was "ioaddr" in the legacy driver), and move it out to virtio_pci-common.h.

---
 drivers/virtio/virtio_pci-common.h |   72 ++++++++++++++++++++++
 drivers/virtio/virtio_pci.c        |   64 -------------------
 drivers/virtio/virtio_pci_legacy.c |  120 +++++++++----------------------------
 3 files changed, 105 insertions(+), 151 deletions(-)
drivers/virtio/virtio_pci-common.h new file +72 -0
new file mode 100644
@@ -0,0 +1,72 @@
+#include <linux/pci.h>
+#include <linux/virtio_pci.h>
+
+/* Our device structure: shared by virtio_pci and virtio_pci_legacy. */
+struct virtio_pci_device
+{
+	struct virtio_device vdev;
+	struct pci_dev *pci_dev;
+
+	/* The IO mapping for the PCI config space (non-legacy mode) */
+	struct virtio_pci_common_cfg __iomem *common;
+	/* Device-specific data (non-legacy mode). */
+	void __iomem *device;
+
+	/* In legacy mode, these two point to within ->legacy. */
+	/* Where to read and clear interrupt */ 
+	u8 __iomem *isr;
+	/* Write the virtqueue index here to notify device of activity. */
+	__le16 __iomem *notify;
+
+#ifdef CONFIG_VIRTIO_PCI_LEGACY
+	/* Instead of common, notify and device, legacy uses this: */
+	void __iomem *legacy;
+#endif
+
+	/* a list of queues so we can dispatch IRQs */
+	spinlock_t lock;
+	struct list_head virtqueues;
+
+	/* MSI-X support */
+	int msix_enabled;
+	int intx_enabled;
+	struct msix_entry *msix_entries;
+	/* Name strings for interrupts. This size should be enough,
+	 * and I'm too lazy to allocate each name separately. */
+	char (*msix_names)[256];
+	/* Number of available vectors */
+	unsigned msix_vectors;
+	/* Vectors allocated, excluding per-vq vectors if any */
+	unsigned msix_used_vectors;
+	/* Whether we have vector per vq */
+	bool per_vq_vectors;
+};
+
+/* Constants for MSI-X */
+/* Use first vector for configuration changes, second and the rest for
+ * virtqueues Thus, we need at least 2 vectors for MSI. */
+enum {
+	VP_MSIX_CONFIG_VECTOR = 0,
+	VP_MSIX_VQ_VECTOR = 1,
+};
+
+struct virtio_pci_vq_info
+{
+	/* the actual virtqueue */
+	struct virtqueue *vq;
+
+	/* the number of entries in the queue */
+	int num;
+
+	/* the index of the queue */
+	int queue_index;
+
+	/* the virtual address of the ring queue */
+	void *queue;
+
+	/* the list node for the virtqueues list */
+	struct list_head node;
+
+	/* MSI-X vector (or none) */
+	unsigned msix_vector;
+};
drivers/virtio/virtio_pci.c +1 -63

76 lines not on this page · raw message

drivers/virtio/virtio_pci_legacy.c +32 -88

271 lines not on this page · raw message

#90 lore raw

RFC 9/11 virtio_pci: share interrupt/notify handlers between legacy and modern.

In reply to Rusty Russell

If we make the legacy driver set up the ->notify and ->isr pointers in the struct virtio_pci_device structure, we can use them in common code (the positions have changed, but the semantics haven't).

---
 drivers/virtio/Makefile            |    4 -
 drivers/virtio/virtio_pci-common.c |   81 ++++++++++++++++++++++++++++++++
 drivers/virtio/virtio_pci-common.h |   34 +++++++++++++
 drivers/virtio/virtio_pci.c        |   87 ++---------------------------------
 drivers/virtio/virtio_pci_legacy.c |   91 ++++---------------------------------
 5 files changed, 135 insertions(+), 162 deletions(-)
drivers/virtio/Makefile +2 -2
@@ -1,6 +1,6 @@
 obj-$(CONFIG_VIRTIO) += virtio.o
 obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
 obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
-obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
-obj-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
+obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o virtio_pci-common.o
+obj-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o virtio_pci-common.o
 obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
drivers/virtio/virtio_pci-common.c new file +81 -0

81 lines not on this page · raw message

drivers/virtio/virtio_pci-common.h +34 -0
@@ -42,6 +42,12 @@ struct virtio_pci_device
 	bool per_vq_vectors;
 };
 
+/* Convert a generic virtio device to our structure */
+static inline struct virtio_pci_device *to_vp_device(struct virtio_device *vdev)
+{
+	return container_of(vdev, struct virtio_pci_device, vdev);
+}
+
 /* Constants for MSI-X */
 /* Use first vector for configuration changes, second and the rest for
  * virtqueues Thus, we need at least 2 vectors for MSI. */
@@ -70,3 +76,31 @@ struct virtio_pci_vq_info
 	/* MSI-X vector (or none) */
 	unsigned msix_vector;
 };
+
+/* the notify function used when creating a virt queue */
+void virtio_pci_notify(struct virtqueue *vq);
+/* Handle a configuration change: Tell driver if it wants to know. */
+irqreturn_t virtio_pci_config_changed(int irq, void *opaque);
+/* Notify all virtqueues on an interrupt. */
+irqreturn_t virtio_pci_vring_interrupt(int irq, void *opaque);
+/* Acknowledge, check for config or vq interrupt. */
+irqreturn_t virtio_pci_interrupt(int irq, void *opaque);
+
+/* Core of a config->find_vqs() implementation */
+int virtio_pci_find_vqs(struct virtio_pci_device *vp_dev,
+			__le16 __iomem *msix_config,
+			struct virtqueue *(setup_vq)(struct virtio_pci_device *,
+						     unsigned,
+						     void (*)(struct virtqueue*),
+						     const char *,
+						     u16 msix_vec),
+			void (*del_vq)(struct virtqueue *vq),
+			unsigned nvqs,
+			struct virtqueue *vqs[],
+			vq_callback_t *callbacks[],
+			const char *names[]);
+
+/* the core of a config->del_vqs() implementation */
+void virtio_pci_del_vqs(struct virtio_pci_device *vp_dev,
+			__le16 __iomem *msix_config,
+			void (*del_vq)(struct virtqueue *vq));
drivers/virtio/virtio_pci.c +6 -79

121 lines not on this page · raw message

drivers/virtio/virtio_pci_legacy.c +10 -79

130 lines not on this page · raw message

#91 lore raw

RFC 10/11 virtio_pci: share virtqueue setup/teardown between modern and legacy driver.

In reply to Rusty Russell

There's a great deal of work in setting up and disabling interrupts, particularly with MSI-X, which is generic. So we move most of the work out to helpers which take the location of the msix_config register, and setup_vq and del_vq functions.

---
 drivers/virtio/virtio_pci-common.c |  249 +++++++++++++++++++++++++++++++++++++
 drivers/virtio/virtio_pci-common.h |   19 ++
 drivers/virtio/virtio_pci.c        |  224 +--------------------------------
 drivers/virtio/virtio_pci_legacy.c |  229 ++--------------------------------
 4 files changed, 291 insertions(+), 430 deletions(-)
drivers/virtio/virtio_pci-common.c +249 -0
@@ -11,6 +11,7 @@
 #define VIRTIO_PCI_NO_LEGACY
 #include "virtio_pci-common.h"
 #include <linux/virtio_ring.h>
+#include <linux/interrupt.h>
 
 /* the notify function used when creating a virt queue */
 void virtio_pci_notify(struct virtqueue *vq)
@@ -79,3 +80,251 @@ irqreturn_t virtio_pci_interrupt(int irq
 
 	return virtio_pci_vring_interrupt(irq, opaque);
 }
+
+static void vp_free_vectors(struct virtio_device *vdev,
+			    __le16 __iomem *msix_config)
+{
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	int i;
+
+	if (vp_dev->intx_enabled) {
+		free_irq(vp_dev->pci_dev->irq, vp_dev);
+		vp_dev->intx_enabled = 0;
+	}
+
+	for (i = 0; i < vp_dev->msix_used_vectors; ++i)
+		free_irq(vp_dev->msix_entries[i].vector, vp_dev);
+
+	if (vp_dev->msix_enabled) {
+		/* Disable the vector used for configuration */
+		iowrite16(VIRTIO_MSI_NO_VECTOR, msix_config);
+		/* Flush the write out to device */
+		ioread16(msix_config);
+
+		pci_disable_msix(vp_dev->pci_dev);
+		vp_dev->msix_enabled = 0;
+		vp_dev->msix_vectors = 0;
+	}
+
+	vp_dev->msix_used_vectors = 0;
+	kfree(vp_dev->msix_names);
+	vp_dev->msix_names = NULL;
+	kfree(vp_dev->msix_entries);
+	vp_dev->msix_entries = NULL;
+}
+
+static int vp_request_msix_vectors(struct virtio_device *vdev,
+				   __le16 __iomem *msix_config,
+				   int nvectors, bool per_vq_vectors)
+{
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	const char *name = dev_name(&vp_dev->vdev.dev);
+	unsigned i, v;
+	int err = -ENOMEM;
+
+	vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries,
+				       GFP_KERNEL);
+	if (!vp_dev->msix_entries)
+		goto error;
+	vp_dev->msix_names = kmalloc(nvectors * sizeof *vp_dev->msix_names,
+				     GFP_KERNEL);
+	if (!vp_dev->msix_names)
+		goto error;
+
+	for (i = 0; i < nvectors; ++i)
+		vp_dev->msix_entries[i].entry = i;
+
+	/* pci_enable_msix returns positive if we can't get this many. */
+	err = pci_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries, nvectors);
+	if (err > 0)
+		err = -ENOSPC;
+	if (err)
+		goto error;
+	vp_dev->msix_vectors = nvectors;
+	vp_dev->msix_enabled = 1;
+
+	/* Set the vector used for configuration */
+	v = vp_dev->msix_used_vectors;
+	snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
+		 "%s-config", name);
+	err = request_irq(vp_dev->msix_entries[v].vector,
+			  virtio_pci_config_changed, 0, vp_dev->msix_names[v],
+			  vp_dev);
+	if (err)
+		goto error;
+	++vp_dev->msix_used_vectors;
+
+	iowrite16(v, msix_config);
+	/* Verify we had enough resources to assign the vector */
+	v = ioread16(msix_config);
+	if (v == VIRTIO_MSI_NO_VECTOR) {
+		err = -EBUSY;
+		goto error;
+	}
+
+	if (!per_vq_vectors) {
+		/* Shared vector for all VQs */
+		v = vp_dev->msix_used_vectors;
+		snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
+			 "%s-virtqueues", name);
+		err = request_irq(vp_dev->msix_entries[v].vector,
+				  virtio_pci_vring_interrupt, 0,
+				  vp_dev->msix_names[v], vp_dev);
+		if (err)
+			goto error;
+		++vp_dev->msix_used_vectors;
+	}
+	return 0;
+error:
+	vp_free_vectors(vdev, msix_config);
+	return err;
+}
+
+static int vp_request_intx(struct virtio_device *vdev)
+{
+	int err;
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+
+	err = request_irq(vp_dev->pci_dev->irq, virtio_pci_interrupt,
+			  IRQF_SHARED, dev_name(&vdev->dev), vp_dev);
+	if (!err)
+		vp_dev->intx_enabled = 1;
+	return err;
+}
+
+static int vp_try_to_find_vqs(struct virtio_device *vdev,
+			      __le16 __iomem *msix_config,
+			      struct virtqueue *(setup_vq)(struct virtio_device*,
+							   unsigned,
+							   void (*)(struct
+								    virtqueue *),
+							   const char *,
+							   u16 msix_vec),
+			      void (*del_vq)(struct virtqueue *vq),
+			      unsigned nvqs,
+			      struct virtqueue *vqs[],
+			      vq_callback_t *callbacks[],
+			      const char *names[],
+			      bool use_msix,
+			      bool per_vq_vectors)
+{
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	u16 msix_vec;
+	int i, err, nvectors, allocated_vectors;
+
+	if (!use_msix) {
+		/* Old style: one normal interrupt for change and all vqs. */
+		err = vp_request_intx(vdev);
+		if (err)
+			goto error_request;
+	} else {
+		if (per_vq_vectors) {
+			/* Best option: one for change interrupt, one per vq. */
+			nvectors = 1;
+			for (i = 0; i < nvqs; ++i)
+				if (callbacks[i])
+					++nvectors;
+		} else {
+			/* Second best: one for change, shared for all vqs. */
+			nvectors = 2;
+		}
+
+		err = vp_request_msix_vectors(vdev, msix_config,
+					      nvectors, per_vq_vectors);
+		if (err)
+			goto error_request;
+	}
+
+	vp_dev->per_vq_vectors = per_vq_vectors;
+	allocated_vectors = vp_dev->msix_used_vectors;
+	for (i = 0; i < nvqs; ++i) {
+		if (!callbacks[i] || !vp_dev->msix_enabled)
+			msix_vec = VIRTIO_MSI_NO_VECTOR;
+		else if (vp_dev->per_vq_vectors)
+			msix_vec = allocated_vectors++;
+		else
+			msix_vec = VP_MSIX_VQ_VECTOR;
+		vqs[i] = setup_vq(vdev, i, callbacks[i], names[i], msix_vec);
+		if (IS_ERR(vqs[i])) {
+			err = PTR_ERR(vqs[i]);
+			goto error_find;
+		}
+
+		if (!vp_dev->per_vq_vectors || msix_vec == VIRTIO_MSI_NO_VECTOR)
+			continue;
+
+		/* allocate per-vq irq if available and necessary */
+		snprintf(vp_dev->msix_names[msix_vec],
+			 sizeof *vp_dev->msix_names,
+			 "%s-%s",
+			 dev_name(&vp_dev->vdev.dev), names[i]);
+		err = request_irq(vp_dev->msix_entries[msix_vec].vector,
+				  vring_interrupt, 0,
+				  vp_dev->msix_names[msix_vec],
+				  vqs[i]);
+		if (err) {
+			del_vq(vqs[i]);
+			goto error_find;
+		}
+	}
+	return 0;
+
+error_find:
+	virtio_pci_del_vqs(vdev, msix_config, del_vq);
+
+error_request:
+	return err;
+}
+
+/* the config->find_vqs() implementation */
+int virtio_pci_find_vqs(struct virtio_device *vdev,
+			__le16 __iomem *msix_config,
+			struct virtqueue *(setup_vq)(struct virtio_device *,
+						     unsigned,
+						     void (*)(struct virtqueue*),
+						     const char *,
+						     u16 msix_vec),
+			void (*del_vq)(struct virtqueue *vq),
+			unsigned nvqs,
+			struct virtqueue *vqs[],
+			vq_callback_t *callbacks[],
+			const char *names[])
+{
+	int err;
+
+	/* Try MSI-X with one vector per queue. */
+	err = vp_try_to_find_vqs(vdev, msix_config, setup_vq, del_vq,
+				 nvqs, vqs, callbacks, names, true, true);
+	if (!err)
+		return 0;
+	/* Fallback: MSI-X with one vector for config, one shared for queues. */
+	err = vp_try_to_find_vqs(vdev, msix_config, setup_vq, del_vq,
+				 nvqs, vqs, callbacks, names, true, false);
+	if (!err)
+		return 0;
+	/* Finally fall back to regular interrupts. */
+	return vp_try_to_find_vqs(vdev, msix_config, setup_vq, del_vq,
+				  nvqs, vqs, callbacks, names, false, false);
+}
+
+/* the core of a config->del_vqs() implementation */
+void virtio_pci_del_vqs(struct virtio_device *vdev,
+			__le16 __iomem *msix_config,
+			void (*del_vq)(struct virtqueue *vq))
+{
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	struct virtqueue *vq, *n;
+	struct virtio_pci_vq_info *info;
+
+	list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
+		info = vq->priv;
+		if (vp_dev->per_vq_vectors &&
+			info->msix_vector != VIRTIO_MSI_NO_VECTOR)
+			free_irq(vp_dev->msix_entries[info->msix_vector].vector,
+				 vq);
+		del_vq(vq);
+	}
+	vp_dev->per_vq_vectors = false;
+
+	vp_free_vectors(vdev, msix_config);
+}
drivers/virtio/virtio_pci-common.h +19 -0
@@ -87,6 +87,25 @@ irqreturn_t virtio_pci_vring_interrupt(i
 irqreturn_t virtio_pci_interrupt(int irq, void *opaque);
 
 /* Core of a config->find_vqs() implementation */
+int virtio_pci_find_vqs(struct virtio_device *vdev,
+			__le16 __iomem *msix_config,
+			struct virtqueue *(setup_vq)(struct virtio_device *,
+						     unsigned,
+						     void (*)(struct virtqueue*),
+						     const char *,
+						     u16 msix_vec),
+			void (*del_vq)(struct virtqueue *vq),
+			unsigned nvqs,
+			struct virtqueue *vqs[],
+			vq_callback_t *callbacks[],
+			const char *names[]);
+
+/* the core of a config->del_vqs() implementation */
+void virtio_pci_del_vqs(struct virtio_device *vdev,
+			__le16 __iomem *msix_config,
+			void (*del_vq)(struct virtqueue *vq));
+
+/* Core of a config->find_vqs() implementation */
 int virtio_pci_find_vqs(struct virtio_pci_device *vp_dev,
 			__le16 __iomem *msix_config,
 			struct virtqueue *(setup_vq)(struct virtio_pci_device *,
drivers/virtio/virtio_pci.c +9 -215

240 lines not on this page · raw message

drivers/virtio/virtio_pci_legacy.c +14 -215

255 lines not on this page · raw message

#92 lore raw

RFC 11/11 virtio_pci: simplify common helpers.

In reply to Rusty Russell

Our helpers can take a virtio_pci_device, rather than converting from a virtio_device all the time. They couldn't do this when they were called from the common virtio code, but now we wrap them anyway, it simplifies things.

---
 drivers/virtio/virtio_pci-common.c |   54 ++++++++++++++++---------------------
 drivers/virtio/virtio_pci-common.h |    6 ++--
 drivers/virtio/virtio_pci.c        |   10 +++---
 drivers/virtio/virtio_pci_legacy.c |    9 ++----
 4 files changed, 36 insertions(+), 43 deletions(-)
drivers/virtio/virtio_pci-common.c +24 -30
@@ -81,10 +81,9 @@ irqreturn_t virtio_pci_interrupt(int irq
 	return virtio_pci_vring_interrupt(irq, opaque);
 }
 
-static void vp_free_vectors(struct virtio_device *vdev,
+static void vp_free_vectors(struct virtio_pci_device *vp_dev,
 			    __le16 __iomem *msix_config)
 {
-	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 	int i;
 
 	if (vp_dev->intx_enabled) {
@@ -113,11 +112,10 @@ static void vp_free_vectors(struct virti
 	vp_dev->msix_entries = NULL;
 }
 
-static int vp_request_msix_vectors(struct virtio_device *vdev,
+static int vp_request_msix_vectors(struct virtio_pci_device *vp_dev,
 				   __le16 __iomem *msix_config,
 				   int nvectors, bool per_vq_vectors)
 {
-	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 	const char *name = dev_name(&vp_dev->vdev.dev);
 	unsigned i, v;
 	int err = -ENOMEM;
@@ -176,30 +174,28 @@ static int vp_request_msix_vectors(struc
 	}
 	return 0;
 error:
-	vp_free_vectors(vdev, msix_config);
+	vp_free_vectors(vp_dev, msix_config);
 	return err;
 }
 
-static int vp_request_intx(struct virtio_device *vdev)
+static int vp_request_intx(struct virtio_pci_device *vp_dev)
 {
 	int err;
-	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-
 	err = request_irq(vp_dev->pci_dev->irq, virtio_pci_interrupt,
-			  IRQF_SHARED, dev_name(&vdev->dev), vp_dev);
+			  IRQF_SHARED, dev_name(&vp_dev->vdev.dev), vp_dev);
 	if (!err)
 		vp_dev->intx_enabled = 1;
 	return err;
 }
 
-static int vp_try_to_find_vqs(struct virtio_device *vdev,
+static int vp_try_to_find_vqs(struct virtio_pci_device *vp_dev,
 			      __le16 __iomem *msix_config,
-			      struct virtqueue *(setup_vq)(struct virtio_device*,
-							   unsigned,
-							   void (*)(struct
-								    virtqueue *),
-							   const char *,
-							   u16 msix_vec),
+			      struct virtqueue *(setup_vq)
+			      (struct virtio_pci_device *,
+			       unsigned,
+			       void (*)(struct virtqueue *),
+			       const char *,
+			       u16 msix_vec),
 			      void (*del_vq)(struct virtqueue *vq),
 			      unsigned nvqs,
 			      struct virtqueue *vqs[],
@@ -208,13 +204,12 @@ static int vp_try_to_find_vqs(struct vir
 			      bool use_msix,
 			      bool per_vq_vectors)
 {
-	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 	u16 msix_vec;
 	int i, err, nvectors, allocated_vectors;
 
 	if (!use_msix) {
 		/* Old style: one normal interrupt for change and all vqs. */
-		err = vp_request_intx(vdev);
+		err = vp_request_intx(vp_dev);
 		if (err)
 			goto error_request;
 	} else {
@@ -229,7 +224,7 @@ static int vp_try_to_find_vqs(struct vir
 			nvectors = 2;
 		}
 
-		err = vp_request_msix_vectors(vdev, msix_config,
+		err = vp_request_msix_vectors(vp_dev, msix_config,
 					      nvectors, per_vq_vectors);
 		if (err)
 			goto error_request;
@@ -244,7 +239,7 @@ static int vp_try_to_find_vqs(struct vir
 			msix_vec = allocated_vectors++;
 		else
 			msix_vec = VP_MSIX_VQ_VECTOR;
-		vqs[i] = setup_vq(vdev, i, callbacks[i], names[i], msix_vec);
+		vqs[i] = setup_vq(vp_dev, i, callbacks[i], names[i], msix_vec);
 		if (IS_ERR(vqs[i])) {
 			err = PTR_ERR(vqs[i]);
 			goto error_find;
@@ -270,16 +265,16 @@ static int vp_try_to_find_vqs(struct vir
 	return 0;
 
 error_find:
-	virtio_pci_del_vqs(vdev, msix_config, del_vq);
+	virtio_pci_del_vqs(vp_dev, msix_config, del_vq);
 
 error_request:
 	return err;
 }
 
 /* the config->find_vqs() implementation */
-int virtio_pci_find_vqs(struct virtio_device *vdev,
+int virtio_pci_find_vqs(struct virtio_pci_device *vp_dev,
 			__le16 __iomem *msix_config,
-			struct virtqueue *(setup_vq)(struct virtio_device *,
+			struct virtqueue *(setup_vq)(struct virtio_pci_device *,
 						     unsigned,
 						     void (*)(struct virtqueue*),
 						     const char *,
@@ -293,30 +288,29 @@ int virtio_pci_find_vqs(struct virtio_de
 	int err;
 
 	/* Try MSI-X with one vector per queue. */
-	err = vp_try_to_find_vqs(vdev, msix_config, setup_vq, del_vq,
+	err = vp_try_to_find_vqs(vp_dev, msix_config, setup_vq, del_vq,
 				 nvqs, vqs, callbacks, names, true, true);
 	if (!err)
 		return 0;
 	/* Fallback: MSI-X with one vector for config, one shared for queues. */
-	err = vp_try_to_find_vqs(vdev, msix_config, setup_vq, del_vq,
+	err = vp_try_to_find_vqs(vp_dev, msix_config, setup_vq, del_vq,
 				 nvqs, vqs, callbacks, names, true, false);
 	if (!err)
 		return 0;
 	/* Finally fall back to regular interrupts. */
-	return vp_try_to_find_vqs(vdev, msix_config, setup_vq, del_vq,
+	return vp_try_to_find_vqs(vp_dev, msix_config, setup_vq, del_vq,
 				  nvqs, vqs, callbacks, names, false, false);
 }
 
 /* the core of a config->del_vqs() implementation */
-void virtio_pci_del_vqs(struct virtio_device *vdev,
+void virtio_pci_del_vqs(struct virtio_pci_device *vp_dev,
 			__le16 __iomem *msix_config,
 			void (*del_vq)(struct virtqueue *vq))
 {
-	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 	struct virtqueue *vq, *n;
 	struct virtio_pci_vq_info *info;
 
-	list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
+	list_for_each_entry_safe(vq, n, &vp_dev->vdev.vqs, list) {
 		info = vq->priv;
 		if (vp_dev->per_vq_vectors &&
 			info->msix_vector != VIRTIO_MSI_NO_VECTOR)
@@ -326,5 +320,5 @@ void virtio_pci_del_vqs(struct virtio_de
 	}
 	vp_dev->per_vq_vectors = false;
 
-	vp_free_vectors(vdev, msix_config);
+	vp_free_vectors(vp_dev, msix_config);
 }
drivers/virtio/virtio_pci-common.h +3 -3
@@ -87,9 +87,9 @@ irqreturn_t virtio_pci_vring_interrupt(i
 irqreturn_t virtio_pci_interrupt(int irq, void *opaque);
 
 /* Core of a config->find_vqs() implementation */
-int virtio_pci_find_vqs(struct virtio_device *vdev,
+int virtio_pci_find_vqs(struct virtio_pci_device *vp_dev,
 			__le16 __iomem *msix_config,
-			struct virtqueue *(setup_vq)(struct virtio_device *,
+			struct virtqueue *(setup_vq)(struct virtio_pci_device *,
 						     unsigned,
 						     void (*)(struct virtqueue*),
 						     const char *,
@@ -101,7 +101,7 @@ int virtio_pci_find_vqs(struct virtio_de
 			const char *names[]);
 
 /* the core of a config->del_vqs() implementation */
-void virtio_pci_del_vqs(struct virtio_device *vdev,
+void virtio_pci_del_vqs(struct virtio_pci_device *vp_dev,
 			__le16 __iomem *msix_config,
 			void (*del_vq)(struct virtqueue *vq));
 
drivers/virtio/virtio_pci.c +5 -5
@@ -163,12 +163,12 @@ static void *alloc_virtqueue_pages(u16 *
 	return NULL;
 }
 
-static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
+static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
+				  unsigned index,
 				  void (*callback)(struct virtqueue *vq),
 				  const char *name,
 				  u16 msix_vec)
 {
-	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 	struct virtio_pci_vq_info *info;
 	struct virtqueue *vq;
 	u16 num;
@@ -213,7 +213,7 @@ static struct virtqueue *setup_vq(struct
 
 	/* create the vring */
 	vq = vring_new_virtqueue(info->num, VIRTIO_PCI_ALIGN,
-				 vdev, info->queue, virtio_pci_notify,
+				 &vp_dev->vdev, info->queue, virtio_pci_notify,
 				 callback, name);
 	if (!vq) {
 		err = -ENOMEM;
@@ -294,7 +294,7 @@ static void vp_del_vq(struct virtqueue *
 static void vp_del_vqs(struct virtio_device *vdev)
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-	virtio_pci_del_vqs(vdev, &vp_dev->common->msix_config, vp_del_vq);
+	virtio_pci_del_vqs(vp_dev, &vp_dev->common->msix_config, vp_del_vq);
 }
 
 static int vp_find_vqs(struct virtio_device *vdev,
@@ -304,7 +304,7 @@ static int vp_find_vqs(struct virtio_dev
 			const char *names[])
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-	return virtio_pci_find_vqs(vdev, &vp_dev->common->msix_config,
+	return virtio_pci_find_vqs(vp_dev, &vp_dev->common->msix_config,
 				   setup_vq, vp_del_vq,
 				   nvqs, vqs, callbacks, names);
 }
drivers/virtio/virtio_pci_legacy.c +4 -5
@@ -134,13 +134,12 @@ static void vp_reset(struct virtio_devic
 	vp_synchronize_vectors(vdev);
 }
 
-static struct virtqueue *setup_legacy_vq(struct virtio_device *vdev,
+static struct virtqueue *setup_legacy_vq(struct virtio_pci_device *vp_dev,
 					 unsigned index,
 					 void (*callback)(struct virtqueue *vq),
 					 const char *name,
 					 u16 msix_vec)
 {
-	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 	struct virtio_pci_vq_info *info;
 	struct virtqueue *vq;
 	unsigned long flags, size;
@@ -178,7 +177,7 @@ static struct virtqueue *setup_legacy_vq
 
 	/* create the vring */
 	vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN,
-				 vdev, info->queue, virtio_pci_notify,
+				 &vp_dev->vdev, info->queue, virtio_pci_notify,
 				 callback, name);
 	if (!vq) {
 		err = -ENOMEM;
@@ -251,7 +250,7 @@ static void vp_del_vqs(struct virtio_dev
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 
-	return virtio_pci_del_vqs(vdev, vp_dev->legacy +
+	return virtio_pci_del_vqs(vp_dev, vp_dev->legacy +
 				  VIRTIO_MSI_LEGACY_CONFIG_VECTOR,
 				  del_legacy_vq);
 }
@@ -264,7 +263,7 @@ static int vp_find_vqs(struct virtio_dev
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 
-	return virtio_pci_find_vqs(vdev, vp_dev->legacy +
+	return virtio_pci_find_vqs(vp_dev, vp_dev->legacy +
 				   VIRTIO_MSI_LEGACY_CONFIG_VECTOR,
 				   setup_legacy_vq, del_legacy_vq,
 				   nvqs, vqs, callbacks, names);
#93 lore raw
In reply to Rusty Russell
Quoted 8 lines · Rusty Russell

Here's the patch series I ended up with. I haven't coded up the QEMU side yet, so no idea if the new driver works.

Questions:
(1) Do we win from separating ISR, NOTIFY and COMMON?
(2) I used a "u8 bar"; should I use a bir and pack it instead? BIR seems a little obscure (noone else in the kernel source seems to refer to it).

I started implementing it for KVM tools, when I noticed a strange thing:
my vq creating was failing because the driver was reading a value other than 0 from the address field of a new vq, and failing.

I've added simple prints in the usermode code, and saw the following ordering:

1. queue select vq 0
2. queue read address (returns 0 - new vq)
3. queue write address (good address of vq)
4. queue read address (returns !=0, fails)
4. queue select vq 1

From that I understood that the ordering is wrong, the driver was trying to read address before selecting the correct vq.

At that point, I've added simple prints to the driver. Initially it looked as follows:

	iowrite16(index, &vp_dev->common->queue_select);

	switch (ioread64(&vp_dev->common->queue_address)) {
		[...]
	};

So I added prints before the iowrite16() and after the ioread64(), and saw that while the driver prints were ordered, the device ones weren't:

	[    1.264052] before iowrite index=1
	kvmtool: net returning pfn (vq=0): 310706176
	kvmtool: queue selected: 1
	[    1.264890] after ioread index=1

Suspecting that something was wrong with ordering, I've added a print between the iowrite and the ioread, and it finally started working well.

Which leads me to the question: Are MMIO vs MMIO reads/writes not ordered?

Signature · 1 line
Sasha.
#94 lore raw
In reply to Rusty Russell
Quoted 8 lines · Rusty Russell

Here's the patch series I ended up with. I haven't coded up the QEMU side yet, so no idea if the new driver works.

Questions:
(1) Do we win from separating ISR, NOTIFY and COMMON?
(2) I used a "u8 bar"; should I use a bir and pack it instead? BIR seems a little obscure (noone else in the kernel source seems to refer to it).

I started implementing it for KVM tools, when I noticed a strange thing:
my vq creating was failing because the driver was reading a value other than 0 from the address field of a new vq, and failing.

I've added simple prints in the usermode code, and saw the following ordering:

1. queue select vq 0
2. queue read address (returns 0 - new vq)
3. queue write address (good address of vq)
4. queue read address (returns !=0, fails)
4. queue select vq 1

Quoted 1 line

From that I understood that the ordering is wrong, the driver was trying

to read address before selecting the correct vq.

At that point, I've added simple prints to the driver. Initially it looked as follows:

	iowrite16(index, &vp_dev->common->queue_select);

	switch (ioread64(&vp_dev->common->queue_address)) {
		[...]
	};

So I added prints before the iowrite16() and after the ioread64(), and saw that while the driver prints were ordered, the device ones weren't:

	[    1.264052] before iowrite index=1
	kvmtool: net returning pfn (vq=0): 310706176
	kvmtool: queue selected: 1
	[    1.264890] after ioread index=1

Suspecting that something was wrong with ordering, I've added a print between the iowrite and the ioread, and it finally started working well.

Which leads me to the question: Are MMIO vs MMIO reads/writes not ordered?

Signature · 1 line
Sasha.
#95 lore raw
Quoted 2 lines · Sasha Levin

Which leads me to the question: Are MMIO vs MMIO reads/writes not ordered?

That seems really odd, especially being repeatable.

BTW, that's an address, not a pfn now.

Cheers,
Rusty.

#96 lore raw
Quoted 5 lines · Rusty Russell
Quoted 2 lines · Sasha Levin

Which leads me to the question: Are MMIO vs MMIO reads/writes not ordered?

That seems really odd, especially being repeatable.

Happens every single time. Can't be a coincidence.

I even went into paranoia mode and made sure that both IO requests come from the same vcpu.

Another weird thing I've noticed is that mb() doesn't fix it, while if I replace the mb() with a printk() it works well.

Quoted 1 line

BTW, that's an address, not a pfn now.

Fixed :)

Signature · 1 line
Sasha.
#97 lore raw
In reply to Sasha Levin
Quoted 43 lines · Sasha Levin
Quoted 8 lines · Rusty Russell

Here's the patch series I ended up with. I haven't coded up the QEMU side yet, so no idea if the new driver works.

Questions:
(1) Do we win from separating ISR, NOTIFY and COMMON?
(2) I used a "u8 bar"; should I use a bir and pack it instead? BIR seems a little obscure (noone else in the kernel source seems to refer to it).

I started implementing it for KVM tools, when I noticed a strange thing:
my vq creating was failing because the driver was reading a value other than 0 from the address field of a new vq, and failing.

I've added simple prints in the usermode code, and saw the following ordering:

1. queue select vq 0
2. queue read address (returns 0 - new vq)
3. queue write address (good address of vq)
4. queue read address (returns !=0, fails)
4. queue select vq 1

From that I understood that the ordering is wrong, the driver was trying to read address before selecting the correct vq.

At that point, I've added simple prints to the driver. Initially it looked as follows:

	iowrite16(index, &vp_dev->common->queue_select);

	switch (ioread64(&vp_dev->common->queue_address)) {
		[...]
	};

So I added prints before the iowrite16() and after the ioread64(), and saw that while the driver prints were ordered, the device ones weren't:

	[    1.264052] before iowrite index=1
	kvmtool: net returning pfn (vq=0): 310706176
	kvmtool: queue selected: 1
	[    1.264890] after ioread index=1
Quoted 5 lines

Suspecting that something was wrong with ordering, I've added a print between the iowrite and the ioread, and it finally started working well.

Which leads me to the question: Are MMIO vs MMIO reads/writes not ordered?

mmios are strictly ordered.

Perhaps your printfs are reordered by buffering? Are they from different threads? Are you using coalesced mmio (which is still strictly ordered, if used correctly)?

Signature · 1 line
error compiling committee.c: too many arguments to function
#98 lore raw
Quoted 5 lines · Avi Kivity

mmios are strictly ordered.

Perhaps your printfs are reordered by buffering? Are they from different threads? Are you using coalesced mmio (which is still strictly ordered, if used correctly)?

I print the queue_selector and queue_address in the printfs, even if printfs were reordered they would be printing the data right, unlike they do now. It's the data in the printfs that matters, not their order.

Same vcpu thread with both accesses.

Not using coalesced mmio.

Signature · 1 line
Sasha.
#99 lore raw
Quoted 7 lines · Sasha Levin
Quoted 5 lines · Avi Kivity

mmios are strictly ordered.

Perhaps your printfs are reordered by buffering? Are they from different threads? Are you using coalesced mmio (which is still strictly ordered, if used correctly)?

Quoted 7 lines

I print the queue_selector and queue_address in the printfs, even if printfs were reordered they would be printing the data right, unlike they do now. It's the data in the printfs that matters, not their order.

Same vcpu thread with both accesses.

Not using coalesced mmio.

Not sure why this would matter, but is the BAR a prefetcheable one? Rusty's patch uses pci_iomap which maps a prefetcheable BAR as cacheable.

Quoted 3 lines
Signature · 1 line
Sasha.
#100 lore raw
Quoted 12 lines · Michael S. Tsirkin
Quoted 11 lines · Sasha Levin
Quoted 5 lines · Avi Kivity

mmios are strictly ordered.

Perhaps your printfs are reordered by buffering? Are they from different threads? Are you using coalesced mmio (which is still strictly ordered, if used correctly)?

I print the queue_selector and queue_address in the printfs, even if printfs were reordered they would be printing the data right, unlike they do now. It's the data in the printfs that matters, not their order.

Quoted 7 lines
Quoted 3 lines

Same vcpu thread with both accesses.

Not using coalesced mmio.

Not sure why this would matter, but is the BAR a prefetcheable one? Rusty's patch uses pci_iomap which maps a prefetcheable BAR as cacheable.

Wasn't defined as prefetchable, but I'm seeing same thing with or without it.

Signature · 1 line
Sasha.
#101 lore raw
In reply to Michael S. Tsirkin
Quoted 12 lines · Michael S. Tsirkin
Quoted 11 lines · Sasha Levin
Quoted 5 lines · Avi Kivity

mmios are strictly ordered.

Perhaps your printfs are reordered by buffering? Are they from different threads? Are you using coalesced mmio (which is still strictly ordered, if used correctly)?

I print the queue_selector and queue_address in the printfs, even if printfs were reordered they would be printing the data right, unlike they do now. It's the data in the printfs that matters, not their order.

Quoted 7 lines
Quoted 3 lines

Same vcpu thread with both accesses.

Not using coalesced mmio.

Not sure why this would matter, but is the BAR a prefetcheable one? Rusty's patch uses pci_iomap which maps a prefetcheable BAR as cacheable.

Wasn't defined as prefetchable, but I'm seeing same thing with or without it.

Signature · 1 line
Sasha.
#102 lore raw
In reply to Sasha Levin
Quoted 43 lines · Sasha Levin
Quoted 8 lines · Rusty Russell

Here's the patch series I ended up with. I haven't coded up the QEMU side yet, so no idea if the new driver works.

Questions:
(1) Do we win from separating ISR, NOTIFY and COMMON?
(2) I used a "u8 bar"; should I use a bir and pack it instead? BIR seems a little obscure (noone else in the kernel source seems to refer to it).

I started implementing it for KVM tools, when I noticed a strange thing:
my vq creating was failing because the driver was reading a value other than 0 from the address field of a new vq, and failing.

I've added simple prints in the usermode code, and saw the following ordering:

1. queue select vq 0
2. queue read address (returns 0 - new vq)
3. queue write address (good address of vq)
4. queue read address (returns !=0, fails)
4. queue select vq 1

Quoted 1 line

From that I understood that the ordering is wrong, the driver was trying

to read address before selecting the correct vq.

At that point, I've added simple prints to the driver. Initially it looked as follows:

	iowrite16(index, &vp_dev->common->queue_select);

	switch (ioread64(&vp_dev->common->queue_address)) {
		[...]
	};

So I added prints before the iowrite16() and after the ioread64(), and saw that while the driver prints were ordered, the device ones weren't:

	[    1.264052] before iowrite index=1
	kvmtool: net returning pfn (vq=0): 310706176
	kvmtool: queue selected: 1
	[    1.264890] after ioread index=1
Quoted 5 lines

Suspecting that something was wrong with ordering, I've added a print between the iowrite and the ioread, and it finally started working well.

Which leads me to the question: Are MMIO vs MMIO reads/writes not ordered?

First, I'd like to answer your questions from the PCI side. Look for PCI rules in the PCI spec.
You will notices that a write is required to be able to
pass a read request. It might also pass read completion.
A read request will not pass a write request.
There's more or less no ordering between different types of transactions (memory versus io/configuration).

That's wrt to the question you asked.

But this is not your setup: you have a single vcpu so you will not initiate a write (select vq) until you get a read completion.

So what you are really describing is this setup: guest reads a value, gets the response, then writes out another one, and kvm tool reports the write before the read.

Quoted 3 lines
Signature · 1 line
Sasha.
#103 lore raw
Quoted 10 lines · Michael S. Tsirkin

First, I'd like to answer your questions from the PCI side. Look for PCI rules in the PCI spec.
You will notices that a write is required to be able to
pass a read request. It might also pass read completion.
A read request will not pass a write request.
There's more or less no ordering between different types of transactions (memory versus io/configuration).

That's wrt to the question you asked.

Quoted 7 lines

But this is not your setup: you have a single vcpu so you will not initiate a write (select vq) until you get a read completion.

So what you are really describing is this setup: guest reads a value, gets the response, then writes out another one, and kvm tool reports the write before the read.

No, it's exactly the opposite. Guest writes a value first and then reads one (writes queue_select and reads queue_address) and kvm tool reporting the read before the write.

I must add here that the kvm tool doesn't do anything fancy with simple IO/MMIO. Theres no thread games or anything similar there. The vcpu thread is doing all the IO/MMIO work.

Signature · 1 line
Sasha.
#104 lore raw
In reply to Michael S. Tsirkin
Quoted 10 lines · Michael S. Tsirkin

First, I'd like to answer your questions from the PCI side. Look for PCI rules in the PCI spec.
You will notices that a write is required to be able to
pass a read request. It might also pass read completion.
A read request will not pass a write request.
There's more or less no ordering between different types of transactions (memory versus io/configuration).

That's wrt to the question you asked.

Quoted 7 lines

But this is not your setup: you have a single vcpu so you will not initiate a write (select vq) until you get a read completion.

So what you are really describing is this setup: guest reads a value, gets the response, then writes out another one, and kvm tool reports the write before the read.

No, it's exactly the opposite. Guest writes a value first and then reads one (writes queue_select and reads queue_address) and kvm tool reporting the read before the write.

I must add here that the kvm tool doesn't do anything fancy with simple IO/MMIO. Theres no thread games or anything similar there. The vcpu thread is doing all the IO/MMIO work.

Signature · 1 line
Sasha.