Skip to content

Commit 6224843

Browse files
isilenceaxboe
authored andcommitted
io_uring: allow empty slots for reg buffers
Allow empty reg buffer slots any request using which should fail. This allows users to not register all buffers in advance, but do it lazily and/or on demand via updates. That is achieved by setting iov_base and iov_len to zero for registration and/or buffer updates. Empty buffer can't have a non-zero tag. Implementation details: to not add extra overhead to io_import_fixed(), create a dummy buffer crafted to fail any request using it, and set it to all empty buffer slots. Signed-off-by: Pavel Begunkov <asml.silence@gmail.com> Link: https://lore.kernel.org/r/7e95e4d700082baaf010c648c72ac764c9cc8826.1619611868.git.asml.silence@gmail.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent b0d658e commit 6224843

1 file changed

Lines changed: 29 additions & 7 deletions

File tree

fs/io_uring.c

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ struct io_ring_ctx {
456456
spinlock_t rsrc_ref_lock;
457457
struct io_rsrc_node *rsrc_node;
458458
struct io_rsrc_node *rsrc_backup_node;
459+
struct io_mapped_ubuf *dummy_ubuf;
459460

460461
struct io_restriction restrictions;
461462

@@ -1158,6 +1159,12 @@ static struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
11581159
goto err;
11591160
__hash_init(ctx->cancel_hash, 1U << hash_bits);
11601161

1162+
ctx->dummy_ubuf = kzalloc(sizeof(*ctx->dummy_ubuf), GFP_KERNEL);
1163+
if (!ctx->dummy_ubuf)
1164+
goto err;
1165+
/* set invalid range, so io_import_fixed() fails meeting it */
1166+
ctx->dummy_ubuf->ubuf = -1UL;
1167+
11611168
if (percpu_ref_init(&ctx->refs, io_ring_ctx_ref_free,
11621169
PERCPU_REF_ALLOW_REINIT, GFP_KERNEL))
11631170
goto err;
@@ -1185,6 +1192,7 @@ static struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
11851192
INIT_LIST_HEAD(&ctx->submit_state.comp.locked_free_list);
11861193
return ctx;
11871194
err:
1195+
kfree(ctx->dummy_ubuf);
11881196
kfree(ctx->cancel_hash);
11891197
kfree(ctx);
11901198
return NULL;
@@ -8109,11 +8117,13 @@ static void io_buffer_unmap(struct io_ring_ctx *ctx, struct io_mapped_ubuf **slo
81098117
struct io_mapped_ubuf *imu = *slot;
81108118
unsigned int i;
81118119

8112-
for (i = 0; i < imu->nr_bvecs; i++)
8113-
unpin_user_page(imu->bvec[i].bv_page);
8114-
if (imu->acct_pages)
8115-
io_unaccount_mem(ctx, imu->acct_pages);
8116-
kvfree(imu);
8120+
if (imu != ctx->dummy_ubuf) {
8121+
for (i = 0; i < imu->nr_bvecs; i++)
8122+
unpin_user_page(imu->bvec[i].bv_page);
8123+
if (imu->acct_pages)
8124+
io_unaccount_mem(ctx, imu->acct_pages);
8125+
kvfree(imu);
8126+
}
81178127
*slot = NULL;
81188128
}
81198129

@@ -8253,6 +8263,11 @@ static int io_sqe_buffer_register(struct io_ring_ctx *ctx, struct iovec *iov,
82538263
size_t size;
82548264
int ret, pret, nr_pages, i;
82558265

8266+
if (!iov->iov_base) {
8267+
*pimu = ctx->dummy_ubuf;
8268+
return 0;
8269+
}
8270+
82568271
ubuf = (unsigned long) iov->iov_base;
82578272
end = (ubuf + iov->iov_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
82588273
start = ubuf >> PAGE_SHIFT;
@@ -8350,7 +8365,9 @@ static int io_buffer_validate(struct iovec *iov)
83508365
* constraints here, we'll -EINVAL later when IO is
83518366
* submitted if they are wrong.
83528367
*/
8353-
if (!iov->iov_base || !iov->iov_len)
8368+
if (!iov->iov_base)
8369+
return iov->iov_len ? -EFAULT : 0;
8370+
if (!iov->iov_len)
83548371
return -EFAULT;
83558372

83568373
/* arbitrary limit, but we need something */
@@ -8400,6 +8417,8 @@ static int io_sqe_buffers_register(struct io_ring_ctx *ctx, void __user *arg,
84008417
ret = io_buffer_validate(&iov);
84018418
if (ret)
84028419
break;
8420+
if (!iov.iov_base && tag)
8421+
return -EINVAL;
84038422

84048423
ret = io_sqe_buffer_register(ctx, &iov, &ctx->user_bufs[i],
84058424
&last_hpage);
@@ -8449,12 +8468,14 @@ static int __io_sqe_buffers_update(struct io_ring_ctx *ctx,
84498468
err = io_buffer_validate(&iov);
84508469
if (err)
84518470
break;
8471+
if (!iov.iov_base && tag)
8472+
return -EINVAL;
84528473
err = io_sqe_buffer_register(ctx, &iov, &imu, &last_hpage);
84538474
if (err)
84548475
break;
84558476

84568477
i = array_index_nospec(offset, ctx->nr_user_bufs);
8457-
if (ctx->user_bufs[i]) {
8478+
if (ctx->user_bufs[i] != ctx->dummy_ubuf) {
84588479
err = io_queue_rsrc_removal(ctx->buf_data, offset,
84598480
ctx->rsrc_node, ctx->user_bufs[i]);
84608481
if (unlikely(err)) {
@@ -8602,6 +8623,7 @@ static void io_ring_ctx_free(struct io_ring_ctx *ctx)
86028623
if (ctx->hash_map)
86038624
io_wq_put_hash(ctx->hash_map);
86048625
kfree(ctx->cancel_hash);
8626+
kfree(ctx->dummy_ubuf);
86058627
kfree(ctx);
86068628
}
86078629

0 commit comments

Comments
 (0)