From: Xuan Zhuo <xuanzhuo@linux.alibaba.com> Date: 2021-10-27 06:19:22
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_ring.c | 57 +++++++++++++++++++++++++++++++-----
1 file changed, 49 insertions(+), 8 deletions(-)
@@ -1086,7 +1121,7 @@ static struct vring_packed_desc *alloc_indirect_packed(unsigned int total_sg,*/gfp&=~__GFP_HIGHMEM;-desc=kmalloc_array(total_sg,sizeof(structvring_packed_desc),gfp);+desc=kmalloc_array(n,sizeof(structvring_packed_desc),gfp);returndesc;}
@@ -1106,7 +1141,7 @@ static int virtqueue_add_indirect_packed(struct vring_virtqueue *vq,dma_addr_taddr;head=vq->packed.next_avail_idx;-desc=alloc_indirect_packed(total_sg,gfp);+desc=alloc_indirect_packed(vq,total_sg,gfp);if(unlikely(vq->vq.num_free<1)){pr_debug("Can't add buf len 1 - avail = 0\n");
@@ -1197,7 +1232,7 @@ static int virtqueue_add_indirect_packed(struct vring_virtqueue *vq,for(i=0;i<err_idx;i++)vring_unmap_desc_packed(vq,&desc[i]);-kfree(desc);+desc_cache_put_packed(vq,desc,total_sg);END_USE(vq);return-ENOMEM;
@@ -1422,20 +1457,22 @@ static void detach_buf_packed(struct vring_virtqueue *vq,}if(vq->indirect){-u32len;+u32len,n;/* Free the indirect table, if any, now that it's unmapped. */desc=state->indir_desc;if(!desc)return;+n=len/sizeof(structvring_packed_desc);+if(vq->use_dma_api){len=vq->packed.desc_extra[id].len;-for(i=0;i<len/sizeof(structvring_packed_desc);-i++)+for(i=0;i<n;i++)vring_unmap_desc_packed(vq,&desc[i]);}-kfree(desc);++desc_cache_put_packed(vq,desc,n);state->indir_desc=NULL;}elseif(ctx){*ctx=state->indir_desc;
From: Xuan Zhuo <xuanzhuo@linux.alibaba.com> Date: 2021-10-27 06:19:23
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(-)
@@ -431,6 +431,12 @@ bool is_virtio_device(struct device *dev)}EXPORT_SYMBOL_GPL(is_virtio_device);+voidvirtio_use_desc_cache(structvirtio_device*dev,boolval)+{+dev->desc_cache=val;+}+EXPORT_SYMBOL_GPL(virtio_use_desc_cache);+voidunregister_virtio_device(structvirtio_device*dev){intindex=dev->index;/* save for after device release */
@@ -117,6 +117,10 @@ struct vring_virtqueue {/* Hint for event idx: already triggered no need to disable. */boolevent_triggered;+/* Is indirect cache used? */+booluse_desc_cache;+void*desc_cache_chain;+union{/* Available for split ring */struct{
@@ -508,7 +548,7 @@ static inline int virtqueue_add_split(struct virtqueue *_vq,head=vq->free_head;if(virtqueue_use_indirect(_vq,total_sg))-desc=alloc_indirect_split(_vq,total_sg,gfp);+desc=alloc_indirect_split(vq,total_sg,gfp);else{desc=NULL;WARN_ON_ONCE(total_sg>vq->split.vring.num&&!vq->indirect);
@@ -652,7 +692,7 @@ static inline int virtqueue_add_split(struct virtqueue *_vq,}if(indirect)-kfree(desc);+desc_cache_put_split(vq,desc,total_sg);END_USE(vq);return-ENOMEM;
@@ -717,7 +757,7 @@ static void detach_buf_split(struct vring_virtqueue *vq, unsigned int head,if(vq->indirect){structvring_desc*indir_desc=vq->split.desc_state[head].indir_desc;-u32len;+u32len,n;/* Free the indirect table, if any, now that it's unmapped. */if(!indir_desc)
From: Xuan Zhuo <xuanzhuo@linux.alibaba.com> Date: 2021-10-27 06:19:24
If the VIRTIO_RING_F_INDIRECT_DESC negotiation succeeds, and the number
of sgs used for sending packets is greater than 1. We must constantly
call __kmalloc/kfree to allocate/release desc.
In the case of extremely fast package delivery, the overhead cannot be
ignored:
27.46% [kernel] [k] virtqueue_add
16.66% [kernel] [k] detach_buf_split
16.51% [kernel] [k] virtnet_xsk_xmit
14.04% [kernel] [k] virtqueue_add_outbuf
5.18% [kernel] [k] __kmalloc
4.08% [kernel] [k] kfree
2.80% [kernel] [k] virtqueue_get_buf_ctx
2.22% [kernel] [k] xsk_tx_peek_desc
2.08% [kernel] [k] memset_erms
0.83% [kernel] [k] virtqueue_kick_prepare
0.76% [kernel] [k] virtnet_xsk_run
0.62% [kernel] [k] __free_old_xmit_ptr
0.60% [kernel] [k] vring_map_one_sg
0.53% [kernel] [k] native_apic_mem_write
0.46% [kernel] [k] sg_next
0.43% [kernel] [k] sg_init_table
0.41% [kernel] [k] kmalloc_slab
Compared to not using virtio indirect cache, virtio-net can get a 16%
performance improvement when using indirect desc cache.
In the test case, the CPU where the package is sent has reached 100%.
The following are the PPS in two cases:
indirect desc cache | no cache
3074658 | 2685132
3111866 | 2666118
3152527 | 2653632
3125867 | 2669820
3027147 | 2644464
3069211 | 2669777
3038522 | 2675645
3034507 | 2671302
3102257 | 2685504
3083712 | 2692800
3051771 | 2676928
3080684 | 2695040
3147816 | 2720876
3123887 | 2705492
3180963 | 2699520
3191579 | 2676480
3161670 | 2686272
3189768 | 2692588
3174272 | 2686692
3143434 | 2682416
Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
---
drivers/net/virtio_net.c | 4 ++++
1 file changed, 4 insertions(+)
From: "Michael S. Tsirkin" <mst@redhat.com> Date: 2021-10-27 08:55:26
On Wed, Oct 27, 2021 at 02:19:11PM +0800, Xuan Zhuo wrote:
quoted hunk
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(-)
@@ -431,6 +431,12 @@ bool is_virtio_device(struct device *dev)}EXPORT_SYMBOL_GPL(is_virtio_device);+voidvirtio_use_desc_cache(structvirtio_device*dev,boolval)+{+dev->desc_cache=val;+}+EXPORT_SYMBOL_GPL(virtio_use_desc_cache);+voidunregister_virtio_device(structvirtio_device*dev){intindex=dev->index;/* save for after device release */
@@ -117,6 +117,10 @@ struct vring_virtqueue {/* Hint for event idx: already triggered no need to disable. */boolevent_triggered;+/* Is indirect cache used? */+booluse_desc_cache;+void*desc_cache_chain;+union{/* Available for split ring */struct{
Let's use llist_head and friends please (I am guessing we want
single-linked to avoid writing into indirect buffer after use,
invalidating the cache, but please document why in a comment). Do not
open-code it.
Also hide all casts in inline wrappers.
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(-)
@@ -431,6 +431,12 @@ bool is_virtio_device(struct device *dev)}EXPORT_SYMBOL_GPL(is_virtio_device);+voidvirtio_use_desc_cache(structvirtio_device*dev,boolval)+{+dev->desc_cache=val;+}+EXPORT_SYMBOL_GPL(virtio_use_desc_cache);+voidunregister_virtio_device(structvirtio_device*dev){intindex=dev->index;/* save for after device release */
@@ -117,6 +117,10 @@ struct vring_virtqueue {/* Hint for event idx: already triggered no need to disable. */boolevent_triggered;+/* Is indirect cache used? */+booluse_desc_cache;+void*desc_cache_chain;+union{/* Available for split ring */struct{
How about to make the VIRT_QUEUE_CACHE_DESC_NUM configurable (at least during
driver probing) unless there is a reason that the default value is 4.
Thank you very much!
Dongli Zhang
quoted hunk
+ } else {
+ n = total_sg;
+ }
/*
* We require lowmem mappings for the descriptors because
@@ -437,12 +476,13 @@ static struct vring_desc *alloc_indirect_split(struct virtqueue *_vq, */ gfp &= ~__GFP_HIGHMEM;- desc = kmalloc_array(total_sg, sizeof(struct vring_desc), gfp);+ desc = kmalloc_array(n, sizeof(struct vring_desc), gfp); if (!desc) return NULL;+got: for (i = 0; i < total_sg; i++)- desc[i].next = cpu_to_virtio16(_vq->vdev, i + 1);+ desc[i].next = cpu_to_virtio16(vq->vq.vdev, i + 1); return desc; }
From: "Michael S. Tsirkin" <mst@redhat.com> Date: 2021-10-27 17:07:29
On Wed, Oct 27, 2021 at 02:19:11PM +0800, Xuan Zhuo wrote:
quoted hunk
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(-)
@@ -431,6 +431,12 @@ bool is_virtio_device(struct device *dev)}EXPORT_SYMBOL_GPL(is_virtio_device);+voidvirtio_use_desc_cache(structvirtio_device*dev,boolval)+{+dev->desc_cache=val;+}+EXPORT_SYMBOL_GPL(virtio_use_desc_cache);+voidunregister_virtio_device(structvirtio_device*dev){intindex=dev->index;/* save for after device release */
@@ -117,6 +117,10 @@ struct vring_virtqueue {/* Hint for event idx: already triggered no need to disable. */boolevent_triggered;+/* Is indirect cache used? */+booluse_desc_cache;+void*desc_cache_chain;+union{/* Available for split ring */struct{
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?
--
MST
From: "Michael S. Tsirkin" <mst@redhat.com> Date: 2021-10-27 19:37:08
On Wed, Oct 27, 2021 at 09:33:46AM -0700, Dongli Zhang wrote:
On 10/26/21 11:19 PM, 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(-)
@@ -431,6 +431,12 @@ bool is_virtio_device(struct device *dev)}EXPORT_SYMBOL_GPL(is_virtio_device);+voidvirtio_use_desc_cache(structvirtio_device*dev,boolval)+{+dev->desc_cache=val;+}+EXPORT_SYMBOL_GPL(virtio_use_desc_cache);+voidunregister_virtio_device(structvirtio_device*dev){intindex=dev->index;/* save for after device release */
@@ -117,6 +117,10 @@ struct vring_virtqueue {/* Hint for event idx: already triggered no need to disable. */boolevent_triggered;+/* Is indirect cache used? */+booluse_desc_cache;+void*desc_cache_chain;+union{/* Available for split ring */struct{
How about to make the VIRT_QUEUE_CACHE_DESC_NUM configurable (at least during
driver probing) unless there is a reason that the default value is 4.
Thank you very much!
Dongli Zhang
I would start with some experimentation showing that it actually makes a
difference in performance.
quoted
+ } else {
+ n = total_sg;
+ }
/*
* We require lowmem mappings for the descriptors because
@@ -437,12 +476,13 @@ static struct vring_desc *alloc_indirect_split(struct virtqueue *_vq, */ gfp &= ~__GFP_HIGHMEM;- desc = kmalloc_array(total_sg, sizeof(struct vring_desc), gfp);+ desc = kmalloc_array(n, sizeof(struct vring_desc), gfp); if (!desc) return NULL;+got: for (i = 0; i < total_sg; i++)- desc[i].next = cpu_to_virtio16(_vq->vdev, i + 1);+ desc[i].next = cpu_to_virtio16(vq->vq.vdev, i + 1); return desc; }
From: kernel test robot <hidden> Date: 2021-10-28 00:58:14
Hi Xuan,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on horms-ipvs/master]
[also build test WARNING on linus/master v5.15-rc7 next-20211027]
[cannot apply to mst-vhost/linux-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Xuan-Zhuo/virtio-support-cache-indirect-desc/20211027-142025
base: https://git.kernel.org/pub/scm/linux/kernel/git/horms/ipvs.git master
config: i386-randconfig-s002-20211027 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce:
# apt-get install sparse
# sparse version: v0.6.4-dirty
# https://github.com/0day-ci/linux/commit/8935e116c155fb7d484bad35a42b2ca98f75e384
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Xuan-Zhuo/virtio-support-cache-indirect-desc/20211027-142025
git checkout 8935e116c155fb7d484bad35a42b2ca98f75e384
# save the attached .config to linux build tree
make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=i386 SHELL=/bin/bash drivers/virtio/
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
sparse warnings: (new ones prefixed by >>)
quoted
drivers/virtio/virtio_ring.c:438:26: sparse: sparse: cast from restricted __virtio64
drivers/virtio/virtio_ring.c:447:28: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __virtio64 [usertype] addr @@ got unsigned long long [usertype] @@
From: Jason Wang <hidden> Date: 2021-10-28 02:16:28
On Thu, Oct 28, 2021 at 1:07 AM Michael S. Tsirkin [off-list ref] wrote:
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(-)
@@ -431,6 +431,12 @@ bool is_virtio_device(struct device *dev)}EXPORT_SYMBOL_GPL(is_virtio_device);+voidvirtio_use_desc_cache(structvirtio_device*dev,boolval)+{+dev->desc_cache=val;+}+EXPORT_SYMBOL_GPL(virtio_use_desc_cache);+voidunregister_virtio_device(structvirtio_device*dev){intindex=dev->index;/* save for after device release */
@@ -117,6 +117,10 @@ struct vring_virtqueue {/* Hint for event idx: already triggered no need to disable. */boolevent_triggered;+/* Is indirect cache used? */+booluse_desc_cache;+void*desc_cache_chain;+union{/* Available for split ring */struct{
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.
Thanks
From: kernel test robot <hidden> Date: 2021-10-28 03:51:23
Hi Xuan,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on horms-ipvs/master]
[also build test WARNING on linus/master v5.15-rc7]
[cannot apply to mst-vhost/linux-next next-20211027]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Xuan-Zhuo/virtio-support-cache-indirect-desc/20211027-142025
base: https://git.kernel.org/pub/scm/linux/kernel/git/horms/ipvs.git master
config: i386-randconfig-s002-20211027 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce:
# apt-get install sparse
# sparse version: v0.6.4-dirty
# https://github.com/0day-ci/linux/commit/bb65ceda850ed4592d8a940e01926d5e3d33ae92
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Xuan-Zhuo/virtio-support-cache-indirect-desc/20211027-142025
git checkout bb65ceda850ed4592d8a940e01926d5e3d33ae92
# save the attached .config to linux build tree
make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=i386 SHELL=/bin/bash drivers/virtio/
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
sparse warnings: (new ones prefixed by >>)
drivers/virtio/virtio_ring.c:438:26: sparse: sparse: cast from restricted __virtio64
drivers/virtio/virtio_ring.c:447:28: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __virtio64 [usertype] addr @@ got unsigned long long [usertype] @@
drivers/virtio/virtio_ring.c:447:28: sparse: expected restricted __virtio64 [usertype] addr
drivers/virtio/virtio_ring.c:447:28: sparse: got unsigned long long [usertype]
drivers/virtio/virtio_ring.c:464:49: sparse: sparse: cast from restricted __virtio64
quoted
drivers/virtio/virtio_ring.c:1083:26: sparse: sparse: cast from restricted __le64
drivers/virtio/virtio_ring.c:1092:28: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le64 [usertype] addr @@ got unsigned long long [usertype] @@
drivers/virtio/virtio_ring.c:1092:28: sparse: expected restricted __le64 [usertype] addr
drivers/virtio/virtio_ring.c:1092:28: sparse: got unsigned long long [usertype]
drivers/virtio/virtio_ring.c:1109:49: sparse: sparse: cast from restricted __le64
vim +1083 drivers/virtio/virtio_ring.c
1076
1077 static void desc_cache_chain_free_packed(void *chain)
1078 {
1079 struct vring_packed_desc *desc;
1080
1081 while (chain) {
1082 desc = chain;
1083 chain = (void *)desc->addr;
1084 kfree(desc);
1085 }
1086 }
1087
1088 static void desc_cache_put_packed(struct vring_virtqueue *vq,
1089 struct vring_packed_desc *desc, int n)
1090 {
1091 if (vq->use_desc_cache && n <= VIRT_QUEUE_CACHE_DESC_NUM) {