We used to have use a single parent for all DMA operations. This tends to complicate the mdev based hardware virtio datapath offloading which may not implement the control path over datapath like ctrl vq in the case of virtio-net. So this series tries to intorduce per DMA domain by allowing trasnport to specify the parent device for each virtqueue. Then for the case of virtio-mdev device, it can hook the DMA ops for control vq back to itself and then using e.g VA or PA to emulate the control virtqueue operation. Vhost-mdev may use similar idea. Note, compiling test only. Jason Wang (2): virtio: accept parent as a parameter when allocating virtqueue virtio: allow query vq parent device drivers/virtio/virtio_ring.c | 36 +++++++++++++++++++++++++---------- include/linux/virtio_config.h | 2 ++ 2 files changed, 28 insertions(+), 10 deletions(-) -- 2.19.1
Jason Wang
2019-Oct-29 10:58 UTC
[RFC PATCH 1/2] virtio: accept parent as a parameter when allocating virtqueue
Signed-off-by: Jason Wang <jasowang at redhat.com> --- drivers/virtio/virtio_ring.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index bdc08244a648..51d83f4d7c32 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -269,12 +269,12 @@ size_t virtio_max_dma_size(struct virtio_device *vdev) } EXPORT_SYMBOL_GPL(virtio_max_dma_size); -static void *vring_alloc_queue(struct virtio_device *vdev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) +static void *vring_alloc_queue(struct virtio_device *vdev, + struct device *parent, size_t size, + dma_addr_t *dma_handle, gfp_t flag) { if (vring_use_dma_api(vdev)) { - return dma_alloc_coherent(vdev->dev.parent, size, - dma_handle, flag); + return dma_alloc_coherent(parent, size, dma_handle, flag); } else { void *queue = alloc_pages_exact(PAGE_ALIGN(size), flag); @@ -859,6 +859,7 @@ static struct virtqueue *vring_create_virtqueue_split( dma_addr_t dma_addr; size_t queue_size_in_bytes; struct vring vring; + struct device *parent = vdev->dev.parent; /* We assume num is a power of 2. */ if (num & (num - 1)) { @@ -868,7 +869,8 @@ static struct virtqueue *vring_create_virtqueue_split( /* TODO: allocate each queue chunk individually */ for (; num && vring_size(num, vring_align) > PAGE_SIZE; num /= 2) { - queue = vring_alloc_queue(vdev, vring_size(num, vring_align), + queue = vring_alloc_queue(vdev, parent, + vring_size(num, vring_align), &dma_addr, GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO); if (queue) @@ -882,7 +884,8 @@ static struct virtqueue *vring_create_virtqueue_split( if (!queue) { /* Try to get a single page. You are my only hope! */ - queue = vring_alloc_queue(vdev, vring_size(num, vring_align), + queue = vring_alloc_queue(vdev, parent, + vring_size(num, vring_align), &dma_addr, GFP_KERNEL|__GFP_ZERO); } if (!queue) @@ -1569,10 +1572,11 @@ static struct virtqueue *vring_create_virtqueue_packed( dma_addr_t ring_dma_addr, driver_event_dma_addr, device_event_dma_addr; size_t ring_size_in_bytes, event_size_in_bytes; unsigned int i; + struct device *parent = vdev->dev.parent; ring_size_in_bytes = num * sizeof(struct vring_packed_desc); - ring = vring_alloc_queue(vdev, ring_size_in_bytes, + ring = vring_alloc_queue(vdev, parent, ring_size_in_bytes, &ring_dma_addr, GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO); if (!ring) @@ -1580,13 +1584,13 @@ static struct virtqueue *vring_create_virtqueue_packed( event_size_in_bytes = sizeof(struct vring_packed_desc_event); - driver = vring_alloc_queue(vdev, event_size_in_bytes, + driver = vring_alloc_queue(vdev, parent, event_size_in_bytes, &driver_event_dma_addr, GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO); if (!driver) goto err_driver; - device = vring_alloc_queue(vdev, event_size_in_bytes, + device = vring_alloc_queue(vdev, parent, event_size_in_bytes, &device_event_dma_addr, GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO); if (!device) -- 2.19.1
Signed-off-by: Jason Wang <jasowang at redhat.com> --- drivers/virtio/virtio_ring.c | 16 ++++++++++++++-- include/linux/virtio_config.h | 2 ++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 51d83f4d7c32..ddeef7421d4d 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -183,6 +183,9 @@ struct vring_virtqueue { /* DMA, allocation, and size information */ bool we_own_ring; + /* Parent device for doing DMA */ + struct device *parent; + #ifdef DEBUG /* They're supposed to lock for us. */ unsigned int in_use; @@ -318,7 +321,7 @@ static void vring_free_queue(struct virtio_device *vdev, size_t size, */ static inline struct device *vring_dma_dev(const struct vring_virtqueue *vq) { - return vq->vq.vdev->dev.parent; + return vq->parent; } /* Map one sg entry. */ @@ -1554,6 +1557,14 @@ static void *virtqueue_detach_unused_buf_packed(struct virtqueue *_vq) return NULL; } +static struct device *vring_get_parent(struct virtio_device *vdev, int index) +{ + if (vdev->config->get_vq_parent) + return vdev->config->get_vq_parent(vdev, index); + else + return vdev->dev.parent; +} + static struct virtqueue *vring_create_virtqueue_packed( unsigned int index, unsigned int num, @@ -1572,7 +1583,7 @@ static struct virtqueue *vring_create_virtqueue_packed( dma_addr_t ring_dma_addr, driver_event_dma_addr, device_event_dma_addr; size_t ring_size_in_bytes, event_size_in_bytes; unsigned int i; - struct device *parent = vdev->dev.parent; + struct device *parent = vring_get_parent(vdev, index); ring_size_in_bytes = num * sizeof(struct vring_packed_desc); @@ -1613,6 +1624,7 @@ static struct virtqueue *vring_create_virtqueue_packed( vq->num_added = 0; vq->packed_ring = true; vq->use_dma_api = vring_use_dma_api(vdev); + vq->parent = parent; list_add_tail(&vq->vq.list, &vdev->vqs); #ifdef DEBUG vq->in_use = false; diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index bb4cc4910750..a95af83aad9f 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -65,6 +65,7 @@ struct irq_affinity; * the caller can then copy. * @set_vq_affinity: set the affinity for a virtqueue (optional). * @get_vq_affinity: get the affinity for a virtqueue (optional). + * @get_vq_parent: get the parent device for a virtqueue (optional). */ typedef void vq_callback_t(struct virtqueue *); struct virtio_config_ops { @@ -88,6 +89,7 @@ struct virtio_config_ops { const struct cpumask *cpu_mask); const struct cpumask *(*get_vq_affinity)(struct virtio_device *vdev, int index); + struct device *(*get_vq_parent)(struct virtio_device *vdev, int index); }; /* If driver didn't advertise the feature, it will never appear. */ -- 2.19.1