io_uring: add support for shutdown(2)
authorJens Axboe <axboe@kernel.dk>
Sat, 5 Sep 2020 17:14:22 +0000 (11:14 -0600)
committerJens Axboe <axboe@kernel.dk>
Mon, 23 Nov 2020 16:15:15 +0000 (09:15 -0700)
This adds support for the shutdown(2) system call, which is useful for
dealing with sockets.

shutdown(2) may block, so we have to punt it to async context.

Suggested-by: Norman Maurer <norman.maurer@googlemail.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/io_uring.c
include/uapi/linux/io_uring.h

index 3cc1e59..d171987 100644 (file)
@@ -541,6 +541,11 @@ struct io_statx {
        struct statx __user             *buffer;
 };
 
+struct io_shutdown {
+       struct file                     *file;
+       int                             how;
+};
+
 struct io_completion {
        struct file                     *file;
        struct list_head                list;
@@ -667,6 +672,7 @@ struct io_kiocb {
                struct io_splice        splice;
                struct io_provide_buf   pbuf;
                struct io_statx         statx;
+               struct io_shutdown      shutdown;
                /* use only after cleaning per-op data, see io_clean_op() */
                struct io_completion    compl;
        };
@@ -934,6 +940,9 @@ static const struct io_op_def io_op_defs[] = {
                .hash_reg_file          = 1,
                .unbound_nonreg_file    = 1,
        },
+       [IORING_OP_SHUTDOWN] = {
+               .needs_file             = 1,
+       },
 };
 
 enum io_mem_account {
@@ -3591,6 +3600,44 @@ out_free:
        return ret;
 }
 
+static int io_shutdown_prep(struct io_kiocb *req,
+                           const struct io_uring_sqe *sqe)
+{
+#if defined(CONFIG_NET)
+       if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
+               return -EINVAL;
+       if (sqe->ioprio || sqe->off || sqe->addr || sqe->rw_flags ||
+           sqe->buf_index)
+               return -EINVAL;
+
+       req->shutdown.how = READ_ONCE(sqe->len);
+       return 0;
+#else
+       return -EOPNOTSUPP;
+#endif
+}
+
+static int io_shutdown(struct io_kiocb *req, bool force_nonblock)
+{
+#if defined(CONFIG_NET)
+       struct socket *sock;
+       int ret;
+
+       if (force_nonblock)
+               return -EAGAIN;
+
+       sock = sock_from_file(req->file, &ret);
+       if (unlikely(!sock))
+               return ret;
+
+       ret = __sys_shutdown_sock(sock, req->shutdown.how);
+       io_req_complete(req, ret);
+       return 0;
+#else
+       return -EOPNOTSUPP;
+#endif
+}
+
 static int __io_splice_prep(struct io_kiocb *req,
                            const struct io_uring_sqe *sqe)
 {
@@ -5775,6 +5822,8 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
                return io_remove_buffers_prep(req, sqe);
        case IORING_OP_TEE:
                return io_tee_prep(req, sqe);
+       case IORING_OP_SHUTDOWN:
+               return io_shutdown_prep(req, sqe);
        }
 
        printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n",
@@ -6018,6 +6067,9 @@ static int io_issue_sqe(struct io_kiocb *req, bool force_nonblock,
        case IORING_OP_TEE:
                ret = io_tee(req, force_nonblock);
                break;
+       case IORING_OP_SHUTDOWN:
+               ret = io_shutdown(req, force_nonblock);
+               break;
        default:
                ret = -EINVAL;
                break;
index 98d8e06..e943bf0 100644 (file)
@@ -132,6 +132,7 @@ enum {
        IORING_OP_PROVIDE_BUFFERS,
        IORING_OP_REMOVE_BUFFERS,
        IORING_OP_TEE,
+       IORING_OP_SHUTDOWN,
 
        /* this goes last, obviously */
        IORING_OP_LAST,