Skip to contentSkip to conversation

virtio: Clean up scatterlists and use the DMA API

PATCH In review Andy Lutomirski 8 participants 28 messages Aug 26, 2014 – Sep 1, 2014

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

cover virtio: Clean up scatterlists and use the DMA API

This fixes virtio on Xen guests as well as on any other platform on which physical addresses don't match bus addresses.

This can be tested with:

    virtme-run --xen xen --kimg arch/x86/boot/bzImage --console

using virtme from here:

https://git.kernel.org/cgit/utils/kernel/virtme/virtme.git

Without these patches, the guest hangs forever. With these patches, everything works.

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

Andy Lutomirski (3):
virtio_ring: Remove sg_next indirection
virtio_ring: Use DMA APIs
virtio_pci: Use the DMA API for virtqueues

 drivers/virtio/Kconfig       |   1 +
 drivers/virtio/virtio_pci.c  |  25 ++++++--
 drivers/virtio/virtio_ring.c | 150 ++++++++++++++++++++++++++++++-------------
 3 files changed, 125 insertions(+), 51 deletions(-)
Signature · 1 line
1.9.3
#1 lore raw

PATCH 1/3 virtio_ring: Remove sg_next indirection

The only unusual thing about virtio's use of scatterlists is that two of the APIs accept scatterlists that might not be terminated. Using function pointers to handle this case is overkill; for_each_sg can do it.

There's a small subtlely here: for_each_sg assumes that the provided count is correct, but, because of the way that virtio_ring handles multiple scatterlists at once, the count is only an upper bound if there is more than one scatterlist. This is easily solved by checking the sg pointer for NULL on each iteration.

---
 drivers/virtio/virtio_ring.c | 46 +++++++++++++++++---------------------------
 1 file changed, 18 insertions(+), 28 deletions(-)
drivers/virtio/virtio_ring.c +18 -28
index 4d08f45a9c29..d356a701c9c2 100644
@@ -99,25 +99,9 @@ struct vring_virtqueue
 
 #define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq)
 
-static inline struct scatterlist *sg_next_chained(struct scatterlist *sg,
-						  unsigned int *count)
-{
-	return sg_next(sg);
-}
-
-static inline struct scatterlist *sg_next_arr(struct scatterlist *sg,
-					      unsigned int *count)
-{
-	if (--(*count) == 0)
-		return NULL;
-	return sg + 1;
-}
-
 /* Set up an indirect table of descriptors and add it to the queue. */
 static inline int vring_add_indirect(struct vring_virtqueue *vq,
 				     struct scatterlist *sgs[],
-				     struct scatterlist *(*next)
-				       (struct scatterlist *, unsigned int *),
 				     unsigned int total_sg,
 				     unsigned int total_out,
 				     unsigned int total_in,
@@ -128,7 +112,7 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
 	struct vring_desc *desc;
 	unsigned head;
 	struct scatterlist *sg;
-	int i, n;
+	int i, j, n;
 
 	/*
 	 * We require lowmem mappings for the descriptors because
@@ -144,7 +128,9 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
 	/* Transfer entries from the sg lists into the indirect page */
 	i = 0;
 	for (n = 0; n < out_sgs; n++) {
-		for (sg = sgs[n]; sg; sg = next(sg, &total_out)) {
+		for_each_sg(sgs[n], sg, total_out, j) {
+			if (!sg)
+				break;
 			desc[i].flags = VRING_DESC_F_NEXT;
 			desc[i].addr = sg_phys(sg);
 			desc[i].len = sg->length;
@@ -153,7 +139,9 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
 		}
 	}
 	for (; n < (out_sgs + in_sgs); n++) {
-		for (sg = sgs[n]; sg; sg = next(sg, &total_in)) {
+		for_each_sg(sgs[n], sg, total_in, j) {
+			if (!sg)
+				break;
 			desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
 			desc[i].addr = sg_phys(sg);
 			desc[i].len = sg->length;
@@ -186,8 +174,6 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
 
 static inline int virtqueue_add(struct virtqueue *_vq,
 				struct scatterlist *sgs[],
-				struct scatterlist *(*next)
-				  (struct scatterlist *, unsigned int *),
 				unsigned int total_out,
 				unsigned int total_in,
 				unsigned int out_sgs,
@@ -197,7 +183,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 {
 	struct vring_virtqueue *vq = to_vvq(_vq);
 	struct scatterlist *sg;
-	unsigned int i, n, avail, uninitialized_var(prev), total_sg;
+	unsigned int i, j, n, avail, uninitialized_var(prev), total_sg;
 	int head;
 
 	START_USE(vq);
@@ -227,7 +213,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 	/* If the host supports indirect descriptor tables, and we have multiple
 	 * buffers, then go indirect. FIXME: tune this threshold */
 	if (vq->indirect && total_sg > 1 && vq->vq.num_free) {
-		head = vring_add_indirect(vq, sgs, next, total_sg, total_out,
+		head = vring_add_indirect(vq, sgs, total_sg, total_out,
 					  total_in,
 					  out_sgs, in_sgs, gfp);
 		if (likely(head >= 0))
@@ -254,7 +240,9 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 
 	head = i = vq->free_head;
 	for (n = 0; n < out_sgs; n++) {
-		for (sg = sgs[n]; sg; sg = next(sg, &total_out)) {
+		for_each_sg(sgs[n], sg, total_out, j) {
+			if (!sg)
+				break;
 			vq->vring.desc[i].flags = VRING_DESC_F_NEXT;
 			vq->vring.desc[i].addr = sg_phys(sg);
 			vq->vring.desc[i].len = sg->length;
@@ -263,7 +251,9 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 		}
 	}
 	for (; n < (out_sgs + in_sgs); n++) {
-		for (sg = sgs[n]; sg; sg = next(sg, &total_in)) {
+		for_each_sg(sgs[n], sg, total_in, j) {
+			if (!sg)
+				break;
 			vq->vring.desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
 			vq->vring.desc[i].addr = sg_phys(sg);
 			vq->vring.desc[i].len = sg->length;
@@ -337,7 +327,7 @@ int virtqueue_add_sgs(struct virtqueue *_vq,
 		for (sg = sgs[i]; sg; sg = sg_next(sg))
 			total_in++;
 	}
-	return virtqueue_add(_vq, sgs, sg_next_chained,
+	return virtqueue_add(_vq, sgs,
 			     total_out, total_in, out_sgs, in_sgs, data, gfp);
 }
 EXPORT_SYMBOL_GPL(virtqueue_add_sgs);
@@ -360,7 +350,7 @@ int virtqueue_add_outbuf(struct virtqueue *vq,
 			 void *data,
 			 gfp_t gfp)
 {
-	return virtqueue_add(vq, &sg, sg_next_arr, num, 0, 1, 0, data, gfp);
+	return virtqueue_add(vq, &sg, num, 0, 1, 0, data, gfp);
 }
 EXPORT_SYMBOL_GPL(virtqueue_add_outbuf);
 
@@ -382,7 +372,7 @@ int virtqueue_add_inbuf(struct virtqueue *vq,
 			void *data,
 			gfp_t gfp)
 {
-	return virtqueue_add(vq, &sg, sg_next_arr, 0, num, 0, 1, data, gfp);
+	return virtqueue_add(vq, &sg, 0, num, 0, 1, data, gfp);
 }
 EXPORT_SYMBOL_GPL(virtqueue_add_inbuf);
 
Signature · 1 line
1.9.3
#2 lore raw

Re: [PATCH 1/3] virtio_ring: Remove sg_next indirection

Quoted 10 lines · Andy Lutomirski

The only unusual thing about virtio's use of scatterlists is that two of the APIs accept scatterlists that might not be terminated. Using function pointers to handle this case is overkill; for_each_sg can do it.

There's a small subtlely here: for_each_sg assumes that the provided count is correct, but, because of the way that virtio_ring handles multiple scatterlists at once, the count is only an upper bound if there is more than one scatterlist. This is easily solved by checking the sg pointer for NULL on each iteration.

Hi Andy,

(Sorry for the delayed response; I was still catching up from
my week at KS.)

Unfortunately the reason we dance through so many hoops here is that it has a measurable performance impact :( Those indirect calls get inlined.

There's only one place which actually uses a weirdly-formed sg now, and that's virtio_net. It's pretty trivial to fix.

However, vring_bench drops 15% when we do this. There's a larger question as to how much difference that makes in Real Life, of course. I'll measure that today.

Here are my two patches, back-to-back (it cam out of of an earlier concern about reducing stack usage, hence the stack measurements).

Cheers,
Rusty.

Subject: virtio_net: pass well-formed sg to virtqueue_add_inbuf()

This is the only place which doesn't hand virtqueue_add_inbuf or virtqueue_add_outbuf a well-formed, well-terminated sg. Fix it, so we can make virtio_add_* simpler.

drivers/net/virtio_net.c +2 -0
index 8a852b5f215f..63299b04cdf2 100644
@@ -590,6 +590,8 @@ static int add_recvbuf_big(struct receive_queue *rq, gfp_t gfp)
 	offset = sizeof(struct padded_vnet_hdr);
 	sg_set_buf(&rq->sg[1], p + offset, PAGE_SIZE - offset);
 
+	sg_mark_end(&rq->sg[MAX_SKB_FRAGS + 2 - 1]);
+
 	/* chain first in list head */
 	first->private = (unsigned long)list;
 	err = virtqueue_add_inbuf(rq->vq, rq->sg, MAX_SKB_FRAGS + 2,

Subject: virtio_ring: assume sgs are always well-formed.

We used to have several callers which just used arrays. They're gone, so we can use sg_next() everywhere, simplifying the code.

Before:

	gcc 4.8.2: virtio_blk: stack used = 392
	gcc 4.6.4: virtio_blk: stack used = 528

After:

	gcc 4.8.2: virtio_blk: stack used = 392
	gcc 4.6.4: virtio_blk: stack used = 480

vring_bench before:

	936153354-967745359(9.44739e+08+/-6.1e+06)ns
vring_bench after:
	1061485790-1104800648(1.08254e+09+/-6.6e+06)ns
drivers/virtio/virtio_ring.c +19 -49

163 lines not on this page · raw message

#3 lore raw

Re: [PATCH 1/3] virtio_ring: Remove sg_next indirection

Quoted 12 lines · Rusty Russell
Quoted 10 lines · Andy Lutomirski

The only unusual thing about virtio's use of scatterlists is that two of the APIs accept scatterlists that might not be terminated. Using function pointers to handle this case is overkill; for_each_sg can do it.

There's a small subtlely here: for_each_sg assumes that the provided count is correct, but, because of the way that virtio_ring handles multiple scatterlists at once, the count is only an upper bound if there is more than one scatterlist. This is easily solved by checking the sg pointer for NULL on each iteration.

Quoted 4 lines

Hi Andy,

(Sorry for the delayed response; I was still catching up from
my week at KS.)

No problem. In the grand scheme of maintainer response time, I think you're near the top. :)

Quoted 3 lines

Unfortunately the reason we dance through so many hoops here is that it has a measurable performance impact :( Those indirect calls get inlined.

gcc inlines that? That must nearly double the size of the object file. :-/

Quoted 7 lines

There's only one place which actually uses a weirdly-formed sg now, and that's virtio_net. It's pretty trivial to fix.

However, vring_bench drops 15% when we do this. There's a larger question as to how much difference that makes in Real Life, of course. I'll measure that today.

Weird. sg_next shouldn't be nearly that slow. Weird.

Quoted 4 lines

Here are my two patches, back-to-back (it cam out of of an earlier concern about reducing stack usage, hence the stack measurements).

I like your version better than mine, except that I suspect that your version will blow up for the same reason that my v2 patches blow up:
you probably need the skb_to_sgvec_nomark fix, too.

IOW, what happens if you apply patches 1-4 from my v3 series and then apply your patches on top of that?

There'll be a hit on some virtio_pci machines due to use of the DMA API. I would argue that, if this is measurable, the right fix is to prod the DMA API maintainers, whoever they are, to fix it. The DMA API really out to be very fast on identity-mapped devices, but I don't know whether it is in practice.

--Andy

#4 lore raw

Re: [PATCH 1/3] virtio_ring: Remove sg_next indirection

Quoted 18 lines · Andy Lutomirski
Quoted 12 lines · Rusty Russell
Quoted 10 lines · Andy Lutomirski

The only unusual thing about virtio's use of scatterlists is that two of the APIs accept scatterlists that might not be terminated. Using function pointers to handle this case is overkill; for_each_sg can do it.

There's a small subtlely here: for_each_sg assumes that the provided count is correct, but, because of the way that virtio_ring handles multiple scatterlists at once, the count is only an upper bound if there is more than one scatterlist. This is easily solved by checking the sg pointer for NULL on each iteration.

Quoted 4 lines

Hi Andy,

(Sorry for the delayed response; I was still catching up from
my week at KS.)

Quoted 12 lines

No problem. In the grand scheme of maintainer response time, I think you're near the top. :)

Quoted 3 lines

Unfortunately the reason we dance through so many hoops here is that it has a measurable performance impact :( Those indirect calls get inlined.

gcc inlines that? That must nearly double the size of the object file. :-/

Quoted 3 lines

There's only one place which actually uses a weirdly-formed sg now, and that's virtio_net. It's pretty trivial to fix.

This path in virtio net is also unused on modern hypervisors, so we probably don't care how well does it perform: why not apply it anyway? It's the virtio_ring changes that we need to worry about.

Quoted 5 lines
Quoted 3 lines

However, vring_bench drops 15% when we do this. There's a larger question as to how much difference that makes in Real Life, of course. I'll measure that today.

Weird. sg_next shouldn't be nearly that slow. Weird.

I think that's down to the fact that it's out of line, so it prevents inlining of the caller.

Quoted 12 lines
Quoted 4 lines

Here are my two patches, back-to-back (it cam out of of an earlier concern about reducing stack usage, hence the stack measurements).

I like your version better than mine, except that I suspect that your version will blow up for the same reason that my v2 patches blow up:
you probably need the skb_to_sgvec_nomark fix, too.

IOW, what happens if you apply patches 1-4 from my v3 series and then apply your patches on top of that?

Quoted 7 lines

There'll be a hit on some virtio_pci machines due to use of the DMA API. I would argue that, if this is measurable, the right fix is to prod the DMA API maintainers, whoever they are, to fix it. The DMA API really out to be very fast on identity-mapped devices, but I don't know whether it is in practice.

--Andy

Right but we'd have to fix that before applying the patches to avoid performance regressions.

Signature · 1 line
MST
#5 lore raw

Re: [PATCH 1/3] virtio_ring: Remove sg_next indirection

Quoted 64 lines · Michael S. Tsirkin
Quoted 18 lines · Andy Lutomirski
Quoted 12 lines · Rusty Russell
Quoted 10 lines · Andy Lutomirski

The only unusual thing about virtio's use of scatterlists is that two of the APIs accept scatterlists that might not be terminated. Using function pointers to handle this case is overkill; for_each_sg can do it.

There's a small subtlely here: for_each_sg assumes that the provided count is correct, but, because of the way that virtio_ring handles multiple scatterlists at once, the count is only an upper bound if there is more than one scatterlist. This is easily solved by checking the sg pointer for NULL on each iteration.

Quoted 4 lines

Hi Andy,

(Sorry for the delayed response; I was still catching up from
my week at KS.)

Quoted 12 lines

No problem. In the grand scheme of maintainer response time, I think you're near the top. :)

Quoted 3 lines

Unfortunately the reason we dance through so many hoops here is that it has a measurable performance impact :( Those indirect calls get inlined.

gcc inlines that? That must nearly double the size of the object file. :-/

Quoted 3 lines

There's only one place which actually uses a weirdly-formed sg now, and that's virtio_net. It's pretty trivial to fix.

This path in virtio net is also unused on modern hypervisors, so we probably don't care how well does it perform: why not apply it anyway? It's the virtio_ring changes that we need to worry about.

Quoted 5 lines
Quoted 3 lines

However, vring_bench drops 15% when we do this. There's a larger question as to how much difference that makes in Real Life, of course. I'll measure that today.

Weird. sg_next shouldn't be nearly that slow. Weird.

I think that's down to the fact that it's out of line, so it prevents inlining of the caller.

Quoted 18 lines
Quoted 4 lines

Here are my two patches, back-to-back (it cam out of of an earlier concern about reducing stack usage, hence the stack measurements).

I like your version better than mine, except that I suspect that your version will blow up for the same reason that my v2 patches blow up:
you probably need the skb_to_sgvec_nomark fix, too.

IOW, what happens if you apply patches 1-4 from my v3 series and then apply your patches on top of that?

There'll be a hit on some virtio_pci machines due to use of the DMA API. I would argue that, if this is measurable, the right fix is to prod the DMA API maintainers, whoever they are, to fix it. The DMA API really out to be very fast on identity-mapped devices, but I don't know whether it is in practice.

Quoted 4 lines
Quoted 1 line

--Andy

Right but we'd have to fix that before applying the patches to avoid performance regressions.

My pathetic attempt to benchmark it came up with no difference. What's the right benchmark? I'm having trouble compiling anything in tools/virtio -- my patch breaks that code, but it seems like it's already broken. The next version of my patches will at least not make the problem worse.

I think that we can eliminate any possibility of a meaningful performance hit due to the streaming part of the mappings by checking PCI_DMA_BUS_IS_PHYS. I'll test and send out a version of the patches that does that.

For what it's worth, there's another possible performance hit: on some architectures, coherent memory could be uncached. This might not matter for anyone, though. I think that x86 uses normal memory for coherent mappings. ARM is unlikely to make serious use of virtio_pci, since AFAIK virtio_mmio is strongly preferred on ARM. s390 should be unaffected for much the same reason. Are there other architectures on which this could be a problem?

It's possible that the weak barrier flag could be used to disable the use of coherent memory, but it might require some DMA API trickery. In particular, the only API I see is DMA_BIDIRECTIONAL, and we'll end up with ugly code in virtio_pci to get it working.

Ultimately, I think that hypervisors need a way to tell their guests that certain PCI devices are emulated and therefore fully coherent regardless our normal architectural considerations.

--Andy

Quoted 3 lines

--
MST

#6 lore raw

PATCH 2/3 virtio_ring: Use DMA APIs

In reply to Andy Lutomirski

virtio_ring currently sends the device (usually a hypervisor) physical addresses of its I/O buffers. This is okay when DMA addresses and physical addresses are the same thing, but this isn't always the case. For example, this never works on Xen guests, and it is likely to fail if a physical "virtio" device ever ends up behind an IOMMU or swiotlb.

The immediate use case for me is to enable virtio on Xen guests. For that to work, we need this fix as well as a corresponding fix to virtio_pci or to another driver.

With this patch, virtfs survives kmemleak and CONFIG_DMA_API_DEBUG. virtio-net warns (correctly) about DMA from the stack in virtnet_set_rx_mode.

This breaks s390's defconfig. The default configuration for s390 does virtio through a KVM-specific interface, but that configuration does not support DMA. I could modify this patch to stub out the DMA API calls if !CONFIG_HAS_DMA, but it seems to me that it would be much nicer to make s390 support DMA unconditionally.

It's actually unclear to me whether this can be fixed without either s390 arch support or a special case for s390 in virtio_ring: on brief inspection of s390's DMA code, it seems to assume that everything goes through a PCI IOMMU, which is presumably not the case for virtio.

---
 drivers/virtio/Kconfig       |   1 +
 drivers/virtio/virtio_ring.c | 116 +++++++++++++++++++++++++++++++++++--------
 2 files changed, 95 insertions(+), 22 deletions(-)
drivers/virtio/Kconfig +1 -0
index c6683f2e396c..b6f3acc61153 100644
@@ -1,5 +1,6 @@
 config VIRTIO
 	tristate
+	depends on HAS_DMA
 	---help---
 	  This option is selected by any driver which implements the virtio
 	  bus, such as CONFIG_VIRTIO_PCI, CONFIG_VIRTIO_MMIO, CONFIG_LGUEST,
drivers/virtio/virtio_ring.c +94 -22

231 lines not on this page · raw message

Signature · 1 line
1.9.3
#7 lore raw

Re: [PATCH 2/3] virtio_ring: Use DMA APIs

Quoted 15 lines · Andy Lutomirski

virtio_ring currently sends the device (usually a hypervisor) physical addresses of its I/O buffers. This is okay when DMA addresses and physical addresses are the same thing, but this isn't always the case. For example, this never works on Xen guests, and it is likely to fail if a physical "virtio" device ever ends up behind an IOMMU or swiotlb.

The immediate use case for me is to enable virtio on Xen guests. For that to work, we need this fix as well as a corresponding fix to virtio_pci or to another driver.

With this patch, virtfs survives kmemleak and CONFIG_DMA_API_DEBUG. virtio-net warns (correctly) about DMA from the stack in virtnet_set_rx_mode.

Quoted 5 lines

This breaks s390's defconfig. The default configuration for s390 does virtio through a KVM-specific interface, but that configuration does not support DMA. I could modify this patch to stub out the DMA API calls if !CONFIG_HAS_DMA, but it seems to me that it would be much nicer to make s390 support DMA unconditionally.

s390 has no DMA per se. Newest systems have a PCI-like I/O attach in addition to the classic channel I/O, and for that we enable the DMA code just for that transport to be able to reuse some of the existing PCI drivers. (only some because, we differ in some aspects from how PCI looks like) But the architecture itself (and the virtio interface) does not provide the DMA interface as you know it:

In essence this patch is a no-go for s390.

I am also aksing myself, if this makes virtio-ring more expensive?

Christian

Quoted 277 lines

It's actually unclear to me whether this can be fixed without either s390 arch support or a special case for s390 in virtio_ring: on brief inspection of s390's DMA code, it seems to assume that everything goes through a PCI IOMMU, which is presumably not the case for virtio.

---
 drivers/virtio/Kconfig       |   1 +
 drivers/virtio/virtio_ring.c | 116 +++++++++++++++++++++++++++++++++++--------
 2 files changed, 95 insertions(+), 22 deletions(-)
drivers/virtio/Kconfig +1 -0
index c6683f2e396c..b6f3acc61153 100644
@@ -1,5 +1,6 @@
 config VIRTIO
 	tristate
+	depends on HAS_DMA
 	---help---
 	  This option is selected by any driver which implements the virtio
 	  bus, such as CONFIG_VIRTIO_PCI, CONFIG_VIRTIO_MMIO, CONFIG_LGUEST,
drivers/virtio/virtio_ring.c +94 -22
index d356a701c9c2..6a78e2fd8e63 100644
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/hrtimer.h>
 #include <linux/kmemleak.h>
+#include <linux/dma-mapping.h>
 
 #ifdef DEBUG
 /* For development, we want to crash whenever the ring is screwed. */
@@ -54,6 +55,12 @@
 #define END_USE(vq)
 #endif
 
+struct vring_desc_state
+{
+	void *data;			/* Data for callback. */
+	struct vring_desc *indir_desc;	/* Indirect descriptor, if any. */
+};
+
 struct vring_virtqueue
 {
 	struct virtqueue vq;
@@ -93,12 +100,45 @@ struct vring_virtqueue
 	ktime_t last_add_time;
 #endif
 
-	/* Tokens for callbacks. */
-	void *data[];
+	/* Per-descriptor state. */
+	struct vring_desc_state desc_state[];
 };
 
 #define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq)
 
+/* Helper to map one sg entry, since we can't use dma_map_sg. */
+static dma_addr_t dma_map_one_sg(struct vring_virtqueue *vq,
+				 struct scatterlist *sg,
+				 enum dma_data_direction direction)
+{
+	return dma_map_page(vq->vq.vdev->dev.parent,
+			    sg_page(sg), sg->offset, sg->length, direction);
+}
+
+static void unmap_one(struct vring_virtqueue *vq, struct vring_desc *desc)
+{
+	if (desc->flags & VRING_DESC_F_INDIRECT) {
+		dma_unmap_single(vq->vq.vdev->dev.parent,
+				 desc->addr, desc->len,
+				 (desc->flags & VRING_DESC_F_WRITE) ?
+				 DMA_FROM_DEVICE : DMA_TO_DEVICE);
+	} else {
+		dma_unmap_page(vq->vq.vdev->dev.parent,
+			       desc->addr, desc->len,
+			       (desc->flags & VRING_DESC_F_WRITE) ?
+			       DMA_FROM_DEVICE : DMA_TO_DEVICE);
+	}
+}
+
+static void unmap_indirect(struct vring_virtqueue *vq, struct vring_desc *desc,
+			   int total)
+{
+	int i;
+
+	for (i = 0; i < total; i++)
+		unmap_one(vq, &desc[i]);
+}
+
 /* Set up an indirect table of descriptors and add it to the queue. */
 static inline int vring_add_indirect(struct vring_virtqueue *vq,
 				     struct scatterlist *sgs[],
@@ -132,7 +172,11 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
 			if (!sg)
 				break;
 			desc[i].flags = VRING_DESC_F_NEXT;
-			desc[i].addr = sg_phys(sg);
+			desc[i].addr =
+				dma_map_one_sg(vq, sg, DMA_TO_DEVICE);
+			if (dma_mapping_error(vq->vq.vdev->dev.parent,
+					      desc[i].addr))
+				goto unmap_free;
 			desc[i].len = sg->length;
 			desc[i].next = i+1;
 			i++;
@@ -143,7 +187,11 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
 			if (!sg)
 				break;
 			desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
-			desc[i].addr = sg_phys(sg);
+			desc[i].addr =
+				dma_map_one_sg(vq, sg, DMA_FROM_DEVICE);
+			if (dma_mapping_error(vq->vq.vdev->dev.parent,
+					      desc[i].addr))
+				goto unmap_free;
 			desc[i].len = sg->length;
 			desc[i].next = i+1;
 			i++;
@@ -161,15 +209,27 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
 	/* Use a single buffer which doesn't continue */
 	head = vq->free_head;
 	vq->vring.desc[head].flags = VRING_DESC_F_INDIRECT;
-	vq->vring.desc[head].addr = virt_to_phys(desc);
-	/* kmemleak gives a false positive, as it's hidden by virt_to_phys */
-	kmemleak_ignore(desc);
+	vq->vring.desc[head].addr =
+		dma_map_single(vq->vq.vdev->dev.parent,
+			       desc, i * sizeof(struct vring_desc),
+			       DMA_TO_DEVICE);
+	if (dma_mapping_error(vq->vq.vdev->dev.parent,
+			      vq->vring.desc[head].addr))
+		goto unmap_free;
 	vq->vring.desc[head].len = i * sizeof(struct vring_desc);
 
 	/* Update free pointer */
 	vq->free_head = vq->vring.desc[head].next;
 
+	/* Save the indirect block */
+	vq->desc_state[head].indir_desc = desc;
+
 	return head;
+
+unmap_free:
+	unmap_indirect(vq, desc, i);
+	kfree(desc);
+	return -ENOMEM;
 }
 
 static inline int virtqueue_add(struct virtqueue *_vq,
@@ -244,7 +304,8 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 			if (!sg)
 				break;
 			vq->vring.desc[i].flags = VRING_DESC_F_NEXT;
-			vq->vring.desc[i].addr = sg_phys(sg);
+			vq->vring.desc[i].addr =
+				dma_map_one_sg(vq, sg, DMA_TO_DEVICE);
 			vq->vring.desc[i].len = sg->length;
 			prev = i;
 			i = vq->vring.desc[i].next;
@@ -255,7 +316,8 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 			if (!sg)
 				break;
 			vq->vring.desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
-			vq->vring.desc[i].addr = sg_phys(sg);
+			vq->vring.desc[i].addr =
+				dma_map_one_sg(vq, sg, DMA_FROM_DEVICE);
 			vq->vring.desc[i].len = sg->length;
 			prev = i;
 			i = vq->vring.desc[i].next;
@@ -269,7 +331,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 
 add_head:
 	/* Set token. */
-	vq->data[head] = data;
+	vq->desc_state[head].data = data;
 
 	/* Put entry in available array (but don't update avail->idx until they
 	 * do sync). */
@@ -470,22 +532,33 @@ static void detach_buf(struct vring_virtqueue *vq, unsigned int head)
 	unsigned int i;
 
 	/* Clear data ptr. */
-	vq->data[head] = NULL;
+	vq->desc_state[head].data = NULL;
 
 	/* Put back on free list: find end */
 	i = head;
 
 	/* Free the indirect table */
-	if (vq->vring.desc[i].flags & VRING_DESC_F_INDIRECT)
-		kfree(phys_to_virt(vq->vring.desc[i].addr));
+	if (vq->desc_state[head].indir_desc) {
+		u32 len = vq->vring.desc[i].len;
+
+		BUG_ON(!(vq->vring.desc[i].flags & VRING_DESC_F_INDIRECT));
+		BUG_ON(len == 0 || len % sizeof(struct vring_desc));
+		unmap_indirect(vq, vq->desc_state[head].indir_desc,
+			       len / sizeof(struct vring_desc));
+		kfree(vq->desc_state[head].indir_desc);
+		vq->desc_state[head].indir_desc = NULL;
+	}
 
 	while (vq->vring.desc[i].flags & VRING_DESC_F_NEXT) {
+		unmap_one(vq, &vq->vring.desc[i]);
 		i = vq->vring.desc[i].next;
 		vq->vq.num_free++;
 	}
 
+	unmap_one(vq, &vq->vring.desc[i]);
 	vq->vring.desc[i].next = vq->free_head;
 	vq->free_head = head;
+
 	/* Plus final descriptor */
 	vq->vq.num_free++;
 }
@@ -542,13 +615,13 @@ void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
 		BAD_RING(vq, "id %u out of range\n", i);
 		return NULL;
 	}
-	if (unlikely(!vq->data[i])) {
+	if (unlikely(!vq->desc_state[i].data)) {
 		BAD_RING(vq, "id %u is not a head!\n", i);
 		return NULL;
 	}
 
 	/* detach_buf clears data, so grab it now. */
-	ret = vq->data[i];
+	ret = vq->desc_state[i].data;
 	detach_buf(vq, i);
 	vq->last_used_idx++;
 	/* If we expect an interrupt for the next entry, tell host
@@ -709,10 +782,10 @@ void *virtqueue_detach_unused_buf(struct virtqueue *_vq)
 	START_USE(vq);
 
 	for (i = 0; i < vq->vring.num; i++) {
-		if (!vq->data[i])
+		if (!vq->desc_state[i].data)
 			continue;
 		/* detach_buf clears data, so grab it now. */
-		buf = vq->data[i];
+		buf = vq->desc_state[i].data;
 		detach_buf(vq, i);
 		vq->vring.avail->idx--;
 		END_USE(vq);
@@ -765,7 +838,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
 		return NULL;
 	}
 
-	vq = kmalloc(sizeof(*vq) + sizeof(void *)*num, GFP_KERNEL);
+	vq = kmalloc(sizeof(*vq) + num * sizeof(struct vring_desc_state),
+		     GFP_KERNEL);
 	if (!vq)
 		return NULL;
 
@@ -795,11 +869,9 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
 
 	/* Put everything in free lists. */
 	vq->free_head = 0;
-	for (i = 0; i < num-1; i++) {
+	for (i = 0; i < num-1; i++)
 		vq->vring.desc[i].next = i+1;
-		vq->data[i] = NULL;
-	}
-	vq->data[i] = NULL;
+	memset(vq->desc_state, 0, num * sizeof(struct vring_desc_state));
 
 	return &vq->vq;
 }
#8 lore raw

Re: [PATCH 2/3] virtio_ring: Use DMA APIs

Quoted 22 lines · Christian Borntraeger
Quoted 15 lines · Andy Lutomirski

virtio_ring currently sends the device (usually a hypervisor) physical addresses of its I/O buffers. This is okay when DMA addresses and physical addresses are the same thing, but this isn't always the case. For example, this never works on Xen guests, and it is likely to fail if a physical "virtio" device ever ends up behind an IOMMU or swiotlb.

The immediate use case for me is to enable virtio on Xen guests. For that to work, we need this fix as well as a corresponding fix to virtio_pci or to another driver.

With this patch, virtfs survives kmemleak and CONFIG_DMA_API_DEBUG. virtio-net warns (correctly) about DMA from the stack in virtnet_set_rx_mode.

Quoted 5 lines

This breaks s390's defconfig. The default configuration for s390 does virtio through a KVM-specific interface, but that configuration does not support DMA. I could modify this patch to stub out the DMA API calls if !CONFIG_HAS_DMA, but it seems to me that it would be much nicer to make s390 support DMA unconditionally.

Quoted 6 lines

s390 has no DMA per se. Newest systems have a PCI-like I/O attach in addition to the classic channel I/O, and for that we enable the DMA code just for that transport to be able to reuse some of the existing PCI drivers. (only some because, we differ in some aspects from how PCI looks like) But the architecture itself (and the virtio interface) does not provide the DMA interface as you know it:

Don't most of those DMA_API end up then being nops? As in, we do have in the dma-api file the #ifdef case when a platform does not do DMA which ends up with all functions stubbed out.

Quoted 2 lines

In essence this patch is a no-go for s390.

Is that due to possible compilation?

Quoted 285 lines

I am also aksing myself, if this makes virtio-ring more expensive?

Christian

Quoted 277 lines

It's actually unclear to me whether this can be fixed without either s390 arch support or a special case for s390 in virtio_ring: on brief inspection of s390's DMA code, it seems to assume that everything goes through a PCI IOMMU, which is presumably not the case for virtio.

---
 drivers/virtio/Kconfig       |   1 +
 drivers/virtio/virtio_ring.c | 116 +++++++++++++++++++++++++++++++++++--------
 2 files changed, 95 insertions(+), 22 deletions(-)
drivers/virtio/Kconfig +1 -0
index c6683f2e396c..b6f3acc61153 100644
@@ -1,5 +1,6 @@
 config VIRTIO
 	tristate
+	depends on HAS_DMA
 	---help---
 	  This option is selected by any driver which implements the virtio
 	  bus, such as CONFIG_VIRTIO_PCI, CONFIG_VIRTIO_MMIO, CONFIG_LGUEST,
drivers/virtio/virtio_ring.c +94 -22
index d356a701c9c2..6a78e2fd8e63 100644
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/hrtimer.h>
 #include <linux/kmemleak.h>
+#include <linux/dma-mapping.h>
 
 #ifdef DEBUG
 /* For development, we want to crash whenever the ring is screwed. */
@@ -54,6 +55,12 @@
 #define END_USE(vq)
 #endif
 
+struct vring_desc_state
+{
+	void *data;			/* Data for callback. */
+	struct vring_desc *indir_desc;	/* Indirect descriptor, if any. */
+};
+
 struct vring_virtqueue
 {
 	struct virtqueue vq;
@@ -93,12 +100,45 @@ struct vring_virtqueue
 	ktime_t last_add_time;
 #endif
 
-	/* Tokens for callbacks. */
-	void *data[];
+	/* Per-descriptor state. */
+	struct vring_desc_state desc_state[];
 };
 
 #define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq)
 
+/* Helper to map one sg entry, since we can't use dma_map_sg. */
+static dma_addr_t dma_map_one_sg(struct vring_virtqueue *vq,
+				 struct scatterlist *sg,
+				 enum dma_data_direction direction)
+{
+	return dma_map_page(vq->vq.vdev->dev.parent,
+			    sg_page(sg), sg->offset, sg->length, direction);
+}
+
+static void unmap_one(struct vring_virtqueue *vq, struct vring_desc *desc)
+{
+	if (desc->flags & VRING_DESC_F_INDIRECT) {
+		dma_unmap_single(vq->vq.vdev->dev.parent,
+				 desc->addr, desc->len,
+				 (desc->flags & VRING_DESC_F_WRITE) ?
+				 DMA_FROM_DEVICE : DMA_TO_DEVICE);
+	} else {
+		dma_unmap_page(vq->vq.vdev->dev.parent,
+			       desc->addr, desc->len,
+			       (desc->flags & VRING_DESC_F_WRITE) ?
+			       DMA_FROM_DEVICE : DMA_TO_DEVICE);
+	}
+}
+
+static void unmap_indirect(struct vring_virtqueue *vq, struct vring_desc *desc,
+			   int total)
+{
+	int i;
+
+	for (i = 0; i < total; i++)
+		unmap_one(vq, &desc[i]);
+}
+
 /* Set up an indirect table of descriptors and add it to the queue. */
 static inline int vring_add_indirect(struct vring_virtqueue *vq,
 				     struct scatterlist *sgs[],
@@ -132,7 +172,11 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
 			if (!sg)
 				break;
 			desc[i].flags = VRING_DESC_F_NEXT;
-			desc[i].addr = sg_phys(sg);
+			desc[i].addr =
+				dma_map_one_sg(vq, sg, DMA_TO_DEVICE);
+			if (dma_mapping_error(vq->vq.vdev->dev.parent,
+					      desc[i].addr))
+				goto unmap_free;
 			desc[i].len = sg->length;
 			desc[i].next = i+1;
 			i++;
@@ -143,7 +187,11 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
 			if (!sg)
 				break;
 			desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
-			desc[i].addr = sg_phys(sg);
+			desc[i].addr =
+				dma_map_one_sg(vq, sg, DMA_FROM_DEVICE);
+			if (dma_mapping_error(vq->vq.vdev->dev.parent,
+					      desc[i].addr))
+				goto unmap_free;
 			desc[i].len = sg->length;
 			desc[i].next = i+1;
 			i++;
@@ -161,15 +209,27 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
 	/* Use a single buffer which doesn't continue */
 	head = vq->free_head;
 	vq->vring.desc[head].flags = VRING_DESC_F_INDIRECT;
-	vq->vring.desc[head].addr = virt_to_phys(desc);
-	/* kmemleak gives a false positive, as it's hidden by virt_to_phys */
-	kmemleak_ignore(desc);
+	vq->vring.desc[head].addr =
+		dma_map_single(vq->vq.vdev->dev.parent,
+			       desc, i * sizeof(struct vring_desc),
+			       DMA_TO_DEVICE);
+	if (dma_mapping_error(vq->vq.vdev->dev.parent,
+			      vq->vring.desc[head].addr))
+		goto unmap_free;
 	vq->vring.desc[head].len = i * sizeof(struct vring_desc);
 
 	/* Update free pointer */
 	vq->free_head = vq->vring.desc[head].next;
 
+	/* Save the indirect block */
+	vq->desc_state[head].indir_desc = desc;
+
 	return head;
+
+unmap_free:
+	unmap_indirect(vq, desc, i);
+	kfree(desc);
+	return -ENOMEM;
 }
 
 static inline int virtqueue_add(struct virtqueue *_vq,
@@ -244,7 +304,8 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 			if (!sg)
 				break;
 			vq->vring.desc[i].flags = VRING_DESC_F_NEXT;
-			vq->vring.desc[i].addr = sg_phys(sg);
+			vq->vring.desc[i].addr =
+				dma_map_one_sg(vq, sg, DMA_TO_DEVICE);
 			vq->vring.desc[i].len = sg->length;
 			prev = i;
 			i = vq->vring.desc[i].next;
@@ -255,7 +316,8 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 			if (!sg)
 				break;
 			vq->vring.desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
-			vq->vring.desc[i].addr = sg_phys(sg);
+			vq->vring.desc[i].addr =
+				dma_map_one_sg(vq, sg, DMA_FROM_DEVICE);
 			vq->vring.desc[i].len = sg->length;
 			prev = i;
 			i = vq->vring.desc[i].next;
@@ -269,7 +331,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 
 add_head:
 	/* Set token. */
-	vq->data[head] = data;
+	vq->desc_state[head].data = data;
 
 	/* Put entry in available array (but don't update avail->idx until they
 	 * do sync). */
@@ -470,22 +532,33 @@ static void detach_buf(struct vring_virtqueue *vq, unsigned int head)
 	unsigned int i;
 
 	/* Clear data ptr. */
-	vq->data[head] = NULL;
+	vq->desc_state[head].data = NULL;
 
 	/* Put back on free list: find end */
 	i = head;
 
 	/* Free the indirect table */
-	if (vq->vring.desc[i].flags & VRING_DESC_F_INDIRECT)
-		kfree(phys_to_virt(vq->vring.desc[i].addr));
+	if (vq->desc_state[head].indir_desc) {
+		u32 len = vq->vring.desc[i].len;
+
+		BUG_ON(!(vq->vring.desc[i].flags & VRING_DESC_F_INDIRECT));
+		BUG_ON(len == 0 || len % sizeof(struct vring_desc));
+		unmap_indirect(vq, vq->desc_state[head].indir_desc,
+			       len / sizeof(struct vring_desc));
+		kfree(vq->desc_state[head].indir_desc);
+		vq->desc_state[head].indir_desc = NULL;
+	}
 
 	while (vq->vring.desc[i].flags & VRING_DESC_F_NEXT) {
+		unmap_one(vq, &vq->vring.desc[i]);
 		i = vq->vring.desc[i].next;
 		vq->vq.num_free++;
 	}
 
+	unmap_one(vq, &vq->vring.desc[i]);
 	vq->vring.desc[i].next = vq->free_head;
 	vq->free_head = head;
+
 	/* Plus final descriptor */
 	vq->vq.num_free++;
 }
@@ -542,13 +615,13 @@ void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
 		BAD_RING(vq, "id %u out of range\n", i);
 		return NULL;
 	}
-	if (unlikely(!vq->data[i])) {
+	if (unlikely(!vq->desc_state[i].data)) {
 		BAD_RING(vq, "id %u is not a head!\n", i);
 		return NULL;
 	}
 
 	/* detach_buf clears data, so grab it now. */
-	ret = vq->data[i];
+	ret = vq->desc_state[i].data;
 	detach_buf(vq, i);
 	vq->last_used_idx++;
 	/* If we expect an interrupt for the next entry, tell host
@@ -709,10 +782,10 @@ void *virtqueue_detach_unused_buf(struct virtqueue *_vq)
 	START_USE(vq);
 
 	for (i = 0; i < vq->vring.num; i++) {
-		if (!vq->data[i])
+		if (!vq->desc_state[i].data)
 			continue;
 		/* detach_buf clears data, so grab it now. */
-		buf = vq->data[i];
+		buf = vq->desc_state[i].data;
 		detach_buf(vq, i);
 		vq->vring.avail->idx--;
 		END_USE(vq);
@@ -765,7 +838,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
 		return NULL;
 	}
 
-	vq = kmalloc(sizeof(*vq) + sizeof(void *)*num, GFP_KERNEL);
+	vq = kmalloc(sizeof(*vq) + num * sizeof(struct vring_desc_state),
+		     GFP_KERNEL);
 	if (!vq)
 		return NULL;
 
@@ -795,11 +869,9 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
 
 	/* Put everything in free lists. */
 	vq->free_head = 0;
-	for (i = 0; i < num-1; i++) {
+	for (i = 0; i < num-1; i++)
 		vq->vring.desc[i].next = i+1;
-		vq->data[i] = NULL;
-	}
-	vq->data[i] = NULL;
+	memset(vq->desc_state, 0, num * sizeof(struct vring_desc_state));
 
 	return &vq->vq;
 }
#9 lore raw

Re: [PATCH 2/3] virtio_ring: Use DMA APIs

Quoted 30 lines · Konrad Rzeszutek Wilk
Quoted 22 lines · Christian Borntraeger
Quoted 15 lines · Andy Lutomirski

virtio_ring currently sends the device (usually a hypervisor) physical addresses of its I/O buffers. This is okay when DMA addresses and physical addresses are the same thing, but this isn't always the case. For example, this never works on Xen guests, and it is likely to fail if a physical "virtio" device ever ends up behind an IOMMU or swiotlb.

The immediate use case for me is to enable virtio on Xen guests. For that to work, we need this fix as well as a corresponding fix to virtio_pci or to another driver.

With this patch, virtfs survives kmemleak and CONFIG_DMA_API_DEBUG. virtio-net warns (correctly) about DMA from the stack in virtnet_set_rx_mode.

Quoted 5 lines

This breaks s390's defconfig. The default configuration for s390 does virtio through a KVM-specific interface, but that configuration does not support DMA. I could modify this patch to stub out the DMA API calls if !CONFIG_HAS_DMA, but it seems to me that it would be much nicer to make s390 support DMA unconditionally.

Quoted 6 lines

s390 has no DMA per se. Newest systems have a PCI-like I/O attach in addition to the classic channel I/O, and for that we enable the DMA code just for that transport to be able to reuse some of the existing PCI drivers. (only some because, we differ in some aspects from how PCI looks like) But the architecture itself (and the virtio interface) does not provide the DMA interface as you know it:

Quoted 4 lines

Don't most of those DMA_API end up then being nops? As in, we do have in the dma-api file the #ifdef case when a platform does not do DMA which ends up with all functions stubbed out.

This doesn't work on s390. It fails to link.

This was my initial thought, too -- it would be nice if s390 set up no-op DMA ops its virtio device. But this might be overkill, if virtio is the *only* device on s390 that does anything that looks like DMA.

Anyway, v2 (coming soon) adds a switch in virtio, so s390 should continue to work.

--Andy

#10 lore raw

PATCH 3/3 virtio_pci: Use the DMA API for virtqueues

In reply to Andy Lutomirski

A virtqueue is a coherent DMA mapping. Use the DMA API for it. This fixes virtio_pci on Xen.

---
 drivers/virtio/virtio_pci.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)
drivers/virtio/virtio_pci.c +18 -7
index 3d1463c6b120..19039c5bec24 100644
@@ -80,8 +80,9 @@ struct virtio_pci_vq_info
 	/* the number of entries in the queue */
 	int num;
 
-	/* the virtual address of the ring queue */
-	void *queue;
+	/* the ring queue */
+	void *queue;			/* virtual address */
+	dma_addr_t queue_dma_addr;	/* bus address */
 
 	/* the list node for the virtqueues list */
 	struct list_head node;
@@ -417,15 +418,16 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
 	info->num = num;
 	info->msix_vector = msix_vec;
 
-	size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN));
-	info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO);
+	size = vring_size(num, VIRTIO_PCI_VRING_ALIGN);
+	info->queue = dma_zalloc_coherent(vdev->dev.parent, size,
+					  &info->queue_dma_addr, GFP_KERNEL);
 	if (info->queue == NULL) {
 		err = -ENOMEM;
 		goto out_info;
 	}
 
 	/* activate the queue */
-	iowrite32(virt_to_phys(info->queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
+	iowrite32(info->queue_dma_addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
 		  vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
 	/* create the vring */
@@ -462,7 +464,8 @@ out_assign:
 	vring_del_virtqueue(vq);
 out_activate_queue:
 	iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
-	free_pages_exact(info->queue, size);
+	dma_free_coherent(vdev->dev.parent, size,
+			  info->queue, info->queue_dma_addr);
 out_info:
 	kfree(info);
 	return ERR_PTR(err);
@@ -493,7 +496,8 @@ static void vp_del_vq(struct virtqueue *vq)
 	iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
 	size = PAGE_ALIGN(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN));
-	free_pages_exact(info->queue, size);
+	dma_free_coherent(vq->vdev->dev.parent, size,
+			  info->queue, info->queue_dma_addr);
 	kfree(info);
 }
 
@@ -712,6 +716,13 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
 	if (err)
 		goto out;
 
+	/*
+	 * We support 64-bit DMA.  If this fails (e.g. some bridge
+	 * or PV code doesn't or DAC is disabled), then we're okay
+	 * with 32-bit DMA.
+	 */
+	dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64));
+
 	err = pci_request_regions(pci_dev, "virtio-pci");
 	if (err)
 		goto out_enable_device;
Signature · 1 line
1.9.3
#11 lore raw

Re: [PATCH 3/3] virtio_pci: Use the DMA API for virtqueues

Quoted 65 lines · Andy Lutomirski

A virtqueue is a coherent DMA mapping. Use the DMA API for it. This fixes virtio_pci on Xen.

---
 drivers/virtio/virtio_pci.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)
drivers/virtio/virtio_pci.c +11 -7
index 3d1463c6b120..19039c5bec24 100644
@@ -80,8 +80,9 @@ struct virtio_pci_vq_info
 	/* the number of entries in the queue */
 	int num;
 
-	/* the virtual address of the ring queue */
-	void *queue;
+	/* the ring queue */
+	void *queue;			/* virtual address */
+	dma_addr_t queue_dma_addr;	/* bus address */
 
 	/* the list node for the virtqueues list */
 	struct list_head node;
@@ -417,15 +418,16 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
 	info->num = num;
 	info->msix_vector = msix_vec;
 
-	size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN));
-	info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO);
+	size = vring_size(num, VIRTIO_PCI_VRING_ALIGN);
+	info->queue = dma_zalloc_coherent(vdev->dev.parent, size,
+					  &info->queue_dma_addr, GFP_KERNEL);
 	if (info->queue == NULL) {
 		err = -ENOMEM;
 		goto out_info;
 	}
 
 	/* activate the queue */
-	iowrite32(virt_to_phys(info->queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
+	iowrite32(info->queue_dma_addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
 		  vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
 	/* create the vring */
@@ -462,7 +464,8 @@ out_assign:
 	vring_del_virtqueue(vq);
 out_activate_queue:
 	iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
-	free_pages_exact(info->queue, size);
+	dma_free_coherent(vdev->dev.parent, size,
+			  info->queue, info->queue_dma_addr);
 out_info:
 	kfree(info);
 	return ERR_PTR(err);
@@ -493,7 +496,8 @@ static void vp_del_vq(struct virtqueue *vq)
 	iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
 	size = PAGE_ALIGN(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN));
-	free_pages_exact(info->queue, size);
+	dma_free_coherent(vq->vdev->dev.parent, size,
+			  info->queue, info->queue_dma_addr);
 	kfree(info);
 }
 

drivers/virtio/virtio_pci.c static int virtio_pci_probe(struct pci_dev *pci_dev, · line 722

Quoted 7 lines
drivers/virtio/virtio_pci.c +4 -0
@@ -712,6 +716,13 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
 	if (err)
 		goto out;
 
+	/*
+	 * We support 64-bit DMA.  If this fails (e.g. some bridge
+	 * or PV code doesn't or DAC is disabled), then we're okay
+	 * with 32-bit DMA.

<scratches his head>

I am having a hard time parsing that. Could you expand a bit the faulting use-case please?

drivers/virtio/virtio_pci.c static int virtio_pci_probe(struct pci_dev *pci_dev, · line 724

Quoted 2 lines
drivers/virtio/virtio_pci.c +2 -0
+	 */
+	dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64));

The usual process is:

	ret = dma_set_mask_and_coherent(..)
	if (ret)
		ret = dma_set_mask_and_coherent(.., DMA_BIT_MASK(32))

	if (ret)
		pr_warn("We are truly screwed. Good luck!\n");
Quoted 7 lines
drivers/virtio/virtio_pci.c +1 -0
+
 	err = pci_request_regions(pci_dev, "virtio-pci");
 	if (err)
 		goto out_enable_device;
Signature · 1 line
1.9.3
#12 lore raw

Re: [PATCH 3/3] virtio_pci: Use the DMA API for virtqueues

Quoted 82 lines · Konrad Rzeszutek Wilk
Quoted 65 lines · Andy Lutomirski

A virtqueue is a coherent DMA mapping. Use the DMA API for it. This fixes virtio_pci on Xen.

---
 drivers/virtio/virtio_pci.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)
drivers/virtio/virtio_pci.c +11 -7
index 3d1463c6b120..19039c5bec24 100644
@@ -80,8 +80,9 @@ struct virtio_pci_vq_info
      /* the number of entries in the queue */
      int num;
 
-     /* the virtual address of the ring queue */
-     void *queue;
+     /* the ring queue */
+     void *queue;                    /* virtual address */
+     dma_addr_t queue_dma_addr;      /* bus address */
 
      /* the list node for the virtqueues list */
      struct list_head node;
@@ -417,15 +418,16 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
      info->num = num;
      info->msix_vector = msix_vec;
 
-     size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN));
-     info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO);
+     size = vring_size(num, VIRTIO_PCI_VRING_ALIGN);
+     info->queue = dma_zalloc_coherent(vdev->dev.parent, size,
+                                       &info->queue_dma_addr, GFP_KERNEL);
      if (info->queue == NULL) {
              err = -ENOMEM;
              goto out_info;
      }
 
      /* activate the queue */
-     iowrite32(virt_to_phys(info->queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
+     iowrite32(info->queue_dma_addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
                vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
      /* create the vring */
@@ -462,7 +464,8 @@ out_assign:
      vring_del_virtqueue(vq);
 out_activate_queue:
      iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
-     free_pages_exact(info->queue, size);
+     dma_free_coherent(vdev->dev.parent, size,
+                       info->queue, info->queue_dma_addr);
 out_info:
      kfree(info);
      return ERR_PTR(err);
@@ -493,7 +496,8 @@ static void vp_del_vq(struct virtqueue *vq)
      iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
      size = PAGE_ALIGN(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN));
-     free_pages_exact(info->queue, size);
+     dma_free_coherent(vq->vdev->dev.parent, size,
+                       info->queue, info->queue_dma_addr);
      kfree(info);
 }
 

drivers/virtio/virtio_pci.c static int virtio_pci_probe(struct pci_dev *pci_dev, · line 722

Quoted 7 lines
drivers/virtio/virtio_pci.c +4 -0
@@ -712,6 +716,13 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
      if (err)
              goto out;
 
+     /*
+      * We support 64-bit DMA.  If this fails (e.g. some bridge
+      * or PV code doesn't or DAC is disabled), then we're okay
+      * with 32-bit DMA.

<scratches his head>

I am having a hard time parsing that. Could you expand a bit the faulting use-case please?

drivers/virtio/virtio_pci.c static int virtio_pci_probe(struct pci_dev *pci_dev, · line 724

Quoted 2 lines
drivers/virtio/virtio_pci.c +2 -0
+      */
+     dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64));
Quoted 9 lines

The usual process is:

        ret = dma_set_mask_and_coherent(..)
        if (ret)
                ret = dma_set_mask_and_coherent(.., DMA_BIT_MASK(32))

        if (ret)
                pr_warn("We are truly screwed. Good luck!\n");

I assumed that, if dma_set_mask_and_coherent(..., DMA_BIT_BASK(64)) fails, then we can still do DMA, just not 64-bit DMA. This driver should be fine with that -- it'll just be a bit slower.

If that's not a safe assumption, I can change it.

--Andy

#13 lore raw

Re: [PATCH 3/3] virtio_pci: Use the DMA API for virtqueues

Quoted 90 lines · Andy Lutomirski
Quoted 88 lines · Konrad Rzeszutek Wilk
Quoted 65 lines · Andy Lutomirski

A virtqueue is a coherent DMA mapping. Use the DMA API for it. This fixes virtio_pci on Xen.

---
 drivers/virtio/virtio_pci.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)
drivers/virtio/virtio_pci.c +11 -7
index 3d1463c6b120..19039c5bec24 100644
@@ -80,8 +80,9 @@ struct virtio_pci_vq_info
      /* the number of entries in the queue */
      int num;
 
-     /* the virtual address of the ring queue */
-     void *queue;
+     /* the ring queue */
+     void *queue;                    /* virtual address */
+     dma_addr_t queue_dma_addr;      /* bus address */
 
      /* the list node for the virtqueues list */
      struct list_head node;
@@ -417,15 +418,16 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
      info->num = num;
      info->msix_vector = msix_vec;
 
-     size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN));
-     info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO);
+     size = vring_size(num, VIRTIO_PCI_VRING_ALIGN);
+     info->queue = dma_zalloc_coherent(vdev->dev.parent, size,
+                                       &info->queue_dma_addr, GFP_KERNEL);
      if (info->queue == NULL) {
              err = -ENOMEM;
              goto out_info;
      }
 
      /* activate the queue */
-     iowrite32(virt_to_phys(info->queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
+     iowrite32(info->queue_dma_addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
                vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
      /* create the vring */
@@ -462,7 +464,8 @@ out_assign:
      vring_del_virtqueue(vq);
 out_activate_queue:
      iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
-     free_pages_exact(info->queue, size);
+     dma_free_coherent(vdev->dev.parent, size,
+                       info->queue, info->queue_dma_addr);
 out_info:
      kfree(info);
      return ERR_PTR(err);
@@ -493,7 +496,8 @@ static void vp_del_vq(struct virtqueue *vq)
      iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
      size = PAGE_ALIGN(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN));
-     free_pages_exact(info->queue, size);
+     dma_free_coherent(vq->vdev->dev.parent, size,
+                       info->queue, info->queue_dma_addr);
      kfree(info);
 }
 

drivers/virtio/virtio_pci.c static int virtio_pci_probe(struct pci_dev *pci_dev, · line 722

Quoted 7 lines
drivers/virtio/virtio_pci.c +4 -0
@@ -712,6 +716,13 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
      if (err)
              goto out;
 
+     /*
+      * We support 64-bit DMA.  If this fails (e.g. some bridge
+      * or PV code doesn't or DAC is disabled), then we're okay
+      * with 32-bit DMA.

<scratches his head>

I am having a hard time parsing that. Could you expand a bit the faulting use-case please?

drivers/virtio/virtio_pci.c static int virtio_pci_probe(struct pci_dev *pci_dev, · line 724

Quoted 2 lines
drivers/virtio/virtio_pci.c +2 -0
+      */
+     dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64));

The usual process is:

        ret = dma_set_mask_and_coherent(..)
        if (ret)
                ret = dma_set_mask_and_coherent(.., DMA_BIT_MASK(32))
Quoted 7 lines
Quoted 3 lines
        if (ret)
                pr_warn("We are truly screwed. Good luck!\n");

I assumed that, if dma_set_mask_and_coherent(..., DMA_BIT_BASK(64)) fails, then we can still do DMA, just not 64-bit DMA. This driver should be fine with that -- it'll just be a bit slower.

Right. It should fail back to 32-bit (the default on PCI bus).

I was merely thinking of the error reporting might be useful but on second thought - saying that the user is screwed - is a bad idea.

Quoted 4 lines

If that's not a safe assumption, I can change it.

--Andy

#14 lore raw
In reply to Andy Lutomirski
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Can you use a transport feature bit to indicate that bus addresses are used? That way both approaches can be supported.

Please also update the virtio specification:
https://tools.oasis-open.org/version-control/browse/wsvn/virtio/

Stefan

#15 lore raw
Quoted 7 lines · Stefan Hajnoczi
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

Quoted 5 lines

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Can you use a transport feature bit to indicate that bus addresses are used? That way both approaches can be supported.

I can try to support both styles of addressing, but I don't think that this can be negotiated between the device (i.e. host or physical virtio-speaking device) and the guest. In the Xen case that I care about (Linux on Xen on KVM), the host doesn't know about the translation at all -- Xen is an intermediate layer that only the guest is aware of. In this case, there are effectively two layers of virtualization, and only the inner one (Xen) knows about the translation despite the fact that the the outer layer is the one providing the virtio device.

I could change virtio_ring to use the DMA API only if requested by the lower driver (virtio_pci, etc) and to have only virtio_pci enable that feature. Will that work for all cases?

On s390, this shouldn't work just like the current code. On x86, I think that if QEMU ever starts exposing an IOMMU attached to a virtio-pci device, then QEMU should expect that IOMMU to be used. If QEMU expects to see physical addresses, then it shouldn't advertise an IOMMU. Since QEMU doesn't currently support guest IOMMUs, this should be fine for everything that uses QEMU.

At least x86's implementation of the DMA ops for devices that aren't behind an IOMMU should be very fast.

Are there any other weird cases for which this might be a problem?

Quoted 4 lines

Please also update the virtio specification:
https://tools.oasis-open.org/version-control/browse/wsvn/virtio/

I'm not sure it will need an update. Perhaps a note in the PCI section indicating that, if the host expects the guest to program an IOMMU, then it should use the appropriate platform-specific mechanism to expose that IOMMU.

--Andy

#16 lore raw
Quoted 45 lines · Andy Lutomirski
Quoted 7 lines · Stefan Hajnoczi
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

Quoted 5 lines

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Can you use a transport feature bit to indicate that bus addresses are used? That way both approaches can be supported.

I can try to support both styles of addressing, but I don't think that this can be negotiated between the device (i.e. host or physical virtio-speaking device) and the guest. In the Xen case that I care about (Linux on Xen on KVM), the host doesn't know about the translation at all -- Xen is an intermediate layer that only the guest is aware of. In this case, there are effectively two layers of virtualization, and only the inner one (Xen) knows about the translation despite the fact that the the outer layer is the one providing the virtio device.

I could change virtio_ring to use the DMA API only if requested by the lower driver (virtio_pci, etc) and to have only virtio_pci enable that feature. Will that work for all cases?

On s390, this shouldn't work just like the current code. On x86, I think that if QEMU ever starts exposing an IOMMU attached to a virtio-pci device, then QEMU should expect that IOMMU to be used. If QEMU expects to see physical addresses, then it shouldn't advertise an IOMMU. Since QEMU doesn't currently support guest IOMMUs, this should be fine for everything that uses QEMU.

At least x86's implementation of the DMA ops for devices that aren't behind an IOMMU should be very fast.

Are there any other weird cases for which this might be a problem?

Quoted 4 lines

Please also update the virtio specification:
https://tools.oasis-open.org/version-control/browse/wsvn/virtio/

Quoted 6 lines

I'm not sure it will need an update. Perhaps a note in the PCI section indicating that, if the host expects the guest to program an IOMMU, then it should use the appropriate platform-specific mechanism to expose that IOMMU.

--Andy

If there's no virtio mechanism to negotate enabling/disabling translations, then specification does not need an extension.

Signature · 1 line
MST
#17 lore raw
Quoted 51 lines · Michael S. Tsirkin
Quoted 50 lines · Andy Lutomirski
Quoted 7 lines · Stefan Hajnoczi
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

Quoted 5 lines

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Can you use a transport feature bit to indicate that bus addresses are used? That way both approaches can be supported.

I can try to support both styles of addressing, but I don't think that this can be negotiated between the device (i.e. host or physical virtio-speaking device) and the guest. In the Xen case that I care about (Linux on Xen on KVM), the host doesn't know about the translation at all -- Xen is an intermediate layer that only the guest is aware of. In this case, there are effectively two layers of virtualization, and only the inner one (Xen) knows about the translation despite the fact that the the outer layer is the one providing the virtio device.

I could change virtio_ring to use the DMA API only if requested by the lower driver (virtio_pci, etc) and to have only virtio_pci enable that feature. Will that work for all cases?

On s390, this shouldn't work just like the current code. On x86, I think that if QEMU ever starts exposing an IOMMU attached to a virtio-pci device, then QEMU should expect that IOMMU to be used. If QEMU expects to see physical addresses, then it shouldn't advertise an IOMMU. Since QEMU doesn't currently support guest IOMMUs, this should be fine for everything that uses QEMU.

At least x86's implementation of the DMA ops for devices that aren't behind an IOMMU should be very fast.

Are there any other weird cases for which this might be a problem?

Quoted 4 lines

Please also update the virtio specification:
https://tools.oasis-open.org/version-control/browse/wsvn/virtio/

I'm not sure it will need an update. Perhaps a note in the PCI section indicating that, if the host expects the guest to program an IOMMU, then it should use the appropriate platform-specific mechanism to expose that IOMMU.

Quoted 4 lines
Quoted 1 line

--Andy

If there's no virtio mechanism to negotate enabling/disabling translations, then specification does not need an extension.

It wouldn't shock me if someone wants to negotiate this for virtio_mmio some day, but that might be more of a device tree thing. I have no idea how that works, but I think it can wait until someone wants it.

I updated the patches, and I'll send them out after I try to test-boot s390 under QEMU :)

--Andy

#18 lore raw

cover virtio: Clean up scatterlists and use the DMA API

Quoted 54 lines · Andy Lutomirski
Quoted 53 lines · Michael S. Tsirkin
Quoted 51 lines · Andy Lutomirski
Quoted 7 lines · Stefan Hajnoczi
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

Quoted 5 lines

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Can you use a transport feature bit to indicate that bus addresses are used? That way both approaches can be supported.

I can try to support both styles of addressing, but I don't think that this can be negotiated between the device (i.e. host or physical virtio-speaking device) and the guest. In the Xen case that I care about (Linux on Xen on KVM), the host doesn't know about the translation at all -- Xen is an intermediate layer that only the guest is aware of. In this case, there are effectively two layers of virtualization, and only the inner one (Xen) knows about the translation despite the fact that the the outer layer is the one providing the virtio device.

I could change virtio_ring to use the DMA API only if requested by the lower driver (virtio_pci, etc) and to have only virtio_pci enable that feature. Will that work for all cases?

On s390, this shouldn't work just like the current code. On x86, I think that if QEMU ever starts exposing an IOMMU attached to a virtio-pci device, then QEMU should expect that IOMMU to be used. If QEMU expects to see physical addresses, then it shouldn't advertise an IOMMU. Since QEMU doesn't currently support guest IOMMUs, this should be fine for everything that uses QEMU.

At least x86's implementation of the DMA ops for devices that aren't behind an IOMMU should be very fast.

Are there any other weird cases for which this might be a problem?

Quoted 4 lines

Please also update the virtio specification:
https://tools.oasis-open.org/version-control/browse/wsvn/virtio/

I'm not sure it will need an update. Perhaps a note in the PCI section indicating that, if the host expects the guest to program an IOMMU, then it should use the appropriate platform-specific mechanism to expose that IOMMU.

--Andy

Quoted 7 lines
Quoted 2 lines

If there's no virtio mechanism to negotate enabling/disabling translations, then specification does not need an extension.

It wouldn't shock me if someone wants to negotiate this for virtio_mmio some day, but that might be more of a device tree thing. I have no idea how that works, but I think it can wait until someone wants it.

At one point I wanted VirtIO-MMIO to not fail miserably on ARM LPAE systems.

http://lists.infradead.org/pipermail/linux-arm-kernel/2014-March/241613.html

Christopher

Signature · 3 lines
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by the Linux Foundation.
#19 lore raw

cover virtio: Clean up scatterlists and use the DMA API

Quoted 58 lines · Christopher Covington
Quoted 57 lines · Andy Lutomirski
Quoted 55 lines · Michael S. Tsirkin
Quoted 45 lines · Andy Lutomirski
Quoted 7 lines · Stefan Hajnoczi
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

Quoted 5 lines

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Can you use a transport feature bit to indicate that bus addresses are used? That way both approaches can be supported.

I can try to support both styles of addressing, but I don't think that this can be negotiated between the device (i.e. host or physical virtio-speaking device) and the guest. In the Xen case that I care about (Linux on Xen on KVM), the host doesn't know about the translation at all -- Xen is an intermediate layer that only the guest is aware of. In this case, there are effectively two layers of virtualization, and only the inner one (Xen) knows about the translation despite the fact that the the outer layer is the one providing the virtio device.

I could change virtio_ring to use the DMA API only if requested by the lower driver (virtio_pci, etc) and to have only virtio_pci enable that feature. Will that work for all cases?

On s390, this shouldn't work just like the current code. On x86, I think that if QEMU ever starts exposing an IOMMU attached to a virtio-pci device, then QEMU should expect that IOMMU to be used. If QEMU expects to see physical addresses, then it shouldn't advertise an IOMMU. Since QEMU doesn't currently support guest IOMMUs, this should be fine for everything that uses QEMU.

At least x86's implementation of the DMA ops for devices that aren't behind an IOMMU should be very fast.

Are there any other weird cases for which this might be a problem?

Quoted 4 lines

Please also update the virtio specification:
https://tools.oasis-open.org/version-control/browse/wsvn/virtio/

Quoted 6 lines

I'm not sure it will need an update. Perhaps a note in the PCI section indicating that, if the host expects the guest to program an IOMMU, then it should use the appropriate platform-specific mechanism to expose that IOMMU.

--Andy

If there's no virtio mechanism to negotate enabling/disabling translations, then specification does not need an extension.

Quoted 9 lines
Quoted 4 lines

It wouldn't shock me if someone wants to negotiate this for virtio_mmio some day, but that might be more of a device tree thing. I have no idea how that works, but I think it can wait until someone wants it.

At one point I wanted VirtIO-MMIO to not fail miserably on ARM LPAE systems.

http://lists.infradead.org/pipermail/linux-arm-kernel/2014-March/241613.html

Since I know nothing about LPAE, I'll leave this to you :) But I'll cc you on patch v2 soon, and please tell me whether my code makes sense on ARM.

(My attempt to boot-test s390 failed because I have no clue what command-line options to pass to QEMU. If anyone wants to give me some pointers to get a working configuration with -kernel and some kind of console, I can add support to virtme. Alas, I think that no one ever bothered to implement 9p over virtio-ccw in QEMU. Why exactly does the virtio stuff in QEMU require that you instantiate virtio-9p-pci instead of just asking for an appropriate virtio dievice?)

--Andy

#20 lore raw

cover virtio: Clean up scatterlists and use the DMA API

Quoted 74 lines · Andy Lutomirski
Quoted 58 lines · Christopher Covington
Quoted 57 lines · Andy Lutomirski
Quoted 55 lines · Michael S. Tsirkin
Quoted 45 lines · Andy Lutomirski
Quoted 7 lines · Stefan Hajnoczi
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

Quoted 5 lines

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Can you use a transport feature bit to indicate that bus addresses are used? That way both approaches can be supported.

I can try to support both styles of addressing, but I don't think that this can be negotiated between the device (i.e. host or physical virtio-speaking device) and the guest. In the Xen case that I care about (Linux on Xen on KVM), the host doesn't know about the translation at all -- Xen is an intermediate layer that only the guest is aware of. In this case, there are effectively two layers of virtualization, and only the inner one (Xen) knows about the translation despite the fact that the the outer layer is the one providing the virtio device.

I could change virtio_ring to use the DMA API only if requested by the lower driver (virtio_pci, etc) and to have only virtio_pci enable that feature. Will that work for all cases?

On s390, this shouldn't work just like the current code. On x86, I think that if QEMU ever starts exposing an IOMMU attached to a virtio-pci device, then QEMU should expect that IOMMU to be used. If QEMU expects to see physical addresses, then it shouldn't advertise an IOMMU. Since QEMU doesn't currently support guest IOMMUs, this should be fine for everything that uses QEMU.

At least x86's implementation of the DMA ops for devices that aren't behind an IOMMU should be very fast.

Are there any other weird cases for which this might be a problem?

Quoted 4 lines

Please also update the virtio specification:
https://tools.oasis-open.org/version-control/browse/wsvn/virtio/

Quoted 6 lines

I'm not sure it will need an update. Perhaps a note in the PCI section indicating that, if the host expects the guest to program an IOMMU, then it should use the appropriate platform-specific mechanism to expose that IOMMU.

--Andy

If there's no virtio mechanism to negotate enabling/disabling translations, then specification does not need an extension.

Quoted 9 lines
Quoted 4 lines

It wouldn't shock me if someone wants to negotiate this for virtio_mmio some day, but that might be more of a device tree thing. I have no idea how that works, but I think it can wait until someone wants it.

At one point I wanted VirtIO-MMIO to not fail miserably on ARM LPAE systems.

http://lists.infradead.org/pipermail/linux-arm-kernel/2014-March/241613.html

Since I know nothing about LPAE, I'll leave this to you :) But I'll cc you on patch v2 soon, and please tell me whether my code makes sense on ARM.

Quoted 7 lines

(My attempt to boot-test s390 failed because I have no clue what command-line options to pass to QEMU. If anyone wants to give me some pointers to get a working configuration with -kernel and some kind of console, I can add support to virtme. Alas, I think that no one ever bothered to implement 9p over virtio-ccw in QEMU. Why exactly does the virtio stuff in QEMU require that you instantiate virtio-9p-pci instead of just asking for an appropriate virtio dievice?)

Virtme looks interesting. If it's any use, here is my modest QEMU command line collection.

$dir/bin/x86_64-linux-gnu/qemu-system-x86_64 \
-m 1024 \
-kernel $dir/boot/x86_64-linux-gnu/bzImage-x86_64 \
-append 'root=/dev/root init=/sbin/x86_64-linux-gnu/init rootfstype=9p
rootflags=trans=virtio,version=9p2000.L ro console=ttyS0' \
-serial stdio \
-fsdev local,id=root,path=$dir,security_model=none \
-device virtio-9p-pci,fsdev=root,mount_tag=/dev/root \
-monitor none

$dir/bin/x86_64-linux-gnu/qemu-system-arm \
-M virt \
-cpu cortex-a15 \
-m 1024 \
-kernel $dir/boot/arm-linux-gnueabihf/Image \
-append 'root=/dev/root init=/sbin/arm-linux-gnueabihf/init rootfstype=9p
rootflags=trans=virtio,version=9p2000.L ro console=ttyAMA0' \
-serial stdio \
-fsdev local,id=root,path=$dir,security_model=none \
-device virtio-9p-device,fsdev=root,mount_tag=/dev/root \
-monitor none

$dir/bin/x86_64-linux-gnu/qemu-system-aarch64 \
-M virt \
-cpu cortex-a57 \
-m 1024 \
-kernel $dir/boot/aarch64-linux-gnu/Image \
-append 'root=/dev/root init=/sbin/aarch64-linux-gnu/init rootfstype=9p
rootflags=trans=virtio,version=9p2000.L ro console=ttyAMA0' \
-serial stdio \
-fsdev local,id=root,path=$dir,security_model=none \
-device virtio-9p-device,fsdev=root,mount_tag=/dev/root -monitor none

Christopher

Signature · 3 lines
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by the Linux Foundation.
#21 lore raw

cover virtio: Clean up scatterlists and use the DMA API

Quoted 7 lines · Christopher Covington
Quoted 2 lines · Andy Lutomirski

On Wed, Aug 27, 2014 at 9:13 AM, Christopher Covington <[email protected]> wrote:

Virtme looks interesting. If it's any use, here is my modest QEMU command line collection.

Quoted 9 lines

$dir/bin/x86_64-linux-gnu/qemu-system-x86_64 \
-m 1024 \
-kernel $dir/boot/x86_64-linux-gnu/bzImage-x86_64 \
-append 'root=/dev/root init=/sbin/x86_64-linux-gnu/init rootfstype=9p
rootflags=trans=virtio,version=9p2000.L ro console=ttyS0' \
-serial stdio \
-fsdev local,id=root,path=$dir,security_model=none \
-device virtio-9p-pci,fsdev=root,mount_tag=/dev/root \
-monitor none

This is similar to what virtme does, except that virtme will give you a much more functional system at the cost of a small fraction of a second of additional boot time. And virtme sometimes supports modules :)

Quoted 12 lines

$dir/bin/x86_64-linux-gnu/qemu-system-arm \
-M virt \
-cpu cortex-a15 \
-m 1024 \
-kernel $dir/boot/arm-linux-gnueabihf/Image \
-append 'root=/dev/root init=/sbin/arm-linux-gnueabihf/init rootfstype=9p
rootflags=trans=virtio,version=9p2000.L ro console=ttyAMA0' \
-serial stdio \
-fsdev local,id=root,path=$dir,security_model=none \
-device virtio-9p-device,fsdev=root,mount_tag=/dev/root \
-monitor none

Can you give me some hints as to how to configure a kernel for this? I tried to get anything other than versatilepb working and gave up.

Quoted 12 lines

$dir/bin/x86_64-linux-gnu/qemu-system-aarch64 \
-M virt \
-cpu cortex-a57 \
-m 1024 \
-kernel $dir/boot/aarch64-linux-gnu/Image \
-append 'root=/dev/root init=/sbin/aarch64-linux-gnu/init rootfstype=9p
rootflags=trans=virtio,version=9p2000.L ro console=ttyAMA0' \
-serial stdio \
-fsdev local,id=root,path=$dir,security_model=none \
-device virtio-9p-device,fsdev=root,mount_tag=/dev/root -monitor none

Ooh, thanks! I'll add support for this to virtme.

--Andy

#22 lore raw

cover virtio: Clean up scatterlists and use the DMA API

Quoted 37 lines · Andy Lutomirski
Quoted 7 lines · Christopher Covington
Quoted 2 lines · Andy Lutomirski

On Wed, Aug 27, 2014 at 9:13 AM, Christopher Covington <[email protected]> wrote:

Virtme looks interesting. If it's any use, here is my modest QEMU command line collection.

Quoted 9 lines

$dir/bin/x86_64-linux-gnu/qemu-system-x86_64 \
-m 1024 \
-kernel $dir/boot/x86_64-linux-gnu/bzImage-x86_64 \
-append 'root=/dev/root init=/sbin/x86_64-linux-gnu/init rootfstype=9p
rootflags=trans=virtio,version=9p2000.L ro console=ttyS0' \
-serial stdio \
-fsdev local,id=root,path=$dir,security_model=none \
-device virtio-9p-pci,fsdev=root,mount_tag=/dev/root \
-monitor none

This is similar to what virtme does, except that virtme will give you a much more functional system at the cost of a small fraction of a second of additional boot time. And virtme sometimes supports modules :)

Quoted 12 lines

$dir/bin/x86_64-linux-gnu/qemu-system-arm \
-M virt \
-cpu cortex-a15 \
-m 1024 \
-kernel $dir/boot/arm-linux-gnueabihf/Image \
-append 'root=/dev/root init=/sbin/arm-linux-gnueabihf/init rootfstype=9p
rootflags=trans=virtio,version=9p2000.L ro console=ttyAMA0' \
-serial stdio \
-fsdev local,id=root,path=$dir,security_model=none \
-device virtio-9p-device,fsdev=root,mount_tag=/dev/root \
-monitor none

Quoted 2 lines

Can you give me some hints as to how to configure a kernel for this? I tried to get anything other than versatilepb working and gave up.

I think you can just use vexpress_defconfig. If you want to from there you can unselect CONFIG_ARCH_VEXPRESS and set CONFIG_ARCH_VIRT=y, but I don't think that's strictly necessary.

Quoted 12 lines
Quoted 11 lines

$dir/bin/x86_64-linux-gnu/qemu-system-aarch64 \
-M virt \
-cpu cortex-a57 \
-m 1024 \
-kernel $dir/boot/aarch64-linux-gnu/Image \
-append 'root=/dev/root init=/sbin/aarch64-linux-gnu/init rootfstype=9p
rootflags=trans=virtio,version=9p2000.L ro console=ttyAMA0' \
-serial stdio \
-fsdev local,id=root,path=$dir,security_model=none \
-device virtio-9p-device,fsdev=root,mount_tag=/dev/root -monitor none

Quoted 1 line

Ooh, thanks! I'll add support for this to virtme.

Cheers,
Christopher

Signature · 3 lines
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by the Linux Foundation.
#23 lore raw
In reply to Andy Lutomirski
Quoted 57 lines · Andy Lutomirski
Quoted 51 lines · Michael S. Tsirkin
Quoted 50 lines · Andy Lutomirski
Quoted 7 lines · Stefan Hajnoczi
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

Quoted 5 lines

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Can you use a transport feature bit to indicate that bus addresses are used? That way both approaches can be supported.

I can try to support both styles of addressing, but I don't think that this can be negotiated between the device (i.e. host or physical virtio-speaking device) and the guest. In the Xen case that I care about (Linux on Xen on KVM), the host doesn't know about the translation at all -- Xen is an intermediate layer that only the guest is aware of. In this case, there are effectively two layers of virtualization, and only the inner one (Xen) knows about the translation despite the fact that the the outer layer is the one providing the virtio device.

I could change virtio_ring to use the DMA API only if requested by the lower driver (virtio_pci, etc) and to have only virtio_pci enable that feature. Will that work for all cases?

On s390, this shouldn't work just like the current code. On x86, I think that if QEMU ever starts exposing an IOMMU attached to a virtio-pci device, then QEMU should expect that IOMMU to be used. If QEMU expects to see physical addresses, then it shouldn't advertise an IOMMU. Since QEMU doesn't currently support guest IOMMUs, this should be fine for everything that uses QEMU.

At least x86's implementation of the DMA ops for devices that aren't behind an IOMMU should be very fast.

Are there any other weird cases for which this might be a problem?

Quoted 4 lines

Please also update the virtio specification:
https://tools.oasis-open.org/version-control/browse/wsvn/virtio/

I'm not sure it will need an update. Perhaps a note in the PCI section indicating that, if the host expects the guest to program an IOMMU, then it should use the appropriate platform-specific mechanism to expose that IOMMU.

Quoted 4 lines
Quoted 1 line

--Andy

If there's no virtio mechanism to negotate enabling/disabling translations, then specification does not need an extension.

Quoted 7 lines

It wouldn't shock me if someone wants to negotiate this for virtio_mmio some day, but that might be more of a device tree thing. I have no idea how that works, but I think it can wait until someone wants it.

I updated the patches, and I'll send them out after I try to test-boot s390 under QEMU :)

The emulation of several parts of s390 including the ccw stuff is currently broken in QEMU. I can test new version.

Christian

#24 lore raw
In reply to Stefan Hajnoczi
Quoted 8 lines · Stefan Hajnoczi
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Isn't that what this is suppose to fix? Right now the semantics of the vring is that bus address == physical address.

Quoted 8 lines

Can you use a transport feature bit to indicate that bus addresses are used? That way both approaches can be supported.

Please also update the virtio specification:
https://tools.oasis-open.org/version-control/browse/wsvn/virtio/

Stefan

#25 lore raw
Quoted 7 lines · Konrad Rzeszutek Wilk
Quoted 6 lines · Stefan Hajnoczi
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

Quoted 5 lines
Quoted 2 lines

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Isn't that what this is suppose to fix? Right now the semantics of the vring is that bus address == physical address.

Today the assumption is that the device emulation code has access to guest physical memory (no translation necessary).

Changing the semantics breaks today's guests. You need to make this change in a way so that existing guests don't break.

Stefan

#26 lore raw
Quoted 9 lines · Stefan Hajnoczi
Quoted 7 lines · Konrad Rzeszutek Wilk
Quoted 6 lines · Stefan Hajnoczi
Quoted 4 lines · Andy Lutomirski

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390. (Maintainers are cc'd -- I don't know what to do about it.)

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

This changes the semantics of vring and breaks existing guests when bus address != physical address.

Isn't that what this is suppose to fix? Right now the semantics of the vring is that bus address == physical address.

Today the assumption is that the device emulation code has access to guest physical memory (no translation necessary).

Right.

Quoted 3 lines

Changing the semantics breaks today's guests. You need to make this change in a way so that existing guests don't break.

Well, the DMA API will preserve that semantic as under KVM it will return phys == bus.

But that is a side-effect of the DMA API implementation preserving this semantic so I understand your point.

Quoted 2 lines

Stefan

#27 lore raw
In reply to Andy Lutomirski
Quoted 9 lines · On Tue, 26 Aug 2014 14:16:59 -0700 Andy Lutomirski <[email protected]> wrote:

This fixes virtio on Xen guests as well as on any other platform on which physical addresses don't match bus addresses.

This can be tested with:

    virtme-run --xen xen --kimg arch/x86/boot/bzImage --console

using virtme from here:

Quoted 8 lines

https://git.kernel.org/cgit/utils/kernel/virtme/virtme.git

Without these patches, the guest hangs forever. With these patches, everything works.

There are two outstanding issues. virtio_net warns if DMA debugging is on because it does DMA from the stack. (The warning is correct.) This also is likely to do something unpleasant to s390.

s/is likely to do something unpleasant to/breaks/

Sorry, this just won't work for s390, as Christian already explained.

Quoted 7 lines

(Maintainers are cc'd -- I don't know what to do about it.)

Andy Lutomirski (3):
virtio_ring: Remove sg_next indirection
virtio_ring: Use DMA APIs
virtio_pci: Use the DMA API for virtqueues

Quoted 5 lines
 drivers/virtio/Kconfig       |   1 +
 drivers/virtio/virtio_pci.c  |  25 ++++++--
 drivers/virtio/virtio_ring.c | 150 ++++++++++++++++++++++++++++++-------------
 3 files changed, 125 insertions(+), 51 deletions(-)

Aren't you also changing some basic assumptions with the conversion to DMA apis? virtqueues currently aren't accessed via dma (or it wouldn't work on s390); if you want (say) virtio-pci to use dma, shouldn't that be something that is negotiated between guest and host?