Skip to content

Commit b10b73c

Browse files
committed
io_uring/kbuf: recycle freed mapped buffer ring entries
Right now we stash any potentially mmap'ed provided ring buffer range for freeing at release time, regardless of when they get unregistered. Since we're keeping track of these ranges anyway, keep track of their registration state as well, and use that to recycle ranges when appropriate rather than always allocate new ones. The lookup is a basic scan of entries, checking for the best matching free entry. Fixes: c392cbe ("io_uring/kbuf: defer release of mapped buffer rings") Signed-off-by: Jens Axboe <[email protected]>
1 parent c392cbe commit b10b73c

File tree

1 file changed

+66
-11
lines changed

1 file changed

+66
-11
lines changed

io_uring/kbuf.c

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ struct io_provide_buf {
3636
struct io_buf_free {
3737
struct hlist_node list;
3838
void *mem;
39+
size_t size;
40+
int inuse;
3941
};
4042

4143
static inline struct io_buffer_list *io_buffer_get_list(struct io_ring_ctx *ctx,
@@ -216,6 +218,24 @@ static __cold int io_init_bl_list(struct io_ring_ctx *ctx)
216218
return 0;
217219
}
218220

221+
/*
222+
* Mark the given mapped range as free for reuse
223+
*/
224+
static void io_kbuf_mark_free(struct io_ring_ctx *ctx, struct io_buffer_list *bl)
225+
{
226+
struct io_buf_free *ibf;
227+
228+
hlist_for_each_entry(ibf, &ctx->io_buf_list, list) {
229+
if (bl->buf_ring == ibf->mem) {
230+
ibf->inuse = 0;
231+
return;
232+
}
233+
}
234+
235+
/* can't happen... */
236+
WARN_ON_ONCE(1);
237+
}
238+
219239
static int __io_remove_buffers(struct io_ring_ctx *ctx,
220240
struct io_buffer_list *bl, unsigned nbufs)
221241
{
@@ -232,6 +252,7 @@ static int __io_remove_buffers(struct io_ring_ctx *ctx,
232252
* io_kbuf_list_free() will free the page(s) at
233253
* ->release() time.
234254
*/
255+
io_kbuf_mark_free(ctx, bl);
235256
bl->buf_ring = NULL;
236257
bl->is_mmap = 0;
237258
} else if (bl->buf_nr_pages) {
@@ -539,6 +560,34 @@ static int io_pin_pbuf_ring(struct io_uring_buf_reg *reg,
539560
return -EINVAL;
540561
}
541562

563+
/*
564+
* See if we have a suitable region that we can reuse, rather than allocate
565+
* both a new io_buf_free and mem region again. We leave it on the list as
566+
* even a reused entry will need freeing at ring release.
567+
*/
568+
static struct io_buf_free *io_lookup_buf_free_entry(struct io_ring_ctx *ctx,
569+
size_t ring_size)
570+
{
571+
struct io_buf_free *ibf, *best = NULL;
572+
size_t best_dist;
573+
574+
hlist_for_each_entry(ibf, &ctx->io_buf_list, list) {
575+
size_t dist;
576+
577+
if (ibf->inuse || ibf->size < ring_size)
578+
continue;
579+
dist = ibf->size - ring_size;
580+
if (!best || dist < best_dist) {
581+
best = ibf;
582+
if (!dist)
583+
break;
584+
best_dist = dist;
585+
}
586+
}
587+
588+
return best;
589+
}
590+
542591
static int io_alloc_pbuf_ring(struct io_ring_ctx *ctx,
543592
struct io_uring_buf_reg *reg,
544593
struct io_buffer_list *bl)
@@ -548,20 +597,26 @@ static int io_alloc_pbuf_ring(struct io_ring_ctx *ctx,
548597
void *ptr;
549598

550599
ring_size = reg->ring_entries * sizeof(struct io_uring_buf_ring);
551-
ptr = io_mem_alloc(ring_size);
552-
if (!ptr)
553-
return -ENOMEM;
554600

555-
/* Allocate and store deferred free entry */
556-
ibf = kmalloc(sizeof(*ibf), GFP_KERNEL_ACCOUNT);
601+
/* Reuse existing entry, if we can */
602+
ibf = io_lookup_buf_free_entry(ctx, ring_size);
557603
if (!ibf) {
558-
io_mem_free(ptr);
559-
return -ENOMEM;
560-
}
561-
ibf->mem = ptr;
562-
hlist_add_head(&ibf->list, &ctx->io_buf_list);
604+
ptr = io_mem_alloc(ring_size);
605+
if (!ptr)
606+
return -ENOMEM;
563607

564-
bl->buf_ring = ptr;
608+
/* Allocate and store deferred free entry */
609+
ibf = kmalloc(sizeof(*ibf), GFP_KERNEL_ACCOUNT);
610+
if (!ibf) {
611+
io_mem_free(ptr);
612+
return -ENOMEM;
613+
}
614+
ibf->mem = ptr;
615+
ibf->size = ring_size;
616+
hlist_add_head(&ibf->list, &ctx->io_buf_list);
617+
}
618+
ibf->inuse = 1;
619+
bl->buf_ring = ibf->mem;
565620
bl->is_mapped = 1;
566621
bl->is_mmap = 1;
567622
return 0;

0 commit comments

Comments
 (0)