Thread (17 messages) 17 messages, 5 authors, 2021-10-31

Re: [PATCH 1/3] virtio: cache indirect desc for split

From: "Michael S. Tsirkin" <mst@redhat.com>
Date: 2021-10-31 14:47:43
Also in: netdev

On Thu, Oct 28, 2021 at 02:16:03PM +0800, Xuan Zhuo wrote:
quoted hunk ↗ jump to hunk
On Thu, 28 Oct 2021 10:16:10 +0800, Jason Wang [off-list ref] wrote:
quoted
On Thu, Oct 28, 2021 at 1:07 AM Michael S. Tsirkin [off-list ref] wrote:
quoted
On Wed, Oct 27, 2021 at 02:19:11PM +0800, Xuan Zhuo wrote:
quoted
In the case of using indirect, indirect desc must be allocated and
released each time, which increases a lot of cpu overhead.

Here, a cache is added for indirect. If the number of indirect desc to be
applied for is less than VIRT_QUEUE_CACHE_DESC_NUM, the desc array with
the size of VIRT_QUEUE_CACHE_DESC_NUM is fixed and cached for reuse.

Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
---
 drivers/virtio/virtio.c      |  6 ++++
 drivers/virtio/virtio_ring.c | 63 ++++++++++++++++++++++++++++++------
 include/linux/virtio.h       | 10 ++++++
 3 files changed, 70 insertions(+), 9 deletions(-)
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 0a5b54034d4b..04bcb74e5b9a 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -431,6 +431,12 @@ bool is_virtio_device(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(is_virtio_device);

+void virtio_use_desc_cache(struct virtio_device *dev, bool val)
+{
+     dev->desc_cache = val;
+}
+EXPORT_SYMBOL_GPL(virtio_use_desc_cache);
+
 void unregister_virtio_device(struct virtio_device *dev)
 {
      int index = dev->index; /* save for after device release */
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index dd95dfd85e98..0b9a8544b0e8 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -117,6 +117,10 @@ struct vring_virtqueue {
      /* Hint for event idx: already triggered no need to disable. */
      bool event_triggered;

+     /* Is indirect cache used? */
+     bool use_desc_cache;
+     void *desc_cache_chain;
+
      union {
              /* Available for split ring */
              struct {
@@ -423,12 +427,47 @@ static unsigned int vring_unmap_one_split(const struct vring_virtqueue *vq,
      return extra[i].next;
 }

-static struct vring_desc *alloc_indirect_split(struct virtqueue *_vq,
+#define VIRT_QUEUE_CACHE_DESC_NUM 4
+
+static void desc_cache_chain_free_split(void *chain)
+{
+     struct vring_desc *desc;
+
+     while (chain) {
+             desc = chain;
+             chain = (void *)desc->addr;
+             kfree(desc);
+     }
+}
+
+static void desc_cache_put_split(struct vring_virtqueue *vq,
+                              struct vring_desc *desc, int n)
+{
+     if (vq->use_desc_cache && n <= VIRT_QUEUE_CACHE_DESC_NUM) {
+             desc->addr = (u64)vq->desc_cache_chain;
+             vq->desc_cache_chain = desc;
+     } else {
+             kfree(desc);
+     }
+}
+

So I have a question here. What happens if we just do:

if (n <= VIRT_QUEUE_CACHE_DESC_NUM) {
        return kmem_cache_alloc(VIRT_QUEUE_CACHE_DESC_NUM * sizeof desc, gfp)
} else {
        return kmalloc_arrat(n, sizeof desc, gfp)
}

A small change and won't we reap most performance benefits?
Yes, I think we need a benchmark to use private cache to see how much
it can help.
I did a test, the code is as follows:

+static void desc_cache_put_packed(struct vring_virtqueue *vq,
+                                  struct vring_packed_desc *desc, int n)
+ {
+       if (n <= VIRT_QUEUE_CACHE_DESC_NUM) {
+               kmem_cache_free(vq->desc_cache, desc);
+       } else {
+               kfree(desc);
+       }

@@ -476,11 +452,14 @@ static struct vring_desc *alloc_indirect_split(struct vring_virtqueue *vq,
         */
        gfp &= ~__GFP_HIGHMEM;

-       desc = kmalloc_array(n, sizeof(struct vring_desc), gfp);
+       if (total_sg <= VIRT_QUEUE_CACHE_DESC_NUM)
+               desc = kmem_cache_alloc(vq->desc_cache, gfp);
+       else
+               desc = kmalloc_array(total_sg, sizeof(struct vring_desc), gfp);
+
	.......

+       vq->desc_cache = kmem_cache_create("virio_desc",
+                                          4 * sizeof(struct vring_desc),
+                                          0, 0, NULL);

The effect is not good, basically there is no improvement, using perf top can
see that the overhead of kmem_cache_alloc/kmem_cache_free is also relatively
large:

         26.91%  [kernel]  [k] virtqueue_add
         15.35%  [kernel]  [k] detach_buf_split
         14.15%  [kernel]  [k] virtnet_xsk_xmit
         13.24%  [kernel]  [k] virtqueue_add_outbuf
       >  9.30%  [kernel]  [k] __slab_free
       >  3.91%  [kernel]  [k] kmem_cache_alloc
          2.85%  [kernel]  [k] virtqueue_get_buf_ctx
       >  2.82%  [kernel]  [k] kmem_cache_free
          2.54%  [kernel]  [k] memset_erms
          2.37%  [kernel]  [k] xsk_tx_peek_desc
          1.20%  [kernel]  [k] virtnet_xsk_run
          0.81%  [kernel]  [k] vring_map_one_sg
          0.69%  [kernel]  [k] __free_old_xmit_ptr
          0.69%  [kernel]  [k] virtqueue_kick_prepare
          0.43%  [kernel]  [k] sg_init_table
          0.41%  [kernel]  [k] sg_next
          0.28%  [kernel]  [k] vring_unmap_one_split
          0.25%  [kernel]  [k] vring_map_single.constprop.34
          0.24%  [kernel]  [k] net_rx_action

Thanks.

How about batching these?  E.g. kmem_cache_alloc_bulk/kmem_cache_free_bulk?

quoted
Thanks
quoted
--
MST
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help