Skip to content

Commit 7cc2a6e

Browse files
committed
io_uring: add IORING_REGISTER_COPY_BUFFERS method
Buffers can get registered with io_uring, which allows to skip the repeated pin_pages, unpin/unref pages for each O_DIRECT operation. This reduces the overhead of O_DIRECT IO. However, registrering buffers can take some time. Normally this isn't an issue as it's done at initialization time (and hence less critical), but for cases where rings can be created and destroyed as part of an IO thread pool, registering the same buffers for multiple rings become a more time sensitive proposition. As an example, let's say an application has an IO memory pool of 500G. Initial registration takes: Got 500 huge pages (each 1024MB) Registered 500 pages in 409 msec or about 0.4 seconds. If we go higher to 900 1GB huge pages being registered: Registered 900 pages in 738 msec which is, as expected, a fully linear scaling. Rather than have each ring pin/map/register the same buffer pool, provide an io_uring_register(2) opcode to simply duplicate the buffers that are registered with another ring. Adding the same 900GB of registered buffers to the target ring can then be accomplished in: Copied 900 pages in 17 usec While timing differs a bit, this provides around a 25,000-40,000x speedup for this use case. Signed-off-by: Jens Axboe <[email protected]>
1 parent 0b6d253 commit 7cc2a6e

File tree

4 files changed

+111
-0
lines changed

4 files changed

+111
-0
lines changed

include/uapi/linux/io_uring.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,9 @@ enum io_uring_register_op {
609609

610610
IORING_REGISTER_CLOCK = 29,
611611

612+
/* copy registered buffers from source ring to current ring */
613+
IORING_REGISTER_COPY_BUFFERS = 30,
614+
612615
/* this goes last */
613616
IORING_REGISTER_LAST,
614617

@@ -694,6 +697,16 @@ struct io_uring_clock_register {
694697
__u32 __resv[3];
695698
};
696699

700+
enum {
701+
IORING_REGISTER_SRC_REGISTERED = 1,
702+
};
703+
704+
struct io_uring_copy_buffers {
705+
__u32 src_fd;
706+
__u32 flags;
707+
__u32 pad[6];
708+
};
709+
697710
struct io_uring_buf {
698711
__u64 addr;
699712
__u32 len;

io_uring/register.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,12 @@ static int __io_uring_register(struct io_ring_ctx *ctx, unsigned opcode,
542542
break;
543543
ret = io_register_clock(ctx, arg);
544544
break;
545+
case IORING_REGISTER_COPY_BUFFERS:
546+
ret = -EINVAL;
547+
if (!arg || nr_args != 1)
548+
break;
549+
ret = io_register_copy_buffers(ctx, arg);
550+
break;
545551
default:
546552
ret = -EINVAL;
547553
break;

io_uring/rsrc.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "openclose.h"
1818
#include "rsrc.h"
1919
#include "memmap.h"
20+
#include "register.h"
2021

2122
struct io_rsrc_update {
2223
struct file *file;
@@ -1137,3 +1138,93 @@ int io_import_fixed(int ddir, struct iov_iter *iter,
11371138

11381139
return 0;
11391140
}
1141+
1142+
static int io_copy_buffers(struct io_ring_ctx *ctx, struct io_ring_ctx *src_ctx)
1143+
{
1144+
struct io_mapped_ubuf **user_bufs;
1145+
struct io_rsrc_data *data;
1146+
int i, ret, nbufs;
1147+
1148+
/*
1149+
* Drop our own lock here. We'll setup the data we need and reference
1150+
* the source buffers, then re-grab, check, and assign at the end.
1151+
*/
1152+
mutex_unlock(&ctx->uring_lock);
1153+
1154+
mutex_lock(&src_ctx->uring_lock);
1155+
ret = -ENXIO;
1156+
nbufs = src_ctx->nr_user_bufs;
1157+
if (!nbufs)
1158+
goto out_unlock;
1159+
ret = io_rsrc_data_alloc(ctx, IORING_RSRC_BUFFER, NULL, nbufs, &data);
1160+
if (ret)
1161+
goto out_unlock;
1162+
1163+
ret = -ENOMEM;
1164+
user_bufs = kcalloc(nbufs, sizeof(*ctx->user_bufs), GFP_KERNEL);
1165+
if (!user_bufs)
1166+
goto out_free_data;
1167+
1168+
for (i = 0; i < nbufs; i++) {
1169+
struct io_mapped_ubuf *src = src_ctx->user_bufs[i];
1170+
1171+
refcount_inc(&src->refs);
1172+
user_bufs[i] = src;
1173+
}
1174+
1175+
/* Have a ref on the bufs now, drop src lock and re-grab our own lock */
1176+
mutex_unlock(&src_ctx->uring_lock);
1177+
mutex_lock(&ctx->uring_lock);
1178+
if (!ctx->user_bufs) {
1179+
ctx->user_bufs = user_bufs;
1180+
ctx->buf_data = data;
1181+
ctx->nr_user_bufs = nbufs;
1182+
return 0;
1183+
}
1184+
1185+
/* someone raced setting up buffers, dump ours */
1186+
for (i = 0; i < nbufs; i++)
1187+
io_buffer_unmap(ctx, &user_bufs[i]);
1188+
io_rsrc_data_free(data);
1189+
kfree(user_bufs);
1190+
return -EBUSY;
1191+
out_free_data:
1192+
io_rsrc_data_free(data);
1193+
out_unlock:
1194+
mutex_unlock(&src_ctx->uring_lock);
1195+
mutex_lock(&ctx->uring_lock);
1196+
return ret;
1197+
}
1198+
1199+
/*
1200+
* Copy the registered buffers from the source ring whose file descriptor
1201+
* is given in the src_fd to the current ring. This is identical to registering
1202+
* the buffers with ctx, except faster as mappings already exist.
1203+
*
1204+
* Since the memory is already accounted once, don't account it again.
1205+
*/
1206+
int io_register_copy_buffers(struct io_ring_ctx *ctx, void __user *arg)
1207+
{
1208+
struct io_uring_copy_buffers buf;
1209+
bool registered_src;
1210+
struct file *file;
1211+
int ret;
1212+
1213+
if (ctx->user_bufs || ctx->nr_user_bufs)
1214+
return -EBUSY;
1215+
if (copy_from_user(&buf, arg, sizeof(buf)))
1216+
return -EFAULT;
1217+
if (buf.flags & ~IORING_REGISTER_SRC_REGISTERED)
1218+
return -EINVAL;
1219+
if (memchr_inv(buf.pad, 0, sizeof(buf.pad)))
1220+
return -EINVAL;
1221+
1222+
registered_src = (buf.flags & IORING_REGISTER_SRC_REGISTERED) != 0;
1223+
file = io_uring_register_get_file(buf.src_fd, registered_src);
1224+
if (IS_ERR(file))
1225+
return PTR_ERR(file);
1226+
ret = io_copy_buffers(ctx, file->private_data);
1227+
if (!registered_src)
1228+
fput(file);
1229+
return ret;
1230+
}

io_uring/rsrc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ int io_import_fixed(int ddir, struct iov_iter *iter,
6868
struct io_mapped_ubuf *imu,
6969
u64 buf_addr, size_t len);
7070

71+
int io_register_copy_buffers(struct io_ring_ctx *ctx, void __user *arg);
7172
void __io_sqe_buffers_unregister(struct io_ring_ctx *ctx);
7273
int io_sqe_buffers_unregister(struct io_ring_ctx *ctx);
7374
int io_sqe_buffers_register(struct io_ring_ctx *ctx, void __user *arg,

0 commit comments

Comments
 (0)