842 lines · 20.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <linux/kernel.h>#include <linux/errno.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/namei.h>#include <linux/poll.h>#include <linux/vmalloc.h>#include <linux/io_uring.h> #include <uapi/linux/io_uring.h> #include "io_uring.h"#include "opdef.h"#include "kbuf.h"#include "memmap.h" /* BIDs are addressed by a 16-bit field in a CQE */#define MAX_BIDS_PER_BGID (1 << 16) struct kmem_cache *io_buf_cachep; struct io_provide_buf {	struct file			*file;	__u64				addr;	__u32				len;	__u32				bgid;	__u32				nbufs;	__u16				bid;}; static inline struct io_buffer_list *io_buffer_get_list(struct io_ring_ctx *ctx,							unsigned int bgid){	lockdep_assert_held(&ctx->uring_lock); 	return xa_load(&ctx->io_bl_xa, bgid);} static int io_buffer_add_list(struct io_ring_ctx *ctx,			      struct io_buffer_list *bl, unsigned int bgid){	/*	 * Store buffer group ID and finally mark the list as visible.	 * The normal lookup doesn't care about the visibility as we're	 * always under the ->uring_lock, but the RCU lookup from mmap does.	 */	bl->bgid = bgid;	atomic_set(&bl->refs, 1);	return xa_err(xa_store(&ctx->io_bl_xa, bgid, bl, GFP_KERNEL));} bool io_kbuf_recycle_legacy(struct io_kiocb *req, unsigned issue_flags){	struct io_ring_ctx *ctx = req->ctx;	struct io_buffer_list *bl;	struct io_buffer *buf; 	io_ring_submit_lock(ctx, issue_flags); 	buf = req->kbuf;	bl = io_buffer_get_list(ctx, buf->bgid);	list_add(&buf->list, &bl->buf_list);	req->flags &= ~REQ_F_BUFFER_SELECTED;	req->buf_index = buf->bgid; 	io_ring_submit_unlock(ctx, issue_flags);	return true;} void __io_put_kbuf(struct io_kiocb *req, int len, unsigned issue_flags){	/*	 * We can add this buffer back to two lists:	 *	 * 1) The io_buffers_cache list. This one is protected by the	 *    ctx->uring_lock. If we already hold this lock, add back to this	 *    list as we can grab it from issue as well.	 * 2) The io_buffers_comp list. This one is protected by the	 *    ctx->completion_lock.	 *	 * We migrate buffers from the comp_list to the issue cache list	 * when we need one.	 */	if (issue_flags & IO_URING_F_UNLOCKED) {		struct io_ring_ctx *ctx = req->ctx; 		spin_lock(&ctx->completion_lock);		__io_put_kbuf_list(req, len, &ctx->io_buffers_comp);		spin_unlock(&ctx->completion_lock);	} else {		lockdep_assert_held(&req->ctx->uring_lock); 		__io_put_kbuf_list(req, len, &req->ctx->io_buffers_cache);	}} static void __user *io_provided_buffer_select(struct io_kiocb *req, size_t *len,					      struct io_buffer_list *bl){	if (!list_empty(&bl->buf_list)) {		struct io_buffer *kbuf; 		kbuf = list_first_entry(&bl->buf_list, struct io_buffer, list);		list_del(&kbuf->list);		if (*len == 0 || *len > kbuf->len)			*len = kbuf->len;		if (list_empty(&bl->buf_list))			req->flags |= REQ_F_BL_EMPTY;		req->flags |= REQ_F_BUFFER_SELECTED;		req->kbuf = kbuf;		req->buf_index = kbuf->bid;		return u64_to_user_ptr(kbuf->addr);	}	return NULL;} static int io_provided_buffers_select(struct io_kiocb *req, size_t *len,				      struct io_buffer_list *bl,				      struct iovec *iov){	void __user *buf; 	buf = io_provided_buffer_select(req, len, bl);	if (unlikely(!buf))		return -ENOBUFS; 	iov[0].iov_base = buf;	iov[0].iov_len = *len;	return 1;} static void __user *io_ring_buffer_select(struct io_kiocb *req, size_t *len,					  struct io_buffer_list *bl,					  unsigned int issue_flags){	struct io_uring_buf_ring *br = bl->buf_ring;	__u16 tail, head = bl->head;	struct io_uring_buf *buf;	void __user *ret; 	tail = smp_load_acquire(&br->tail);	if (unlikely(tail == head))		return NULL; 	if (head + 1 == tail)		req->flags |= REQ_F_BL_EMPTY; 	buf = io_ring_head_to_buf(br, head, bl->mask);	if (*len == 0 || *len > buf->len)		*len = buf->len;	req->flags |= REQ_F_BUFFER_RING | REQ_F_BUFFERS_COMMIT;	req->buf_list = bl;	req->buf_index = buf->bid;	ret = u64_to_user_ptr(buf->addr); 	if (issue_flags & IO_URING_F_UNLOCKED || !io_file_can_poll(req)) {		/*		 * If we came in unlocked, we have no choice but to consume the		 * buffer here, otherwise nothing ensures that the buffer won't		 * get used by others. This does mean it'll be pinned until the		 * IO completes, coming in unlocked means we're being called from		 * io-wq context and there may be further retries in async hybrid		 * mode. For the locked case, the caller must call commit when		 * the transfer completes (or if we get -EAGAIN and must poll of		 * retry).		 */		io_kbuf_commit(req, bl, *len, 1);		req->buf_list = NULL;	}	return ret;} void __user *io_buffer_select(struct io_kiocb *req, size_t *len,			      unsigned int issue_flags){	struct io_ring_ctx *ctx = req->ctx;	struct io_buffer_list *bl;	void __user *ret = NULL; 	io_ring_submit_lock(req->ctx, issue_flags); 	bl = io_buffer_get_list(ctx, req->buf_index);	if (likely(bl)) {		if (bl->flags & IOBL_BUF_RING)			ret = io_ring_buffer_select(req, len, bl, issue_flags);		else			ret = io_provided_buffer_select(req, len, bl);	}	io_ring_submit_unlock(req->ctx, issue_flags);	return ret;} /* cap it at a reasonable 256, will be one page even for 4K */#define PEEK_MAX_IMPORT		256 static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg,				struct io_buffer_list *bl){	struct io_uring_buf_ring *br = bl->buf_ring;	struct iovec *iov = arg->iovs;	int nr_iovs = arg->nr_iovs;	__u16 nr_avail, tail, head;	struct io_uring_buf *buf; 	tail = smp_load_acquire(&br->tail);	head = bl->head;	nr_avail = min_t(__u16, tail - head, UIO_MAXIOV);	if (unlikely(!nr_avail))		return -ENOBUFS; 	buf = io_ring_head_to_buf(br, head, bl->mask);	if (arg->max_len) {		u32 len = READ_ONCE(buf->len); 		if (unlikely(!len))			return -ENOBUFS;		/*		 * Limit incremental buffers to 1 segment. No point trying		 * to peek ahead and map more than we need, when the buffers		 * themselves should be large when setup with		 * IOU_PBUF_RING_INC.		 */		if (bl->flags & IOBL_INC) {			nr_avail = 1;		} else {			size_t needed; 			needed = (arg->max_len + len - 1) / len;			needed = min_not_zero(needed, (size_t) PEEK_MAX_IMPORT);			if (nr_avail > needed)				nr_avail = needed;		}	} 	/*	 * only alloc a bigger array if we know we have data to map, eg not	 * a speculative peek operation.	 */	if (arg->mode & KBUF_MODE_EXPAND && nr_avail > nr_iovs && arg->max_len) {		iov = kmalloc_array(nr_avail, sizeof(struct iovec), GFP_KERNEL);		if (unlikely(!iov))			return -ENOMEM;		if (arg->mode & KBUF_MODE_FREE)			kfree(arg->iovs);		arg->iovs = iov;		nr_iovs = nr_avail;	} else if (nr_avail < nr_iovs) {		nr_iovs = nr_avail;	} 	/* set it to max, if not set, so we can use it unconditionally */	if (!arg->max_len)		arg->max_len = INT_MAX; 	req->buf_index = buf->bid;	do {		u32 len = buf->len; 		/* truncate end piece, if needed, for non partial buffers */		if (len > arg->max_len) {			len = arg->max_len;			if (!(bl->flags & IOBL_INC))				buf->len = len;		} 		iov->iov_base = u64_to_user_ptr(buf->addr);		iov->iov_len = len;		iov++; 		arg->out_len += len;		arg->max_len -= len;		if (!arg->max_len)			break; 		buf = io_ring_head_to_buf(br, ++head, bl->mask);	} while (--nr_iovs); 	if (head == tail)		req->flags |= REQ_F_BL_EMPTY; 	req->flags |= REQ_F_BUFFER_RING;	req->buf_list = bl;	return iov - arg->iovs;} int io_buffers_select(struct io_kiocb *req, struct buf_sel_arg *arg,		      unsigned int issue_flags){	struct io_ring_ctx *ctx = req->ctx;	struct io_buffer_list *bl;	int ret = -ENOENT; 	io_ring_submit_lock(ctx, issue_flags);	bl = io_buffer_get_list(ctx, req->buf_index);	if (unlikely(!bl))		goto out_unlock; 	if (bl->flags & IOBL_BUF_RING) {		ret = io_ring_buffers_peek(req, arg, bl);		/*		 * Don't recycle these buffers if we need to go through poll.		 * Nobody else can use them anyway, and holding on to provided		 * buffers for a send/write operation would happen on the app		 * side anyway with normal buffers. Besides, we already		 * committed them, they cannot be put back in the queue.		 */		if (ret > 0) {			req->flags |= REQ_F_BUFFERS_COMMIT | REQ_F_BL_NO_RECYCLE;			io_kbuf_commit(req, bl, arg->out_len, ret);		}	} else {		ret = io_provided_buffers_select(req, &arg->out_len, bl, arg->iovs);	}out_unlock:	io_ring_submit_unlock(ctx, issue_flags);	return ret;} int io_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg){	struct io_ring_ctx *ctx = req->ctx;	struct io_buffer_list *bl;	int ret; 	lockdep_assert_held(&ctx->uring_lock); 	bl = io_buffer_get_list(ctx, req->buf_index);	if (unlikely(!bl))		return -ENOENT; 	if (bl->flags & IOBL_BUF_RING) {		ret = io_ring_buffers_peek(req, arg, bl);		if (ret > 0)			req->flags |= REQ_F_BUFFERS_COMMIT;		return ret;	} 	/* don't support multiple buffer selections for legacy */	return io_provided_buffers_select(req, &arg->max_len, bl, arg->iovs);} static int __io_remove_buffers(struct io_ring_ctx *ctx,			       struct io_buffer_list *bl, unsigned nbufs){	unsigned i = 0; 	/* shouldn't happen */	if (!nbufs)		return 0; 	if (bl->flags & IOBL_BUF_RING) {		i = bl->buf_ring->tail - bl->head;		if (bl->buf_nr_pages) {			int j; 			if (!(bl->flags & IOBL_MMAP)) {				for (j = 0; j < bl->buf_nr_pages; j++)					unpin_user_page(bl->buf_pages[j]);			}			io_pages_unmap(bl->buf_ring, &bl->buf_pages,					&bl->buf_nr_pages, bl->flags & IOBL_MMAP);			bl->flags &= ~IOBL_MMAP;		}		/* make sure it's seen as empty */		INIT_LIST_HEAD(&bl->buf_list);		bl->flags &= ~IOBL_BUF_RING;		return i;	} 	/* protects io_buffers_cache */	lockdep_assert_held(&ctx->uring_lock); 	while (!list_empty(&bl->buf_list)) {		struct io_buffer *nxt; 		nxt = list_first_entry(&bl->buf_list, struct io_buffer, list);		list_move(&nxt->list, &ctx->io_buffers_cache);		if (++i == nbufs)			return i;		cond_resched();	} 	return i;} void io_put_bl(struct io_ring_ctx *ctx, struct io_buffer_list *bl){	if (atomic_dec_and_test(&bl->refs)) {		__io_remove_buffers(ctx, bl, -1U);		kfree_rcu(bl, rcu);	}} void io_destroy_buffers(struct io_ring_ctx *ctx){	struct io_buffer_list *bl;	struct list_head *item, *tmp;	struct io_buffer *buf;	unsigned long index; 	xa_for_each(&ctx->io_bl_xa, index, bl) {		xa_erase(&ctx->io_bl_xa, bl->bgid);		io_put_bl(ctx, bl);	} 	/*	 * Move deferred locked entries to cache before pruning	 */	spin_lock(&ctx->completion_lock);	if (!list_empty(&ctx->io_buffers_comp))		list_splice_init(&ctx->io_buffers_comp, &ctx->io_buffers_cache);	spin_unlock(&ctx->completion_lock); 	list_for_each_safe(item, tmp, &ctx->io_buffers_cache) {		buf = list_entry(item, struct io_buffer, list);		kmem_cache_free(io_buf_cachep, buf);	}} int io_remove_buffers_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_provide_buf *p = io_kiocb_to_cmd(req, struct io_provide_buf);	u64 tmp; 	if (sqe->rw_flags || sqe->addr || sqe->len || sqe->off ||	    sqe->splice_fd_in)		return -EINVAL; 	tmp = READ_ONCE(sqe->fd);	if (!tmp || tmp > MAX_BIDS_PER_BGID)		return -EINVAL; 	memset(p, 0, sizeof(*p));	p->nbufs = tmp;	p->bgid = READ_ONCE(sqe->buf_group);	return 0;} int io_remove_buffers(struct io_kiocb *req, unsigned int issue_flags){	struct io_provide_buf *p = io_kiocb_to_cmd(req, struct io_provide_buf);	struct io_ring_ctx *ctx = req->ctx;	struct io_buffer_list *bl;	int ret = 0; 	io_ring_submit_lock(ctx, issue_flags); 	ret = -ENOENT;	bl = io_buffer_get_list(ctx, p->bgid);	if (bl) {		ret = -EINVAL;		/* can't use provide/remove buffers command on mapped buffers */		if (!(bl->flags & IOBL_BUF_RING))			ret = __io_remove_buffers(ctx, bl, p->nbufs);	}	io_ring_submit_unlock(ctx, issue_flags);	if (ret < 0)		req_set_fail(req);	io_req_set_res(req, ret, 0);	return IOU_OK;} int io_provide_buffers_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	unsigned long size, tmp_check;	struct io_provide_buf *p = io_kiocb_to_cmd(req, struct io_provide_buf);	u64 tmp; 	if (sqe->rw_flags || sqe->splice_fd_in)		return -EINVAL; 	tmp = READ_ONCE(sqe->fd);	if (!tmp || tmp > MAX_BIDS_PER_BGID)		return -E2BIG;	p->nbufs = tmp;	p->addr = READ_ONCE(sqe->addr);	p->len = READ_ONCE(sqe->len); 	if (check_mul_overflow((unsigned long)p->len, (unsigned long)p->nbufs,				&size))		return -EOVERFLOW;	if (check_add_overflow((unsigned long)p->addr, size, &tmp_check))		return -EOVERFLOW; 	size = (unsigned long)p->len * p->nbufs;	if (!access_ok(u64_to_user_ptr(p->addr), size))		return -EFAULT; 	p->bgid = READ_ONCE(sqe->buf_group);	tmp = READ_ONCE(sqe->off);	if (tmp > USHRT_MAX)		return -E2BIG;	if (tmp + p->nbufs > MAX_BIDS_PER_BGID)		return -EINVAL;	p->bid = tmp;	return 0;}

Lines 1–500 of 842.