io_uring: add support for sendto(2) and recvfrom(2)
Subsystems (4)
PATCH 1/1 io_uring: add support for sendto(2) and recvfrom(2)
This adds IORING_OP_SENDTO for sendto(2) support, and IORING_OP_RECVFROM for recvfrom(2) support.
--- fs/io_uring.c | 67 +++++++++++++++++++++++++++++++---- include/linux/socket.h | 2 ++ include/uapi/linux/io_uring.h | 6 ++++ net/socket.c | 4 +-- 4 files changed, 71 insertions(+), 8 deletions(-)
fs/io_uring.c +61 -6
@@ -414,7 +414,14 @@ struct io_sr_msg {struct file *file;union {struct user_msghdr __user *msg; -void __user *buf; +struct { +void __user *buf; +struct sockaddr __user *addr; +union { +void __user *recvfrom_addr_len; +size_t sendto_addr_len; + }; + } sr; };int msg_flags;int bgid;
@@ -878,6 +885,18 @@ static const struct io_op_def io_op_defs[] = { .hash_reg_file = 1, .unbound_nonreg_file = 1, }, + [IORING_OP_SENDTO] = { + .needs_mm = 1, + .needs_file = 1, + .unbound_nonreg_file = 1, + .pollout = 1, + }, + [IORING_OP_RECVFROM] = { + .needs_mm = 1, + .needs_file = 1, + .unbound_nonreg_file = 1, + .pollout = 1, + }, };static void io_wq_submit_work (struct io_wq_work **workptr);
@@ -3545,6 +3564,11 @@ static int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) sr->msg_flags |=MSG_CMSG_COMPAT ;#endif +if (req->opcode ==IORING_OP_SENDTO ) { + sr->sr.addr =u64_to_user_ptr (READ_ONCE (sqe->addr2)); + sr->sr.sendto_addr_len =READ_ONCE (sqe->sendto_addr_len); +return 0 ; + }if (!io || req->opcode ==IORING_OP_SEND )return 0 ;/* iovec is already imported */
@@ -3620,20 +3644,29 @@ static int io_send(struct io_kiocb *req, bool force_nonblock) sock =sock_from_file (req->file, &ret);if (sock) { +struct sockaddr_storage address;struct io_sr_msg *sr = &req->sr_msg;struct msghdr msg;struct iovec iov;unsigned flags; - ret =import_single_range (WRITE , sr->buf, sr->len, &iov, + ret =import_single_range (WRITE , sr->sr.buf, sr->len, &iov, &msg.msg_iter);if (ret)return ret; - msg.msg_name =NULL ; +if (req->opcode ==IORING_OP_SEND || sr->sr.addr ==NULL ) { + msg.msg_name =NULL ; + msg.msg_namelen =0 ; + }else { + ret =move_addr_to_kernel (sr->sr.addr, sr->sr.sendto_addr_len, &address); +if (ret) +return ret; + msg.msg_name = (struct sockaddr *)&address; + msg.msg_namelen = sr->sr.sendto_addr_len; + } msg.msg_control =NULL ; msg.msg_controllen =0 ; - msg.msg_namelen =0 ; flags = req->sr_msg.msg_flags;if (flags &MSG_DONTWAIT )
@@ -3783,6 +3816,11 @@ static int io_recvmsg_prep(struct io_kiocb *req, sr->msg_flags |=MSG_CMSG_COMPAT ;#endif +if (req->opcode ==IORING_OP_RECVFROM ) { + sr->sr.addr =u64_to_user_ptr (READ_ONCE (sqe->addr2)); + sr->sr.recvfrom_addr_len =u64_to_user_ptr (READ_ONCE (sqe->recvfrom_addr_len)); +return 0 ; + }if (!io || req->opcode ==IORING_OP_RECV )return 0 ;/* iovec is already imported */
@@ -3864,8 +3902,9 @@ static int io_recv(struct io_kiocb *req, bool force_nonblock) sock =sock_from_file (req->file, &ret);if (sock) { +struct sockaddr_storage address;struct io_sr_msg *sr = &req->sr_msg; -void __user *buf = sr->buf; +void __user *buf = sr->sr.buf;struct msghdr msg;struct iovec iov;unsigned flags;
@@ -3884,13 +3923,17 @@ static int io_recv(struct io_kiocb *req, bool force_nonblock) } req->flags |=REQ_F_NEED_CLEANUP ; - msg.msg_name =NULL ; msg.msg_control =NULL ; msg.msg_controllen =0 ; msg.msg_namelen =0 ; msg.msg_iocb =NULL ; msg.msg_flags =0 ; +if (req->opcode ==IORING_OP_RECV ) + msg.msg_name =NULL ; +else + msg.msg_name = (struct sockaddr *)&address; + flags = req->sr_msg.msg_flags;if (flags &MSG_DONTWAIT ) req->flags |=REQ_F_NOWAIT ;
@@ -3902,6 +3945,12 @@ static int io_recv(struct io_kiocb *req, bool force_nonblock)return -EAGAIN ;if (ret == -ERESTARTSYS ) ret = -EINTR ; +if (req->opcode ==IORING_OP_RECVFROM && ret >=0 && sr->sr.addr) { +int err =move_addr_to_user (&address, msg.msg_namelen, + sr->sr.addr, sr->sr.recvfrom_addr_len); +if (err <0 ) + ret = err; + } }kfree (kbuf);
@@ -4997,10 +5046,12 @@ static int io_req_defer_prep(struct io_kiocb *req,break ;case IORING_OP_SENDMSG :case IORING_OP_SEND : +case IORING_OP_SENDTO : ret =io_sendmsg_prep (req, sqe);break ;case IORING_OP_RECVMSG :case IORING_OP_RECV : +case IORING_OP_RECVFROM : ret =io_recvmsg_prep (req, sqe);break ;case IORING_OP_CONNECT :
@@ -5205,6 +5256,7 @@ static int io_issue_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe,break ;case IORING_OP_SENDMSG :case IORING_OP_SEND : +case IORING_OP_SENDTO :if (sqe) { ret =io_sendmsg_prep (req, sqe);if (ret <0 )
@@ -5217,6 +5269,7 @@ static int io_issue_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe,break ;case IORING_OP_RECVMSG :case IORING_OP_RECV : +case IORING_OP_RECVFROM :if (sqe) { ret =io_recvmsg_prep (req, sqe);if (ret)
@@ -8266,6 +8319,8 @@ static int __init io_uring_init(void)BUILD_BUG_SQE_ELEM (40 , __u16, buf_index);BUILD_BUG_SQE_ELEM (42 , __u16, personality);BUILD_BUG_SQE_ELEM (44 , __s32, splice_fd_in); +BUILD_BUG_SQE_ELEM (48 , __s32, sendto_addr_len); +BUILD_BUG_SQE_ELEM (48 , __s64, recvfrom_addr_len);BUILD_BUG_ON (ARRAY_SIZE (io_op_defs) !=IORING_OP_LAST );BUILD_BUG_ON (__REQ_F_LAST_BIT >=8 *sizeof (int ));
include/linux/socket.h +2 -0
@@ -364,6 +364,8 @@ struct ucred {#define IPX_TYPE 1extern int move_addr_to_kernel (void __user *uaddr,int ulen,struct sockaddr_storage *kaddr); +extern int move_addr_to_user (struct sockaddr_storage *kaddr,int klen, +void __user *uaddr,int __user *ulen);extern int put_cmsg (struct msghdr *,int level,int type,int len,void *data);struct timespec64 ;
include/uapi/linux/io_uring.h +6 -0
@@ -55,6 +55,10 @@ struct io_uring_sqe {/* personality to use, if used */ __u16 personality;__s32 splice_fd_in; +union { +__u32 sendto_addr_len; +__u64 recvfrom_addr_len; + }; };__u64 __pad2[3 ]; };
@@ -130,6 +134,8 @@ enum {IORING_OP_PROVIDE_BUFFERS ,IORING_OP_REMOVE_BUFFERS ,IORING_OP_TEE , +IORING_OP_SENDTO , +IORING_OP_RECVFROM ,/* this goes last, obviously */ IORING_OP_LAST,
net/socket.c +2 -2
@@ -217,8 +217,8 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *k* specified. Zero is returned for a success. */ -static int move_addr_to_user (struct sockaddr_storage *kaddr,int klen, -void __user *uaddr,int __user *ulen) +int move_addr_to_user (struct sockaddr_storage *kaddr,int klen, +void __user *uaddr,int __user *ulen) {int err;int len;
Signature · 1 line
2.18.4
Jens Axboe
maintainerQuoted 2 lines · Alex Nash
This adds
IORING_OP_SENDTOfor sendto(2) support, andIORING_OP_RECVFROMfor recvfrom(2) support.
I'll ask the basic question that Jann also asked last week - why sendto and recvfrom, when you can use recvmsg and sendmsg to achieve the same thing?
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerQuoted 7 lines · Jens Axboe
Quoted 2 lines · Alex Nash
This adds
IORING_OP_SENDTOfor sendto(2) support, andIORING_OP_RECVFROMfor recvfrom(2) support.I'll ask the basic question that Jann also asked last week - why sendto and recvfrom, when you can use recvmsg and sendmsg to achieve the same thing?
In a private conversation with the author, a good point was brought up that the sendto/recvfrom do not require an allocation of an async context, if we need to defer or go async with the request. I think that's a major win, to be honest. There are other benefits as well (like shorter path), but to me, the async less part is nice and will reduce overhead.
For the patch itself, a few comments below:
fs/io_uring.c +8 -1
@@ -415,7 +415,14 @@ struct io_sr_msg {struct file *file;union {struct user_msghdr __user *msg; -void __user *buf; +struct { +void __user *buf; +struct sockaddr __user *addr; +union { +void __user *recvfrom_addr_len; +size_t sendto_addr_len; + }; + } sr; };int msg_flags;int bgid;
Some inconsistent indentation here.
@@ -876,6 +883,18 @@ static const struct io_op_def io_op_defs[] = {
.hash_reg_file = 1,
.unbound_nonreg_file = 1,
},
+ [IORING_OP_SENDTO] = {
+ .needs_mm = 1,
+ .needs_file = 1,
+ .unbound_nonreg_file = 1,
+ .pollout = 1,
+ },
+ [IORING_OP_RECVFROM] = {
+ .needs_mm = 1,
+ .needs_file = 1,
+ .unbound_nonreg_file = 1,
+ .pollout = 1,
+ },
};
RECVFROM should have pollin set, not pollout?
enum io_mem_account {
@@ -3910,6 +3929,11 @@ static int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
sr->msg_flags |= MSG_CMSG_COMPAT;
#endif
+ if (req->opcode == IORING_OP_SENDTO) {
+ sr->sr.addr = u64_to_user_ptr(READ_ONCE(sqe->addr2));
+ sr->sr.sendto_addr_len = READ_ONCE(sqe->sendto_addr_len);
+ return 0;
+ }
if (!io || req->opcode == IORING_OP_SEND)
return 0;
Might be better with a switch for the opcode here since we're now doing three ops in that prep?
@@ -4148,6 +4181,11 @@ static int io_recvmsg_prep(struct io_kiocb *req,
sr->msg_flags |= MSG_CMSG_COMPAT;
#endif
+ if (req->opcode == IORING_OP_RECVFROM) {
+ sr->sr.addr = u64_to_user_ptr(READ_ONCE(sqe->addr2));
+ sr->sr.recvfrom_addr_len = u64_to_user_ptr(READ_ONCE(sqe->recvfrom_addr_len));
+ return 0;
+ }
if (!io || req->opcode == IORING_OP_RECV)
return 0;
Same comment here.
include/uapi/linux/io_uring.h +4 -0
@@ -56,6 +56,10 @@ struct io_uring_sqe {/* personality to use, if used */ __u16 personality;__s32 splice_fd_in; +union { +__u32 sendto_addr_len; +__u64 recvfrom_addr_len; + }; };__u64 __pad2[3 ]; };
I guess we have no more room in the "regular" parts of the SQE with recvfrom/sendto taking 6 arguments?
Signature · 1 line
Jens Axboe
PATCH 1/1 io_uring: add support for sendto(2) and recvfrom(2)
This adds IORING_OP_SENDTO for sendto(2) support, and IORING_OP_RECVFROM for recvfrom(2) support.
--- fs/io_uring.c | 89 +++++++++++++++++++++++++++++++---- include/linux/socket.h | 2 + include/uapi/linux/io_uring.h | 3 ++ net/socket.c | 4 +- 4 files changed, 86 insertions(+), 12 deletions(-)
fs/io_uring.c +79 -10
@@ -414,7 +414,14 @@ struct io_sr_msg {struct file *file;union {struct user_msghdr __user *msg; -void __user *buf; +struct { +void __user *buf; +struct sockaddr __user *addr; +union { +void __user *recvfrom_addr_len; +size_t sendto_addr_len; + }; + } sr; };int msg_flags;int bgid;
@@ -878,6 +885,18 @@ static const struct io_op_def io_op_defs[] = { .hash_reg_file = 1, .unbound_nonreg_file = 1, }, + [IORING_OP_SENDTO] = { + .needs_mm = 1, + .needs_file = 1, + .unbound_nonreg_file = 1, + .pollout = 1, + }, + [IORING_OP_RECVFROM] = { + .needs_mm = 1, + .needs_file = 1, + .unbound_nonreg_file = 1, + .pollin = 1, + }, };static void io_wq_submit_work (struct io_wq_work **workptr);
@@ -3545,8 +3564,18 @@ static int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) sr->msg_flags |=MSG_CMSG_COMPAT ;#endif -if (!io || req->opcode ==IORING_OP_SEND ) +switch (req->opcode) { +case IORING_OP_SENDMSG : +if (!io) +return 0 ; +break ; +case IORING_OP_SEND : +return 0 ; +case IORING_OP_SENDTO : + sr->sr.addr =u64_to_user_ptr (READ_ONCE (sqe->addr2)); + sr->sr.sendto_addr_len =READ_ONCE (sqe->addr3);return 0 ; + }/* iovec is already imported */ if (req->flags &REQ_F_NEED_CLEANUP )return 0 ;
@@ -3620,20 +3649,29 @@ static int io_send(struct io_kiocb *req, bool force_nonblock) sock =sock_from_file (req->file, &ret);if (sock) { +struct sockaddr_storage address;struct io_sr_msg *sr = &req->sr_msg;struct msghdr msg;struct iovec iov;unsigned flags; - ret =import_single_range (WRITE , sr->buf, sr->len, &iov, + ret =import_single_range (WRITE , sr->sr.buf, sr->len, &iov, &msg.msg_iter);if (ret)return ret; - msg.msg_name =NULL ; +if (req->opcode ==IORING_OP_SEND || sr->sr.addr ==NULL ) { + msg.msg_name =NULL ; + msg.msg_namelen =0 ; + }else { + ret =move_addr_to_kernel (sr->sr.addr, sr->sr.sendto_addr_len, &address); +if (ret) +return ret; + msg.msg_name = (struct sockaddr *)&address; + msg.msg_namelen = sr->sr.sendto_addr_len; + } msg.msg_control =NULL ; msg.msg_controllen =0 ; - msg.msg_namelen =0 ; flags = req->sr_msg.msg_flags;if (flags &MSG_DONTWAIT )
@@ -3783,8 +3821,18 @@ static int io_recvmsg_prep(struct io_kiocb *req, sr->msg_flags |=MSG_CMSG_COMPAT ;#endif -if (!io || req->opcode ==IORING_OP_RECV ) +switch (req->opcode) { +case IORING_OP_RECVMSG : +if (!io) +return 0 ; +break ; +case IORING_OP_RECV : +return 0 ; +case IORING_OP_RECVFROM : + sr->sr.addr =u64_to_user_ptr (READ_ONCE (sqe->addr2)); + sr->sr.recvfrom_addr_len =u64_to_user_ptr (READ_ONCE (sqe->addr3));return 0 ; + }/* iovec is already imported */ if (req->flags &REQ_F_NEED_CLEANUP )return 0 ;
@@ -3864,8 +3912,9 @@ static int io_recv(struct io_kiocb *req, bool force_nonblock) sock =sock_from_file (req->file, &ret);if (sock) { +struct sockaddr_storage address;struct io_sr_msg *sr = &req->sr_msg; -void __user *buf = sr->buf; +void __user *buf = sr->sr.buf;struct msghdr msg;struct iovec iov;unsigned flags;
@@ -3884,13 +3933,17 @@ static int io_recv(struct io_kiocb *req, bool force_nonblock) } req->flags |=REQ_F_NEED_CLEANUP ; - msg.msg_name =NULL ; msg.msg_control =NULL ; msg.msg_controllen =0 ; msg.msg_namelen =0 ; msg.msg_iocb =NULL ; msg.msg_flags =0 ; +if (req->opcode ==IORING_OP_RECV ) + msg.msg_name =NULL ; +else + msg.msg_name = (struct sockaddr *)&address; + flags = req->sr_msg.msg_flags;if (flags &MSG_DONTWAIT ) req->flags |=REQ_F_NOWAIT ;
@@ -3900,10 +3953,20 @@ static int io_recv(struct io_kiocb *req, bool force_nonblock) ret =sock_recvmsg (sock, &msg, flags);if (force_nonblock && ret == -EAGAIN )return -EAGAIN ; -if (ret == -ERESTARTSYS ) - ret = -EINTR ; +if (unlikely (ret <0 )) { +if (ret == -ERESTARTSYS ) + ret = -EINTR ; + goto out; + } +if (req->opcode ==IORING_OP_RECVFROM && sr->sr.addr) { +int err =move_addr_to_user (&address, msg.msg_namelen, + sr->sr.addr, sr->sr.recvfrom_addr_len); +if (err <0 ) + ret = err; + } } +out:kfree (kbuf); req->flags &= ~REQ_F_NEED_CLEANUP ;__io_cqring_add_event (req, ret, cflags);
@@ -4997,10 +5060,12 @@ static int io_req_defer_prep(struct io_kiocb *req,break ;case IORING_OP_SENDMSG :case IORING_OP_SEND : +case IORING_OP_SENDTO : ret =io_sendmsg_prep (req, sqe);break ;case IORING_OP_RECVMSG :case IORING_OP_RECV : +case IORING_OP_RECVFROM : ret =io_recvmsg_prep (req, sqe);break ;case IORING_OP_CONNECT :
@@ -5125,6 +5190,7 @@ static void io_cleanup_req(struct io_kiocb *req)kfree (io->msg.iov);break ;case IORING_OP_RECV : +case IORING_OP_RECVFROM :if (req->flags &REQ_F_BUFFER_SELECTED )kfree (req->sr_msg.kbuf);break ;
@@ -5205,6 +5271,7 @@ static int io_issue_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe,break ;case IORING_OP_SENDMSG :case IORING_OP_SEND : +case IORING_OP_SENDTO :if (sqe) { ret =io_sendmsg_prep (req, sqe);if (ret <0 )
@@ -5217,6 +5284,7 @@ static int io_issue_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe,break ;case IORING_OP_RECVMSG :case IORING_OP_RECV : +case IORING_OP_RECVFROM :if (sqe) { ret =io_recvmsg_prep (req, sqe);if (ret)
@@ -8266,6 +8334,7 @@ static int __init io_uring_init(void)BUILD_BUG_SQE_ELEM (40 , __u16, buf_index);BUILD_BUG_SQE_ELEM (42 , __u16, personality);BUILD_BUG_SQE_ELEM (44 , __s32, splice_fd_in); +BUILD_BUG_SQE_ELEM (48 , __s64, addr3);BUILD_BUG_ON (ARRAY_SIZE (io_op_defs) !=IORING_OP_LAST );BUILD_BUG_ON (__REQ_F_LAST_BIT >=8 *sizeof (int ));
include/linux/socket.h +2 -0
@@ -364,6 +364,8 @@ struct ucred {#define IPX_TYPE 1extern int move_addr_to_kernel (void __user *uaddr,int ulen,struct sockaddr_storage *kaddr); +extern int move_addr_to_user (struct sockaddr_storage *kaddr,int klen, +void __user *uaddr,int __user *ulen);extern int put_cmsg (struct msghdr *,int level,int type,int len,void *data);struct timespec64 ;
include/uapi/linux/io_uring.h +3 -0
@@ -55,6 +55,7 @@ struct io_uring_sqe {/* personality to use, if used */ __u16 personality;__s32 splice_fd_in; +__u64 addr3; };__u64 __pad2[3 ]; };
@@ -130,6 +131,8 @@ enum {IORING_OP_PROVIDE_BUFFERS ,IORING_OP_REMOVE_BUFFERS ,IORING_OP_TEE , +IORING_OP_SENDTO , +IORING_OP_RECVFROM ,/* this goes last, obviously */ IORING_OP_LAST,
net/socket.c +2 -2
@@ -217,8 +217,8 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *k* specified. Zero is returned for a success. */ -static int move_addr_to_user (struct sockaddr_storage *kaddr,int klen, -void __user *uaddr,int __user *ulen) +int move_addr_to_user (struct sockaddr_storage *kaddr,int klen, +void __user *uaddr,int __user *ulen) {int err;int len;
Signature · 1 line
2.18.4