io_uring/msg_ring: add support for sending a sync message

Normally MSG_RING requires both a source and a destination ring. But
some users don't always have a ring avilable to send a message from, yet
they still need to notify a target ring.

Add support for using io_uring_register(2) without having a source ring,
using a file descriptor of -1 for that. Internally those are called
blind registration opcodes. Implement IORING_REGISTER_SEND_MSG_RING as a
blind opcode, which simply takes an sqe that the application can put on
the stack and use the normal liburing helpers to initialize it. Then the
app can call:

io_uring_register(-1, IORING_REGISTER_SEND_MSG_RING, &sqe, 1);

and get the same behavior in terms of the target, where a CQE is posted
with the details given in the sqe.

For now this takes a single sqe pointer argument, and hence arg must
be set to that, and nr_args must be 1. Could easily be extended to take
an array of sqes, but for now let's keep it simple.

Link: https://lore.kernel.org/r/20240924115932.116167-3-axboe@kernel.dk
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Jens Axboe 2024-09-24 05:57:31 -06:00
parent 95d6c9229a
commit a377132154
4 changed files with 63 additions and 0 deletions

View File

@ -612,6 +612,9 @@ enum io_uring_register_op {
/* clone registered buffers from source ring to current ring */
IORING_REGISTER_CLONE_BUFFERS = 30,
/* send MSG_RING without having a ring */
IORING_REGISTER_SEND_MSG_RING = 31,
/* this goes last */
IORING_REGISTER_LAST,

View File

@ -331,6 +331,35 @@ done:
return IOU_OK;
}
int io_uring_sync_msg_ring(struct io_uring_sqe *sqe)
{
struct io_msg io_msg = { };
struct fd f;
int ret;
ret = __io_msg_ring_prep(&io_msg, sqe);
if (unlikely(ret))
return ret;
/*
* Only data sending supported, not IORING_MSG_SEND_FD as that one
* doesn't make sense without a source ring to send files from.
*/
if (io_msg.cmd != IORING_MSG_DATA)
return -EINVAL;
ret = -EBADF;
f = fdget(sqe->fd);
if (fd_file(f)) {
ret = -EBADFD;
if (io_is_uring_fops(fd_file(f)))
ret = __io_msg_ring_data(fd_file(f)->private_data,
&io_msg, IO_URING_F_UNLOCKED);
fdput(f);
}
return ret;
}
void io_msg_cache_free(const void *entry)
{
struct io_kiocb *req = (struct io_kiocb *) entry;

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
int io_uring_sync_msg_ring(struct io_uring_sqe *sqe);
int io_msg_ring_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
int io_msg_ring(struct io_kiocb *req, unsigned int issue_flags);
void io_msg_ring_cleanup(struct io_kiocb *req);

View File

@ -28,6 +28,7 @@
#include "kbuf.h"
#include "napi.h"
#include "eventfd.h"
#include "msg_ring.h"
#define IORING_MAX_RESTRICTIONS (IORING_RESTRICTION_LAST + \
IORING_REGISTER_LAST + IORING_OP_LAST)
@ -588,6 +589,32 @@ struct file *io_uring_register_get_file(unsigned int fd, bool registered)
return ERR_PTR(-EOPNOTSUPP);
}
/*
* "blind" registration opcodes are ones where there's no ring given, and
* hence the source fd must be -1.
*/
static int io_uring_register_blind(unsigned int opcode, void __user *arg,
unsigned int nr_args)
{
switch (opcode) {
case IORING_REGISTER_SEND_MSG_RING: {
struct io_uring_sqe sqe;
if (!arg || nr_args != 1)
return -EINVAL;
if (copy_from_user(&sqe, arg, sizeof(sqe)))
return -EFAULT;
/* no flags supported */
if (sqe.flags)
return -EINVAL;
if (sqe.opcode == IORING_OP_MSG_RING)
return io_uring_sync_msg_ring(&sqe);
}
}
return -EINVAL;
}
SYSCALL_DEFINE4(io_uring_register, unsigned int, fd, unsigned int, opcode,
void __user *, arg, unsigned int, nr_args)
{
@ -602,6 +629,9 @@ SYSCALL_DEFINE4(io_uring_register, unsigned int, fd, unsigned int, opcode,
if (opcode >= IORING_REGISTER_LAST)
return -EINVAL;
if (fd == -1)
return io_uring_register_blind(opcode, arg, nr_args);
file = io_uring_register_get_file(fd, use_registered_ring);
if (IS_ERR(file))
return PTR_ERR(file);