zcrx RQ improvements and dynamic memory provisioning
Subsystems (4)
Pavel Begunkov
maintainercover zcrx RQ improvements and dynamic memory provisioning
Sending it out mainly to trigger review bots. The first half improves the refill queue implementation and improves refilling limits, which shows up when niovs are heavily fragmented like with large rx pages. The 2nd half adds dynamic backing memory provisioning.
Pavel Begunkov (17):
io_uring/zcrx: scale refilling with large pages io_uring/zcrx: move RQ head/tail to separate cache lines io_uring/zcrx: add RQ iterator
io_uring/zcrx: cache RQ tail
io_uring/zcrx: coalesce same-niov RQEs on refill io_uring/zcrx: constify area_reg on import io_uring/zcrx: add helper for deriving area token io_uring/zcrx: don't pass ifq_reg to area creation io_uring/zcrx: split dmabuf unmap and release io_uring/zcrx: unmap under netdev lock io_uring/zcrx: split append out of area creation io_uring/zcrx: move freelist lock to struct zcrx io_uring/zcrx: array of areas
io_uring/zcrx: pass area_id to __zcrx_create_area() io_uring/zcrx: add dynamic area creation io_urint/zcrx: narrow var scope in io_zcrx_recv_skb() io_uring/zcrx: don't reload skb_shinfo
include/uapi/linux/io_uring/zcrx.h | 7 + io_uring/query.c | 2 +- io_uring/zcrx.c | 445 +++++++++++++++++++++-------- io_uring/zcrx.h | 15 +- 4 files changed, 345 insertions(+), 124 deletions(-)
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 1/17 io_uring/zcrx: scale refilling with large pages
io_zcrx_ring_refill() caps the loop by mixing the max number of allocated netmems and the number of available RQEs together, which caps the number of entries to process the pp cache size. As a result, when niovs are heavily fragmented, the refilling logic allocates only a small number of niovs per call on average and sometimes even none.
Keep a separate counter for the number of processed RQ entries, which is capped by a roughly calculated from the page size value to keep the cache full. And separately break if it allocates enough niovs.
--- io_uring/zcrx.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-)
io_uring/zcrx.c +14 -7
@@ -28,6 +28,13 @@#include "zcrx.h" #include "rsrc.h" +#define ZCRX_MAX_FRAGS_PER_PAGE MAX(PAGE_SIZE / 1024, 1) +/* +* We need a reasonable limit to be able to fill in 64 entries on average +* for 1500 byte MTU. Over-estimate it to keep it pow2. +*/ +#define ZCRX_REFILL_CAP MIN(64 * ZCRX_MAX_FRAGS_PER_PAGE, 1024) +#define IO_ZCRX_AREA_SUPPORTED_FLAGS (IORING_ZCRX_AREA_DMABUF)#define IO_DMA_ATTR (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING)
@@ -1125,17 +1132,15 @@ static unsigned io_zcrx_ring_refill(struct page_pool *pp, {struct zcrx_rq *rq = &ifq->rq;unsigned int mask = rq->nr_entries -1 ; -unsigned int entries; +unsigned int rqes_left;unsigned allocated =0 ;guard (spinlock_bh)(&rq->lock); - entries =zcrx_rq_entries (rq); - entries =min_t (unsigned, entries, to_alloc); -if (unlikely (!entries)) -return 0 ; + rqes_left =zcrx_rq_entries (rq); + rqes_left =min_t (unsigned, rqes_left,ZCRX_REFILL_CAP ); - do { +for (; rqes_left; rqes_left--) {struct io_uring_zcrx_rqe *rqe =zcrx_next_rqe (rq, mask);struct net_iov *niov;netmem_ref netmem;
@@ -1156,7 +1161,9 @@ static unsigned io_zcrx_ring_refill(struct page_pool *pp, netmems[allocated] = netmem; allocated++; - }while (--entries); +if (allocated >= to_alloc) +break ; + }smp_store_release (&rq->ring->head, rq->cached_head);return allocated;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 2/17 io_uring/zcrx: move RQ head/tail to separate cache lines
In reply to Pavel BegunkovRQ head and tail are currently put into the same cache line, which can cause false sharing problems when refill is run on another CPU. Put them into separate cache lines.
--- io_uring/query.c | 2 +- io_uring/zcrx.c | 8 ++++---- io_uring/zcrx.h | 7 ++++++- 3 files changed, 11 insertions(+), 6 deletions(-)
io_uring/query.c +1 -1
@@ -38,7 +38,7 @@ static ssize_t io_query_zcrx(union io_query_data *data) e->register_flags =ZCRX_SUPPORTED_REG_FLAGS ; e->area_flags =IORING_ZCRX_AREA_DMABUF ; e->nr_ctrl_opcodes = __ZCRX_CTRL_LAST; - e->rq_hdr_size =sizeof (struct io_uring ); + e->rq_hdr_size =sizeof (struct zcrx_rq_hdr ); e->rq_hdr_alignment =L1_CACHE_BYTES ; e->features =ZCRX_FEATURES ; e->__resv2 =0 ;
io_uring/zcrx.c +4 -4
@@ -380,9 +380,9 @@ static void io_zcrx_get_niov_uref(struct net_iov *niov)static void io_fill_zcrx_offsets (struct io_uring_zcrx_offsets *offsets) { - offsets->head = offsetof(struct io_uring , head); - offsets->tail = offsetof(struct io_uring , tail); - offsets->rqes =ALIGN (sizeof (struct io_uring ),L1_CACHE_BYTES ); + offsets->head = offsetof(struct zcrx_rq_hdr , head); + offsets->tail = offsetof(struct zcrx_rq_hdr , tail); + offsets->rqes =ALIGN (sizeof (struct zcrx_rq_hdr ),L1_CACHE_BYTES ); } static int io_allocate_rbuf_ring(struct io_ring_ctx *ctx,
@@ -410,7 +410,7 @@ static int io_allocate_rbuf_ring(struct io_ring_ctx *ctx,return ret; ptr =io_region_get_ptr (&ifq->rq_region); - ifq->rq.ring = (struct io_uring *)ptr; + ifq->rq.ring = (struct zcrx_rq_hdr *)ptr; ifq->rq.rqes = (struct io_uring_zcrx_rqe *)(ptr + off);memset (ifq->rq.ring,0 ,sizeof (*ifq->rq.ring));
io_uring/zcrx.h +6 -1
@@ -43,9 +43,14 @@ struct io_zcrx_area {struct io_zcrx_mem mem; }; +struct zcrx_rq_hdr { +u32 head____cacheline_aligned_in_smp ; +u32 tail____cacheline_aligned_in_smp ; +}; +struct zcrx_rq {spinlock_t lock; -struct io_uring *ring; +struct zcrx_rq_hdr *ring;struct io_uring_zcrx_rqe *rqes;u32 cached_head;u32 nr_entries;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 3/17 io_uring/zcrx: add RQ iterator
In reply to Pavel BegunkovAdd a iterator structure and helper functions for the refill queue processing to avoid polluting io_zcrx_ring_refill() with extra state and logic once it's extended in following patches.
--- io_uring/zcrx.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-)
io_uring/zcrx.c +26 -6
@@ -1088,6 +1088,10 @@ void io_unregister_zcrx(struct io_ring_ctx *ctx)xa_destroy (&ctx->zcrx_ctxs); } +struct zcrx_rq_iter { +int rqes_left; +}; +static inline u32 zcrx_rq_entries (struct zcrx_rq *rq) {u32 entries;
@@ -1103,6 +1107,24 @@ static struct io_uring_zcrx_rqe *zcrx_next_rqe(struct zcrx_rq *rq, unsigned maskreturn &rq->rqes[idx]; } +static inline void zcrx_rq_iter_init (struct zcrx_rq_iter *it, +struct zcrx_rq *rq) +{ + it->rqes_left =min_t (unsigned,zcrx_rq_entries (rq),ZCRX_REFILL_CAP ); +} + +static inline bool zcrx_rq_iter_next (struct zcrx_rq_iter *it, +struct zcrx_rq *rq, +struct io_uring_zcrx_rqe **rqe) +{ + it->rqes_left--; +if (unlikely (it->rqes_left <0 )) +return false; + + *rqe =zcrx_next_rqe (rq, rq->nr_entries -1 ); +return true; +} +static inline bool io_parse_rqe (struct io_uring_zcrx_rqe *rqe,struct io_zcrx_ifq *ifq,struct net_iov **ret_niov)
@@ -1131,17 +1153,15 @@ static unsigned io_zcrx_ring_refill(struct page_pool *pp, netmem_ref *netmems, unsigned to_alloc) {struct zcrx_rq *rq = &ifq->rq; -unsigned int mask = rq->nr_entries -1 ; -unsigned int rqes_left; +struct io_uring_zcrx_rqe *rqe; +struct zcrx_rq_iter it;unsigned allocated =0 ;guard (spinlock_bh)(&rq->lock); - rqes_left =zcrx_rq_entries (rq); - rqes_left =min_t (unsigned, rqes_left,ZCRX_REFILL_CAP ); +zcrx_rq_iter_init (&it, rq); -for (; rqes_left; rqes_left--) { -struct io_uring_zcrx_rqe *rqe =zcrx_next_rqe (rq, mask); +while (zcrx_rq_iter_next (&it, rq, &rqe)) {struct net_iov *niov;netmem_ref netmem;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 4/17 io_uring/zcrx: cache RQ tail
In reply to Pavel BegunkovThe RQ tail is updated by the user space. Cache it to reduce cache line bouncing. Refilling now tries to exhaust the previous batch of rqes, but since it could be too low, the iterator is allowed to recalculate the rqes to process once after synching the tail value.
--- io_uring/zcrx.c | 27 +++++++++++++++++++++------ io_uring/zcrx.h | 1 + 2 files changed, 22 insertions(+), 6 deletions(-)
io_uring/zcrx.c +21 -6
@@ -1090,16 +1090,22 @@ void io_unregister_zcrx(struct io_ring_ctx *ctx)struct zcrx_rq_iter {int rqes_left; +bool flushed; }; -static inline u32 zcrx_rq_entries (struct zcrx_rq *rq) +static inline u32 __zcrx_rq_entries (struct zcrx_rq *rq) { -u32 entries; +u32 entries = rq->cached_tail - rq->cached_head; - entries =smp_load_acquire (&rq->ring->tail) - rq->cached_head;return min (entries, rq->nr_entries); } +static inline u32 zcrx_rq_entries (struct zcrx_rq *rq) +{ + rq->cached_tail =smp_load_acquire (&rq->ring->tail); +return __zcrx_rq_entries (rq); +} +static struct io_uring_zcrx_rqe *zcrx_next_rqe (struct zcrx_rq *rq,unsigned mask) {unsigned int idx = rq->cached_head++ & mask;
@@ -1110,7 +1116,8 @@ static struct io_uring_zcrx_rqe *zcrx_next_rqe(struct zcrx_rq *rq, unsigned maskstatic inline void zcrx_rq_iter_init (struct zcrx_rq_iter *it,struct zcrx_rq *rq) { - it->rqes_left =min_t (unsigned,zcrx_rq_entries (rq),ZCRX_REFILL_CAP ); + it->rqes_left =min_t (unsigned,__zcrx_rq_entries (rq),ZCRX_REFILL_CAP ); + it->flushed = false; } static inline bool zcrx_rq_iter_next(struct zcrx_rq_iter *it,
@@ -1118,8 +1125,16 @@ static inline bool zcrx_rq_iter_next(struct zcrx_rq_iter *it, struct io_uring_zcrx_rqe **rqe) { it->rqes_left--; -if (unlikely (it->rqes_left <0 )) -return false; +if (unlikely (it->rqes_left <0 )) { +if (it->flushed) +return false; + rq->cached_tail =smp_load_acquire (&rq->ring->tail); + it->rqes_left =min_t (unsigned,__zcrx_rq_entries (rq), +ZCRX_REFILL_CAP ); + it->flushed = true; +if (--it->rqes_left <0 ) +return false; + } *rqe =zcrx_next_rqe (rq, rq->nr_entries -1 );return true;
io_uring/zcrx.h +1 -0
@@ -53,6 +53,7 @@ struct zcrx_rq {struct zcrx_rq_hdr *ring;struct io_uring_zcrx_rqe *rqes;u32 cached_head; +u32 cached_tail;u32 nr_entries; };
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 5/17 io_uring/zcrx: coalesce same-niov RQEs on refill
In reply to Pavel BegunkovWith large rx piages I often see >10 sequential RQEs referring to the same niov. Instead of putting them one by one, count such RQEs during parsing and batch refcounting for the niov.
--- io_uring/zcrx.c | 56 +++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 20 deletions(-)
io_uring/zcrx.c +36 -20
@@ -359,16 +359,16 @@ static inline atomic_t *io_get_user_counter(struct net_iov *niov)return &area->user_refs[net_iov_idx (niov)]; } -static bool io_zcrx_put_niov_uref (struct net_iov *niov) +static bool io_zcrx_put_niov_uref (struct net_iov *niov,unsigned refs) {atomic_t *uref =io_get_user_counter (niov);int old; old =atomic_read (uref);do { -if (unlikely (old ==0 )) +if (unlikely (old < refs))return false; - }while (!atomic_try_cmpxchg (uref, &old, old -1 )); + }while (!atomic_try_cmpxchg (uref, &old, old - refs));return true; }
@@ -1163,6 +1163,22 @@ static inline bool io_parse_rqe(struct io_uring_zcrx_rqe *rqe,return true; } +static bool zcrx_put_refill_niov (struct net_iov *niov,struct page_pool *pp, +unsigned refs) +{ +netmem_ref netmem =net_iov_to_netmem (niov); + +if (!io_zcrx_put_niov_uref (niov, refs)) +return false; +if (page_pool_unref_netmem (netmem, refs) !=0 ) +return false; +if (unlikely (niov->desc.pp != pp)) { +io_zcrx_return_niov (niov); +return false; + } +return true; +} +static unsigned io_zcrx_ring_refill (struct page_pool *pp,struct io_zcrx_ifq *ifq,netmem_ref *netmems,unsigned to_alloc)
@@ -1170,34 +1186,34 @@ static unsigned io_zcrx_ring_refill(struct page_pool *pp,struct zcrx_rq *rq = &ifq->rq;struct io_uring_zcrx_rqe *rqe;struct zcrx_rq_iter it; +struct net_iov *niov =NULL ; +unsigned niov_refs =0 ;unsigned allocated =0 ;guard (spinlock_bh)(&rq->lock);zcrx_rq_iter_init (&it, rq); -while (zcrx_rq_iter_next (&it, rq, &rqe)) { -struct net_iov *niov; -netmem_ref netmem; +while (allocated < to_alloc -1 &&zcrx_rq_iter_next (&it, rq, &rqe)) { +struct net_iov *next_niov; -if (!io_parse_rqe (rqe, ifq, &niov)) -continue ; -if (!io_zcrx_put_niov_uref (niov)) +if (!io_parse_rqe (rqe, ifq, &next_niov))continue ; - - netmem =net_iov_to_netmem (niov); -if (!page_pool_unref_and_test (netmem)) -continue ; - -if (unlikely (niov->desc.pp != pp)) { -io_zcrx_return_niov (niov); +if (niov == next_niov) { + niov_refs++;continue ; } +if (niov &&zcrx_put_refill_niov (niov, pp, niov_refs)) { + netmems[allocated] =net_iov_to_netmem (niov); + allocated++; + } + niov = next_niov; + niov_refs =1 ; + } - netmems[allocated] = netmem; +if (niov &&zcrx_put_refill_niov (niov, pp, niov_refs)) { + netmems[allocated] =net_iov_to_netmem (niov); allocated++; -if (allocated >= to_alloc) -break ; }smp_store_release (&rq->ring->head, rq->cached_head);
@@ -1401,7 +1417,7 @@ static void zcrx_return_buffers(netmem_ref *netmems, unsigned nr)netmem_ref netmem = netmems[i];struct net_iov *niov =netmem_to_net_iov (netmem); -if (!io_zcrx_put_niov_uref (niov)) +if (!io_zcrx_put_niov_uref (niov,1 ))continue ;if (!page_pool_unref_and_test (netmem))continue ;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 6/17 io_uring/zcrx: constify area_reg on import
In reply to Pavel Begunkovio_import_area() doesn't modify its struct io_uring_zcrx_area_reg argument, add const to enforce that, it'll make later modifications easier.
--- io_uring/zcrx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
io_uring/zcrx.c +3 -3
@@ -136,7 +136,7 @@ static void io_release_dmabuf(struct io_zcrx_mem *mem)static int io_import_dmabuf (struct io_zcrx_ifq *ifq,struct io_zcrx_mem *mem, -struct io_uring_zcrx_area_reg *area_reg) +const struct io_uring_zcrx_area_reg *area_reg) {unsigned long off = (unsigned long )area_reg->addr;unsigned long len = (unsigned long )area_reg->len;
@@ -208,7 +208,7 @@ static unsigned long io_count_account_pages(struct page **pages, unsigned nr_pagstatic int io_import_umem (struct io_zcrx_ifq *ifq,struct io_zcrx_mem *mem, -struct io_uring_zcrx_area_reg *area_reg) +const struct io_uring_zcrx_area_reg *area_reg) {struct page **pages;int nr_pages, ret;
@@ -274,7 +274,7 @@ static void io_release_area_mem(struct io_zcrx_mem *mem)static int io_import_area (struct io_zcrx_ifq *ifq,struct io_zcrx_mem *mem, -struct io_uring_zcrx_area_reg *area_reg) +const struct io_uring_zcrx_area_reg *area_reg) {int ret;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 7/17 io_uring/zcrx: add helper for deriving area token
In reply to Pavel BegunkovAdd zcrx_area_id_to_token() to deduplicate the way the area token is calculated out of the area index.
--- io_uring/zcrx.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
io_uring/zcrx.c +7 -2
@@ -39,6 +39,11 @@#define IO_DMA_ATTR (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) +static inline u64 zcrx_area_id_to_token (u32 area_id) +{ +return (u64 )area_id <<IORING_ZCRX_AREA_SHIFT ; +} +static inline struct io_zcrx_ifq *io_pp_to_ifq (struct page_pool *pp) {return pp->mp_priv;
@@ -527,7 +532,7 @@ static int io_zcrx_create_area(struct io_zcrx_ifq *ifq, area->free_count = nr_iovs;/* we're only supporting one area per ifq for now */ area->area_id =0 ; - area_reg->rq_area_token = (u64 )area->area_id <<IORING_ZCRX_AREA_SHIFT ; + area_reg->rq_area_token =zcrx_area_id_to_token (area->area_id);spin_lock_init (&area->freelist_lock); ret =io_zcrx_append_area (ifq, area);
@@ -1525,7 +1530,7 @@ static bool io_zcrx_queue_cqe(struct io_kiocb *req, struct net_iov *niov, area =io_zcrx_iov_to_area (niov); offset = off + (net_iov_idx (niov) << ifq->niov_shift); rcqe = (struct io_uring_zcrx_cqe *)(cqe +1 ); - rcqe->off = offset + ((u64 )area->area_id <<IORING_ZCRX_AREA_SHIFT ); + rcqe->off = offset +zcrx_area_id_to_token (area->area_id); rcqe->__pad =0 ;return true; }
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 8/17 io_uring/zcrx: don't pass ifq_reg to area creation
In reply to Pavel BegunkovWe might want to create an area without having an instance of struct io_uring_zcrx_ifq_reg. Extract a helper that doesn't have the ifq registration structure as an argument but takes the buf length explicitly.
--- io_uring/zcrx.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
io_uring/zcrx.c +14 -6
@@ -460,21 +460,22 @@ static int io_zcrx_append_area(struct io_zcrx_ifq *ifq,return 0 ; } -static int io_zcrx_create_area (struct io_zcrx_ifq *ifq, +static int __zcrx_create_area (struct io_zcrx_ifq *ifq,struct io_uring_zcrx_area_reg *area_reg, -struct io_uring_zcrx_ifq_reg *reg) +u32 rx_buf_len) {int buf_size_shift =PAGE_SHIFT ;struct io_zcrx_area *area;unsigned nr_iovs;int i, ret; -if (reg->rx_buf_len) { -if (!is_power_of_2 (reg->rx_buf_len) || - reg->rx_buf_len <PAGE_SIZE ) +if (rx_buf_len) { +if (!is_power_of_2 (rx_buf_len) || rx_buf_len <PAGE_SIZE )return -EINVAL ; - buf_size_shift =ilog2 (reg->rx_buf_len); + buf_size_shift =ilog2 (rx_buf_len); } +if (WARN_ON_ONCE (ifq->niov_shift)) +return -EINVAL ;if (!ifq->dev && buf_size_shift !=PAGE_SHIFT )return -EOPNOTSUPP ;
@@ -544,6 +545,13 @@ static int io_zcrx_create_area(struct io_zcrx_ifq *ifq,return ret; } +static int io_zcrx_create_area (struct io_zcrx_ifq *ifq, +struct io_uring_zcrx_area_reg *area_reg, +struct io_uring_zcrx_ifq_reg *reg) +{ +return __zcrx_create_area (ifq, area_reg, reg->rx_buf_len); +} +static struct io_zcrx_ifq *io_zcrx_ifq_alloc (struct io_ring_ctx *ctx) {struct io_zcrx_ifq *ifq;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 9/17 io_uring/zcrx: split dmabuf unmap and release
In reply to Pavel BegunkovUntil now unmapping and destroying dmabuf were the same thing. To keep it consistent with non-dmabuf, split it into two separate helpers. Unmap destroys mappings and attachements as it should, and release only putting down the dmabuf fd reference.
--- io_uring/zcrx.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
io_uring/zcrx.c +11 -6
@@ -121,21 +121,25 @@ static int io_populate_area_dma(struct io_zcrx_ifq *ifq,return 0 ; } -static void io_release_dmabuf (struct io_zcrx_mem *mem) +static void io_unmap_dmabuf (struct io_zcrx_mem *mem) {if (!IS_ENABLED (CONFIG_DMA_SHARED_BUFFER ))return ; -if (mem->sgt)dma_buf_unmap_attachment_unlocked (mem->attach, mem->sgt,DMA_FROM_DEVICE );if (mem->attach)dma_buf_detach (mem->dmabuf, mem->attach); -if (mem->dmabuf) -dma_buf_put (mem->dmabuf); - mem->sgt =NULL ; mem->attach =NULL ; +} + +static void io_release_dmabuf (struct io_zcrx_mem *mem) +{ +if (!IS_ENABLED (CONFIG_DMA_SHARED_BUFFER )) +return ; +if (mem->dmabuf) +dma_buf_put (mem->dmabuf); mem->dmabuf =NULL ; }
@@ -190,6 +194,7 @@ static int io_import_dmabuf(struct io_zcrx_ifq *ifq, mem->size = len;return 0 ; err: +io_unmap_dmabuf (mem);io_release_dmabuf (mem);return ret; }
@@ -317,7 +322,7 @@ static void io_zcrx_unmap_area(struct io_zcrx_ifq *ifq, }if (area->mem.is_dmabuf) { -io_release_dmabuf (&area->mem); +io_unmap_dmabuf (&area->mem); }else {dma_unmap_sgtable (ifq->dev, &area->mem.page_sg_table,DMA_FROM_DEVICE ,IO_DMA_ATTR );
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 10/17 io_uring/zcrx: unmap under netdev lock
In reply to Pavel BegunkovMake sure we unmap areas while closing a queue.
--- io_uring/zcrx.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
io_uring/zcrx.c +14 -8
@@ -311,6 +311,9 @@ static void io_zcrx_unmap_area(struct io_zcrx_ifq *ifq, {int i; +if (!area) +return ; +guard (mutex)(&ifq->pp_lock);if (!area->is_mapped)return ;
@@ -438,7 +441,8 @@ static void io_free_rbuf_ring(struct io_zcrx_ifq *ifq)static void io_zcrx_free_area (struct io_zcrx_ifq *ifq,struct io_zcrx_area *area) { -io_zcrx_unmap_area (ifq, area); +if (WARN_ON_ONCE (area->is_mapped)) +return ;io_release_area_mem (&area->mem);if (area->mem.account_pages)
@@ -545,8 +549,10 @@ static int __zcrx_create_area(struct io_zcrx_ifq *ifq,if (!ret)return 0 ; err: -if (area) +if (area) { +io_zcrx_unmap_area (ifq, area);io_zcrx_free_area (ifq, area); + }return ret; }
@@ -600,11 +606,12 @@ static void io_close_queue(struct io_zcrx_ifq *ifq) }if (netdev) { -if (ifq->if_rxq !=-1 ) { -netdev_lock (netdev); +netdev_lock (netdev); +if (ifq->if_rxq !=-1 )netif_mp_close_rxq (netdev, ifq->if_rxq, &p); -netdev_unlock (netdev); - } + +io_zcrx_unmap_area (ifq, ifq->area); +netdev_unlock (netdev);netdev_put (netdev, &netdev_tracker); } ifq->if_rxq =-1 ;
@@ -1389,8 +1396,7 @@ static void io_pp_uninstall(void *mp_priv, struct netdev_rx_queue *rxq)struct io_zcrx_ifq *ifq = mp_priv;io_zcrx_drop_netdev (ifq); -if (ifq->area) -io_zcrx_unmap_area (ifq, ifq->area); +io_zcrx_unmap_area (ifq, ifq->area); p->mp_ops =NULL ; p->mp_priv =NULL ;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 11/17 io_uring/zcrx: split append out of area creation
In reply to Pavel BegunkovA preparation patch, move appending an area from __zcrx_create_area() to the caller.
--- io_uring/zcrx.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-)
io_uring/zcrx.c +16 -5
@@ -471,6 +471,7 @@ static int io_zcrx_append_area(struct io_zcrx_ifq *ifq,static int __zcrx_create_area (struct io_zcrx_ifq *ifq,struct io_uring_zcrx_area_reg *area_reg, +struct io_zcrx_area **res_area,u32 rx_buf_len) {int buf_size_shift =PAGE_SHIFT ;
@@ -544,10 +545,8 @@ static int __zcrx_create_area(struct io_zcrx_ifq *ifq, area->area_id =0 ; area_reg->rq_area_token =zcrx_area_id_to_token (area->area_id);spin_lock_init (&area->freelist_lock); - - ret =io_zcrx_append_area (ifq, area); -if (!ret) -return 0 ; + *res_area = area; +return 0 ; err:if (area) {io_zcrx_unmap_area (ifq, area);
@@ -560,7 +559,19 @@ static int io_zcrx_create_area(struct io_zcrx_ifq *ifq,struct io_uring_zcrx_area_reg *area_reg, struct io_uring_zcrx_ifq_reg *reg) { - return __zcrx_create_area(ifq, area_reg, reg->rx_buf_len); + struct io_zcrx_area *area; +int ret; + + ret =__zcrx_create_area (ifq, area_reg, &area, reg->rx_buf_len); +if (ret) +return ret; + + ret =io_zcrx_append_area (ifq, area); +if (ret) { +io_zcrx_free_area (ifq, area); +return ret; + } +return 0 ; }static struct io_zcrx_ifq *io_zcrx_ifq_alloc (struct io_ring_ctx *ctx)
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 12/17 io_uring/zcrx: move freelist lock to struct zcrx
In reply to Pavel Begunkovfreelist_lock, which protects slow path allocations, is currently stored in struct io_zcrx_area. Once we add support for multiple queues, we'll need a lock in the zcrx ctx, move it there.
--- io_uring/zcrx.c | 14 +++++++------- io_uring/zcrx.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-)
io_uring/zcrx.c +7 -7
@@ -544,7 +544,6 @@ static int __zcrx_create_area(struct io_zcrx_ifq *ifq,/* we're only supporting one area per ifq for now */ area->area_id =0 ; area_reg->rq_area_token =zcrx_area_id_to_token (area->area_id); -spin_lock_init (&area->freelist_lock); *res_area = area;return 0 ; err:
@@ -585,6 +584,7 @@ static struct io_zcrx_ifq *io_zcrx_ifq_alloc(struct io_ring_ctx *ctx) ifq->if_rxq =-1 ;spin_lock_init (&ifq->ctx_lock);spin_lock_init (&ifq->rq.lock); +spin_lock_init (&ifq->alloc_lock);mutex_init (&ifq->pp_lock);refcount_set (&ifq->refs,1 );refcount_set (&ifq->user_refs,1 );
@@ -659,8 +659,9 @@ static void io_put_zcrx_ifq(struct io_zcrx_ifq *ifq)static void io_zcrx_return_niov_freelist (struct net_iov *niov) {struct io_zcrx_area *area =io_zcrx_iov_to_area (niov); +struct io_zcrx_ifq *ifq = area->ifq; -guard (spinlock_bh)(&area->freelist_lock); +guard (spinlock_bh)(&ifq->alloc_lock);if (WARN_ON_ONCE (area->free_count >= area->nia.num_niovs))return ; area->freelist[area->free_count++] =net_iov_idx (niov);
@@ -670,7 +671,7 @@ static struct net_iov *zcrx_get_free_niov(struct io_zcrx_area *area) {unsigned niov_idx; -lockdep_assert_held (&area->freelist_lock); +lockdep_assert_held (&area->ifq->alloc_lock);if (unlikely (!area->free_count))return NULL ;
@@ -1262,7 +1263,7 @@ static unsigned io_zcrx_refill_slow(struct page_pool *pp, struct io_zcrx_ifq *ifstruct io_zcrx_area *area = ifq->area;unsigned allocated =0 ; -guard (spinlock_bh)(&area->freelist_lock); +guard (spinlock_bh)(&ifq->alloc_lock);for (allocated =0 ; allocated < to_alloc; allocated++) {struct net_iov *niov =zcrx_get_free_niov (area);
@@ -1567,14 +1568,13 @@ static bool io_zcrx_queue_cqe(struct io_kiocb *req, struct net_iov *niov,static struct net_iov *io_alloc_fallback_niov (struct io_zcrx_ifq *ifq) { -struct io_zcrx_area *area = ifq->area;struct net_iov *niov =NULL ;if (!ifq->kern_readable)return NULL ; -scoped_guard (spinlock_bh, &area->freelist_lock) - niov = zcrx_get_free_niov(area); +scoped_guard (spinlock_bh, &ifq->alloc_lock) + niov = zcrx_get_free_niov(ifq->area);if (niov)page_pool_fragment_netmem (net_iov_to_netmem (niov),1 );
io_uring/zcrx.h +1 -1
@@ -36,7 +36,6 @@ struct io_zcrx_area {u16 area_id;/* freelist */ -spinlock_t freelist_lock____cacheline_aligned_in_smp ;u32 free_count;u32 *freelist;
@@ -65,6 +64,7 @@ struct io_zcrx_ifq {bool kern_readable;struct zcrx_rq rq____cacheline_aligned_in_smp ; +spinlock_t alloc_lock____cacheline_aligned_in_smp ;u32 if_rxq;struct device *dev;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 13/17 io_uring/zcrx: array of areas
In reply to Pavel BegunkovCurrently, we have only a one area per zcrx instance, and struct io_zcrx_ifq stores a single pointer. To prepare for adding more areas, replace it with an array of areas.
We'll be creating them at runtime, and the array is protected by 3
locks: ->pp_lock, ->alloc_lock and ->rq.lock. It takes all of them when switching arrays, and readers should hold either of them.
--- io_uring/zcrx.c | 95 ++++++++++++++++++++++++++++++++++++------------- io_uring/zcrx.h | 5 ++- 2 files changed, 75 insertions(+), 25 deletions(-)
io_uring/zcrx.c +71 -24
@@ -332,6 +332,14 @@ static void io_zcrx_unmap_area(struct io_zcrx_ifq *ifq, } } +static void io_zcrx_unmap_areas (struct io_zcrx_ifq *ifq) +{ +unsigned area_idx; + +for (area_idx =0 ; area_idx < ifq->nr_areas; area_idx++) +io_zcrx_unmap_area (ifq, ifq->areas[area_idx]); +} +static void zcrx_sync_for_device (struct page_pool *pp,struct io_zcrx_ifq *zcrx,netmem_ref *netmems,unsigned nr) {
@@ -459,13 +467,29 @@ static int io_zcrx_append_area(struct io_zcrx_ifq *ifq, struct io_zcrx_area *area) {bool kern_readable = !area->mem.is_dmabuf; +struct io_zcrx_area **areas, **old_areas; +unsigned old_nr; -if (WARN_ON_ONCE (ifq->area)) -return -EINVAL ;if (WARN_ON_ONCE (ifq->kern_readable != kern_readable))return -EINVAL ; - ifq->area = area; + old_areas = ifq->areas; + old_nr = ifq->nr_areas; + + areas =kmalloc_array (old_nr +1 ,sizeof (areas[0 ]), +GFP_KERNEL_ACCOUNT | __GFP_ZERO); +if (!areas) +return -ENOMEM ; +if (old_areas) +memcpy (areas, old_areas, old_nr *sizeof (areas[0 ])); + areas[old_nr] = area; + + scoped_guard(spinlock_bh, &ifq->rq.lock) { +guard (spinlock_bh)(&ifq->alloc_lock); + ifq->areas = areas; + ifq->nr_areas = old_nr +1 ; + } +kfree (old_areas);return 0 ; }
@@ -621,7 +645,7 @@ static void io_close_queue(struct io_zcrx_ifq *ifq)if (ifq->if_rxq !=-1 )netif_mp_close_rxq (netdev, ifq->if_rxq, &p); -io_zcrx_unmap_area (ifq, ifq->area); +io_zcrx_unmap_areas (ifq);netdev_unlock (netdev);netdev_put (netdev, &netdev_tracker); }
@@ -630,6 +654,8 @@ static void io_close_queue(struct io_zcrx_ifq *ifq)static void io_zcrx_ifq_free (struct io_zcrx_ifq *ifq) { +int i; +if (WARN_ON_ONCE (ifq->if_rxq !=-1 ))return ;if (WARN_ON_ONCE (ifq->netdev !=NULL ))
@@ -637,8 +663,8 @@ static void io_zcrx_ifq_free(struct io_zcrx_ifq *ifq)if (WARN_ON_ONCE (ifq->master_ctx))return ; -if (ifq->area) -io_zcrx_free_area (ifq, ifq->area); +for (i =0 ; i < ifq->nr_areas; i++) +io_zcrx_free_area (ifq, ifq->areas[i]);if (ifq->mm_account)mmdrop (ifq->mm_account);if (ifq->dev)
@@ -647,6 +673,7 @@ static void io_zcrx_ifq_free(struct io_zcrx_ifq *ifq)io_free_rbuf_ring (ifq);free_uid (ifq->user);mutex_destroy (&ifq->pp_lock); +kfree (ifq->areas);kfree (ifq); }
@@ -692,14 +719,10 @@ static void io_zcrx_return_niov(struct net_iov *niov)page_pool_put_unrefed_netmem (niov->desc.pp, netmem,-1 , false); } -static void io_zcrx_scrub (struct io_zcrx_ifq *ifq) +static void io_zcrx_scrub_area (struct io_zcrx_ifq *ifq,struct io_zcrx_area *area) { -struct io_zcrx_area *area = ifq->area;int i; -if (!area) -return ; -/* Reclaim back all buffers given to the user space. */ for (i =0 ; i < area->nia.num_niovs; i++) {struct net_iov *niov = &area->nia.niovs[i];
@@ -713,6 +736,15 @@ static void io_zcrx_scrub(struct io_zcrx_ifq *ifq) } } +static void io_zcrx_scrub (struct io_zcrx_ifq *ifq) +{ +int i; + +guard (mutex)(&ifq->pp_lock); +for (i =0 ; i < ifq->nr_areas; i++) +io_zcrx_scrub_area (ifq, ifq->areas[i]); +} +static void zcrx_unregister_user (struct io_zcrx_ifq *ifq,struct io_ring_ctx *ctx) { scoped_guard(spinlock_bh, &ifq->ctx_lock) {
@@ -1185,12 +1217,15 @@ static inline bool io_parse_rqe(struct io_uring_zcrx_rqe *rqe,unsigned niov_idx, area_idx;struct io_zcrx_area *area; +lockdep_assert_held (&ifq->rq.lock); + area_idx = off >>IORING_ZCRX_AREA_SHIFT ; niov_idx = (off & ~IORING_ZCRX_AREA_MASK ) >> ifq->niov_shift; -if (unlikely (rqe->__pad || area_idx)) +if (unlikely (rqe->__pad || area_idx >= ifq->nr_areas))return false; - area = ifq->area; + area_idx =array_index_nospec (area_idx, ifq->nr_areas); + area = ifq->areas[area_idx];if (unlikely (niov_idx >= area->nia.num_niovs))return false;
@@ -1260,18 +1295,24 @@ static unsigned io_zcrx_ring_refill(struct page_pool *pp,static unsigned io_zcrx_refill_slow (struct page_pool *pp,struct io_zcrx_ifq *ifq,netmem_ref *netmems,unsigned to_alloc) { -struct io_zcrx_area *area = ifq->area; +unsigned area_idx =0 ;unsigned allocated =0 ;guard (spinlock_bh)(&ifq->alloc_lock); -for (allocated =0 ; allocated < to_alloc; allocated++) { -struct net_iov *niov =zcrx_get_free_niov (area); +while (allocated < to_alloc) { +struct net_iov *niov =zcrx_get_free_niov (ifq->areas[area_idx]); + +if (!niov) { + area_idx++; +if (area_idx >= ifq->nr_areas) +break ; +continue ; + } -if (!niov) -break ;net_mp_niov_set_page_pool (pp, niov); netmems[allocated] =net_iov_to_netmem (niov); + allocated++; }return allocated; }
@@ -1407,8 +1448,8 @@ static void io_pp_uninstall(void *mp_priv, struct netdev_rx_queue *rxq)struct pp_memory_provider_params *p = &rxq->mp_params;struct io_zcrx_ifq *ifq = mp_priv; +io_zcrx_unmap_areas (ifq);io_zcrx_drop_netdev (ifq); -io_zcrx_unmap_area (ifq, ifq->area); p->mp_ops =NULL ; p->mp_priv =NULL ;
@@ -1569,16 +1610,22 @@ static bool io_zcrx_queue_cqe(struct io_kiocb *req, struct net_iov *niov,static struct net_iov *io_alloc_fallback_niov (struct io_zcrx_ifq *ifq) {struct net_iov *niov =NULL ; +unsigned area_idx;if (!ifq->kern_readable)return NULL ; -scoped_guard (spinlock_bh, &ifq->alloc_lock) - niov = zcrx_get_free_niov(ifq->area); +guard (spinlock_bh)(&ifq->alloc_lock); + +for (area_idx =0 ; area_idx < ifq->nr_areas; area_idx++) { + niov =zcrx_get_free_niov (ifq->areas[area_idx]); +if (niov) { +page_pool_fragment_netmem (net_iov_to_netmem (niov),1 ); +return niov; + } + } -if (niov) -page_pool_fragment_netmem (net_iov_to_netmem (niov),1 ); -return niov; +return NULL ; }struct io_copy_cache {
io_uring/zcrx.h +4 -1
@@ -57,7 +57,10 @@ struct zcrx_rq { };struct io_zcrx_ifq { -struct io_zcrx_area *area; +/* read-protected by any of: ->pp_lock, ->alloc_lock, ->rq.lock */ +struct io_zcrx_area **areas; +unsigned nr_areas; +unsigned niov_shift;struct user_struct *user;struct mm_struct *mm_account;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 14/17 io_uring/zcrx: pass area_id to __zcrx_create_area()
In reply to Pavel BegunkovInstead of generating an area id inside of __zcrx_create_area(), let the caller to pass it. It needs the id to derive the user token, and we might need to know it before creating and publishing the area.
--- io_uring/zcrx.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-)
io_uring/zcrx.c +15 -6
@@ -44,6 +44,11 @@ static inline u64 zcrx_area_id_to_token(u32 area_id)return (u64 )area_id <<IORING_ZCRX_AREA_SHIFT ; } +static inline u32 zcrx_next_area_id (struct io_zcrx_ifq *zcrx) +{ +return zcrx->nr_areas; +} +static inline struct io_zcrx_ifq *io_pp_to_ifq (struct page_pool *pp) {return pp->mp_priv;
@@ -472,6 +477,8 @@ static int io_zcrx_append_area(struct io_zcrx_ifq *ifq,if (WARN_ON_ONCE (ifq->kern_readable != kern_readable))return -EINVAL ; +if (WARN_ON_ONCE (area->area_id !=zcrx_next_area_id (ifq))) +return -EINVAL ; old_areas = ifq->areas; old_nr = ifq->nr_areas;
@@ -494,9 +501,10 @@ static int io_zcrx_append_area(struct io_zcrx_ifq *ifq, }static int __zcrx_create_area (struct io_zcrx_ifq *ifq, -struct io_uring_zcrx_area_reg *area_reg, +const struct io_uring_zcrx_area_reg *area_reg,struct io_zcrx_area **res_area, -u32 rx_buf_len) +u32 rx_buf_len, +u32 area_id) {int buf_size_shift =PAGE_SHIFT ;struct io_zcrx_area *area;
@@ -565,9 +573,7 @@ static int __zcrx_create_area(struct io_zcrx_ifq *ifq, } area->free_count = nr_iovs; -/* we're only supporting one area per ifq for now */ - area->area_id =0 ; - area_reg->rq_area_token =zcrx_area_id_to_token (area->area_id); + area->area_id = area_id; *res_area = area;return 0 ; err:
@@ -583,9 +589,12 @@ static int io_zcrx_create_area(struct io_zcrx_ifq *ifq, struct io_uring_zcrx_ifq_reg *reg) {struct io_zcrx_area *area; +u32 id =zcrx_next_area_id (ifq);int ret; - ret =__zcrx_create_area (ifq, area_reg, &area, reg->rx_buf_len); + area_reg->rq_area_token =zcrx_area_id_to_token (id); + + ret =__zcrx_create_area (ifq, area_reg, &area, reg->rx_buf_len, id);if (ret)return ret;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 15/17 io_uring/zcrx: add dynamic area creation
In reply to Pavel BegunkovIt's not always possible for the user to predict during registration how much memory zcrx will need to sustain the traffic. Allow to dynamically add more areas with a new ctrl code ZCRX_CTRL_ADD_AREA.
--- include/uapi/linux/io_uring/zcrx.h | 7 +++ io_uring/zcrx.c | 84 +++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 12 deletions(-)
include/uapi/linux/io_uring/zcrx.h +7 -0
@@ -116,6 +116,7 @@ enum zcrx_ctrl_op {ZCRX_CTRL_FLUSH_RQ ,ZCRX_CTRL_EXPORT ,ZCRX_CTRL_ARM_NOTIFICATION , +ZCRX_CTRL_ADD_AREA , __ZCRX_CTRL_LAST, };
@@ -134,6 +135,11 @@ struct zcrx_ctrl_arm_notif {__u32 __resv[11 ]; }; +struct zcrx_ctrl_add_area { +__u64 area_ptr;/* pointer to struct io_uring_zcrx_area_reg */ +__u64 __resv[5 ]; +}; +struct zcrx_ctrl {__u32 zcrx_id;__u32 op;/* see enum zcrx_ctrl_op */
@@ -143,6 +149,7 @@ struct zcrx_ctrl {struct zcrx_ctrl_export zc_export;struct zcrx_ctrl_flush_rq zc_flush;struct zcrx_ctrl_arm_notif zc_arm_notif; +struct zcrx_ctrl_add_area zc_area; }; };
io_uring/zcrx.c +72 -12
159 lines not on this page · raw message
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerPATCH 16/17 io_urint/zcrx: narrow var scope in io_zcrx_recv_skb()
In reply to Pavel BegunkovA preparation patch that limits scopes of a couple variables in io_zcrx_recv_skb() and rename them, it makes it easier to reason about the code.
--- io_uring/zcrx.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-)
io_uring/zcrx.c +16 -19
@@ -1836,8 +1836,7 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb,struct io_kiocb *req = args->req;struct sk_buff *frag_iter;unsigned start, start_off = offset; -int i, copy, end, off; -int ret =0 ; +int i, ret =0 ; len =min_t (size_t, len, desc->count);/*
@@ -1875,20 +1874,19 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb,for (i =0 ; i <skb_shinfo (skb)->nr_frags; i++) {const skb_frag_t *frag; +unsigned frag_end;if (WARN_ON (start > offset + len))return -EFAULT ; frag = &skb_shinfo (skb)->frags[i]; - end = start +skb_frag_size (frag); + frag_end = start +skb_frag_size (frag); -if (offset < end) { - copy = end - offset; -if (copy > len) - copy = len; +if (offset < frag_end) { +unsigned copy =min (frag_end - offset, len); +unsigned frag_off = offset - start; - off = offset - start; - ret =io_zcrx_recv_frag (req, ifq, frag, off, copy); + ret =io_zcrx_recv_frag (req, ifq, frag, frag_off, copy);if (ret <0 ) goto out;
@@ -1897,24 +1895,23 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb,if (len ==0 || ret != copy) goto out; } - start = end; + start = frag_end; } skb_walk_frags(skb, frag_iter) { +unsigned frag_end; +if (WARN_ON (start > offset + len))return -EFAULT ; - end = start + frag_iter->len; -if (offset < end) { + frag_end = start + frag_iter->len; +if (offset < frag_end) { +unsigned copy =min (frag_end - offset, len); +unsigned frag_off = offset - start;size_t count; - copy = end - offset; -if (copy > len) - copy = len; - - off = offset - start; count = desc->count; - ret =io_zcrx_recv_skb (desc, frag_iter, off, copy); + ret =io_zcrx_recv_skb (desc, frag_iter, frag_off, copy); desc->count = count;if (ret <0 ) goto out;
@@ -1924,7 +1921,7 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb,if (len ==0 || ret != copy) goto out; } - start = end; + start = frag_end; } out:
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerLooks like the last patch wasn't delivered, I'm going to resend.
Quoted 30 lines · Pavel Begunkov
Sending it out mainly to trigger review bots. The first half improves the refill queue implementation and improves refilling limits, which shows up when niovs are heavily fragmented like with large rx pages. The 2nd half adds dynamic backing memory provisioning.
Pavel Begunkov (17):
io_uring/zcrx: scale refilling with large pagesio_uring/zcrx: move RQ head/tail to separate cache linesio_uring/zcrx: add RQ iterator
io_uring/zcrx: cache RQ tail
io_uring/zcrx: coalesce same-niov RQEs on refillio_uring/zcrx: constifyarea_regon importio_uring/zcrx: add helper for deriving area tokenio_uring/zcrx: don't passifq_regto area creationio_uring/zcrx: split dmabuf unmap and releaseio_uring/zcrx: unmap under netdev lockio_uring/zcrx: split append out of area creationio_uring/zcrx: move freelist lock to struct zcrxio_uring/zcrx: array of areas
io_uring/zcrx: passarea_idto__zcrx_create_area()io_uring/zcrx: add dynamic area creation io_urint/zcrx: narrow var scope inio_zcrx_recv_skb()io_uring/zcrx: don't reloadskb_shinfoinclude/uapi/linux/io_uring/zcrx.h | 7 + io_uring/query.c | 2 +- io_uring/zcrx.c | 445 +++++++++++++++++++++-------- io_uring/zcrx.h | 15 +- 4 files changed, 345 insertions(+), 124 deletions(-)
Signature · 1 line
Pavel Begunkov
Pavel Begunkov
maintainerPATCH 17/17 io_uring/zcrx: don't reload skb_shinfo
In reply to Pavel BegunkovKeep skb_shinfo in a local variable so that it doesn't reload it on every iteration of the loop.
--- io_uring/zcrx.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
io_uring/zcrx.c +5 -6
@@ -1836,6 +1836,7 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb,struct io_kiocb *req = args->req;struct sk_buff *frag_iter;unsigned start, start_off = offset; +struct skb_shared_info *shi;int i, ret =0 ; len =min_t (size_t, len, desc->count);
@@ -1871,17 +1872,15 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb, } start =skb_headlen (skb); + shi =skb_shinfo (skb); -for (i =0 ; i <skb_shinfo (skb)->nr_frags; i++) { -const skb_frag_t *frag; -unsigned frag_end; +for (i =0 ; i < shi->nr_frags; i++) { +const skb_frag_t *frag = &shi->frags[i]; +unsigned frag_end = start +skb_frag_size (frag);if (WARN_ON (start > offset + len))return -EFAULT ; - frag = &skb_shinfo (skb)->frags[i]; - frag_end = start +skb_frag_size (frag); -if (offset < frag_end) {unsigned copy =min (frag_end - offset, len);unsigned frag_off = offset - start;
Signature · 1 line
2.54.0