mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 23:19:34 +02:00
Merge tag 'for-7.3/io_uring-20260819' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux
Pull io_uring update from Jens Axboe:
"On top of the usual cleanups and fixes, the bigger items in here are:
- zcrx work, most of it centered around adding dynamic area
provisioning, plus a bunch of prep and cleanups leading up to it:
scale refilling with large pages, coalesce same-niov RQEs on
refill, separate the RQ head/tail cache lines and cache the RQ
tail, and rework the area creation locking.
- Fix the futex inflight accounting so that only private futex waits
are marked inflight, and don't mark wake requests as inflight at
all.
- Drop the custom iov copy in the buffer select prep and msg header
copy paths, using the generic helpers instead.
- Fix a folio size overflow in io_vec_fill_bvec(), and account the
pages a compound region really uses in the memmap path.
- Fix an iovec leak in uring_cmd when the async cmd isn't recycled,
skip the blocking task work for io_uring_cmd_issue_blocking(), and
don't skip completion for a synchronous multishot cmd
- Defer eventfd signaling when queued from a wakeup handler
- Fix io-wq worker accounting when canceling creation callbacks
- Annotate remote tasks for kcoverage"
* tag 'for-7.3/io_uring-20260819' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux: (30 commits)
io_uring: Add missing include for ITER_SOURCE and ITER_DEST
io_uring/uring_cmd: don't skip completion for a synchronous multishot cmd
io_uring/memmap: account the pages a compound region really uses
io_uring/zcrx: add dynamic area provisioning
io_uring/zcrx: lock area creation with pp_lock
io_uring/zcrx: keep array of areas
io_uring/zcrx: move freelist lock to struct zcrx
io_uring/zcrx: unmap under netdev lock
io_uring/zcrx: split dmabuf unmap and release
io_uring/zcrx: don't pass ifq_reg to area creation
io_uring/zcrx: add helper for deriving area token
io_uring/zcrx: don't reload skb_shinfo
io_urint/zcrx: narrow var scope in io_zcrx_recv_skb()
io_uring/zcrx: constify area_reg on import
io_uring/zcrx: coalesce same-niov RQEs on refill
io_uring/zcrx: cache RQ tail
io_uring/zcrx: add RQ iterator
io_uring/zcrx: move RQ head/tail to separate cache lines
io_uring/zcrx: scale refilling with large pages
io_uring/io-wq: fix worker accounting when canceling creation callbacks
...
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <linux/task_work.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/llist.h>
|
||||
#include <linux/uio.h>
|
||||
#include <uapi/linux/io_uring.h>
|
||||
|
||||
struct iou_loop_params;
|
||||
@@ -20,6 +21,14 @@ enum {
|
||||
* It's also ignored unless IORING_SETUP_DEFER_TASKRUN is set.
|
||||
*/
|
||||
IOU_F_TWQ_LAZY_WAKE = 1,
|
||||
|
||||
/*
|
||||
* Set when task_work is queued from a waitqueue wakeup handler, where
|
||||
* an arbitrary provider waitqueue lock is held. Signaling the CQ ring
|
||||
* eventfd inline from there can recurse back into that lock through
|
||||
* epoll, so the eventfd signal must be deferred.
|
||||
*/
|
||||
IOU_F_TWQ_IN_WAKE = 2,
|
||||
};
|
||||
|
||||
enum io_uring_cmd_flags {
|
||||
@@ -534,6 +543,8 @@ struct io_ring_ctx {
|
||||
struct io_mapped_region ring_region;
|
||||
/* used for optimised request parameter and wait argument passing */
|
||||
struct io_mapped_region param_region;
|
||||
|
||||
struct kcov_common_handle_id kcov_handle;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -116,6 +116,7 @@ enum zcrx_ctrl_op {
|
||||
ZCRX_CTRL_FLUSH_RQ,
|
||||
ZCRX_CTRL_EXPORT,
|
||||
ZCRX_CTRL_ARM_EVENT,
|
||||
ZCRX_CTRL_ADD_AREA,
|
||||
|
||||
__ZCRX_CTRL_LAST,
|
||||
};
|
||||
@@ -134,6 +135,11 @@ struct zcrx_ctrl_arm_event {
|
||||
__u32 __resv[11];
|
||||
};
|
||||
|
||||
struct zcrx_ctrl_add_area {
|
||||
__u64 area_ptr; /* pointer to struct io_uring_zcrx_area_reg */
|
||||
__u64 __resv[5];
|
||||
};
|
||||
|
||||
struct zcrx_ctrl {
|
||||
__u32 zcrx_id;
|
||||
__u32 op; /* see enum zcrx_ctrl_op */
|
||||
@@ -143,6 +149,7 @@ struct zcrx_ctrl {
|
||||
struct zcrx_ctrl_export zc_export;
|
||||
struct zcrx_ctrl_flush_rq zc_flush;
|
||||
struct zcrx_ctrl_arm_event zc_arm_event;
|
||||
struct zcrx_ctrl_add_area zc_area;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -51,9 +51,9 @@ static void io_eventfd_do_signal(struct rcu_head *rcu)
|
||||
/*
|
||||
* Returns true if the caller should put the ev_fd reference, false if not.
|
||||
*/
|
||||
static bool __io_eventfd_signal(struct io_ev_fd *ev_fd)
|
||||
static bool __io_eventfd_signal(struct io_ev_fd *ev_fd, bool defer)
|
||||
{
|
||||
if (eventfd_signal_allowed()) {
|
||||
if (!defer && eventfd_signal_allowed()) {
|
||||
eventfd_signal_mask(ev_fd->cq_ev_fd, EPOLL_URING_WAKE);
|
||||
return true;
|
||||
}
|
||||
@@ -73,7 +73,7 @@ static bool io_eventfd_trigger(struct io_ev_fd *ev_fd)
|
||||
return !ev_fd->eventfd_async || io_wq_current_is_worker();
|
||||
}
|
||||
|
||||
void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event)
|
||||
void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event, bool defer)
|
||||
{
|
||||
bool skip = false;
|
||||
struct io_ev_fd *ev_fd;
|
||||
@@ -113,7 +113,7 @@ void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event)
|
||||
spin_unlock(&ctx->completion_lock);
|
||||
}
|
||||
|
||||
if (skip || __io_eventfd_signal(ev_fd))
|
||||
if (skip || __io_eventfd_signal(ev_fd, defer))
|
||||
io_eventfd_put(ev_fd);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -5,4 +5,4 @@ int io_eventfd_register(struct io_ring_ctx *ctx, void __user *arg,
|
||||
unsigned int eventfd_async);
|
||||
int io_eventfd_unregister(struct io_ring_ctx *ctx);
|
||||
|
||||
void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event);
|
||||
void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event, bool defer);
|
||||
|
||||
+24
-4
@@ -149,7 +149,20 @@ int io_futex_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
||||
!futex_validate_input(iof->futex_flags, iof->futex_mask))
|
||||
return -EINVAL;
|
||||
|
||||
/* Mark as inflight, so file exit cancelation will find it */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int io_futex_wait_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
||||
{
|
||||
struct io_futex *iof = io_kiocb_to_cmd(req, struct io_futex);
|
||||
int ret;
|
||||
|
||||
ret = io_futex_prep(req, sqe);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
/* inflight tracking only needed for mm private hash */
|
||||
if (!(iof->futex_flags & FLAGS_SHARED))
|
||||
io_req_track_inflight(req);
|
||||
return 0;
|
||||
}
|
||||
@@ -168,13 +181,14 @@ static void io_futex_wakev_fn(struct wake_q_head *wake_q, struct futex_q *q)
|
||||
|
||||
io_req_set_res(req, 0, 0);
|
||||
req->io_task_work.func = io_futexv_complete;
|
||||
io_req_task_work_add(req);
|
||||
__io_req_task_work_add(req, IOU_F_TWQ_IN_WAKE);
|
||||
}
|
||||
|
||||
int io_futexv_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
||||
{
|
||||
struct io_futex *iof = io_kiocb_to_cmd(req, struct io_futex);
|
||||
struct io_futexv_data *ifd;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
/* No flags or mask supported for waitv */
|
||||
@@ -199,8 +213,14 @@ int io_futexv_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Mark as inflight, so file exit cancelation will find it */
|
||||
/* inflight tracking only needed for mm private hash */
|
||||
for (i = 0; i < iof->futex_nr; i++) {
|
||||
if (!(ifd->futexv[i].w.flags & FLAGS_SHARED)) {
|
||||
io_req_track_inflight(req);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
iof->futexv_unqueued = 0;
|
||||
req->flags |= REQ_F_ASYNC_DATA;
|
||||
req->async_data = ifd;
|
||||
@@ -217,7 +237,7 @@ static void io_futex_wake_fn(struct wake_q_head *wake_q, struct futex_q *q)
|
||||
|
||||
io_req_set_res(req, 0, 0);
|
||||
req->io_task_work.func = io_futex_complete;
|
||||
io_req_task_work_add(req);
|
||||
__io_req_task_work_add(req, IOU_F_TWQ_IN_WAKE);
|
||||
}
|
||||
|
||||
int io_futexv_wait(struct io_kiocb *req, unsigned int issue_flags)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "cancel.h"
|
||||
|
||||
int io_futex_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
|
||||
int io_futex_wait_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
|
||||
int io_futexv_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
|
||||
int io_futex_wait(struct io_kiocb *req, unsigned int issue_flags);
|
||||
int io_futexv_wait(struct io_kiocb *req, unsigned int issue_flags);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/mmu_context.h>
|
||||
#include <linux/sched/sysctl.h>
|
||||
#include <uapi/linux/io_uring.h>
|
||||
#include <linux/kcov.h>
|
||||
|
||||
#include "io-wq.h"
|
||||
#include "slist.h"
|
||||
@@ -211,9 +212,12 @@ static void io_worker_cancel_cb(struct io_worker *worker)
|
||||
struct io_wq *wq = worker->wq;
|
||||
|
||||
atomic_dec(&acct->nr_running);
|
||||
/* create_worker_cb() has not reserved a worker slot yet. */
|
||||
if (worker->create_work.func != create_worker_cb) {
|
||||
raw_spin_lock(&acct->workers_lock);
|
||||
acct->nr_workers--;
|
||||
raw_spin_unlock(&acct->workers_lock);
|
||||
}
|
||||
io_worker_ref_put(wq);
|
||||
clear_bit_unlock(0, &worker->create_state);
|
||||
io_worker_release(worker);
|
||||
@@ -643,13 +647,17 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
|
||||
unsigned int hash = __io_wq_is_hashed(work_flags)
|
||||
? __io_get_work_hash(work_flags)
|
||||
: -1U;
|
||||
struct io_kiocb *req;
|
||||
|
||||
next_hashed = wq_next_work(work);
|
||||
|
||||
if (do_kill &&
|
||||
(work_flags & IO_WQ_WORK_UNBOUND))
|
||||
atomic_or(IO_WQ_WORK_CANCEL, &work->flags);
|
||||
req = container_of(work, struct io_kiocb, work);
|
||||
kcov_remote_start_common(req->ctx->kcov_handle);
|
||||
io_wq_submit_work(work);
|
||||
kcov_remote_stop();
|
||||
io_assign_current_work(worker, NULL);
|
||||
|
||||
linked = io_wq_free_work(work);
|
||||
|
||||
+4
-13
@@ -59,6 +59,7 @@
|
||||
#include <linux/audit.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/kcov.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/io_uring.h>
|
||||
@@ -293,6 +294,7 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
|
||||
INIT_HLIST_HEAD(&ctx->cancelable_uring_cmd);
|
||||
io_napi_init(ctx);
|
||||
mutex_init(&ctx->mmap_lock);
|
||||
ctx->kcov_handle = kcov_common_handle();
|
||||
|
||||
return ctx;
|
||||
|
||||
@@ -405,7 +407,7 @@ static void io_prep_async_link(struct io_kiocb *req)
|
||||
}
|
||||
}
|
||||
|
||||
static void io_queue_iowq(struct io_kiocb *req)
|
||||
void io_queue_iowq(struct io_kiocb *req)
|
||||
{
|
||||
struct io_uring_task *tctx = req->tctx;
|
||||
|
||||
@@ -433,17 +435,6 @@ static void io_queue_iowq(struct io_kiocb *req)
|
||||
io_wq_enqueue(tctx->io_wq, &req->work);
|
||||
}
|
||||
|
||||
static void io_req_queue_iowq_tw(struct io_tw_req tw_req, io_tw_token_t tw)
|
||||
{
|
||||
io_queue_iowq(tw_req.req);
|
||||
}
|
||||
|
||||
void io_req_queue_iowq(struct io_kiocb *req)
|
||||
{
|
||||
req->io_task_work.func = io_req_queue_iowq_tw;
|
||||
io_req_task_work_add(req);
|
||||
}
|
||||
|
||||
unsigned io_linked_nr(struct io_kiocb *req)
|
||||
{
|
||||
struct io_kiocb *tmp;
|
||||
@@ -484,7 +475,7 @@ void __io_commit_cqring_flush(struct io_ring_ctx *ctx)
|
||||
if (ctx->int_flags & IO_RING_F_OFF_TIMEOUT_USED)
|
||||
io_flush_timeouts(ctx);
|
||||
if (ctx->int_flags & IO_RING_F_HAS_EVFD)
|
||||
io_eventfd_signal(ctx, true);
|
||||
io_eventfd_signal(ctx, true, false);
|
||||
}
|
||||
|
||||
static inline void __io_cq_lock(struct io_ring_ctx *ctx)
|
||||
|
||||
+1
-1
@@ -195,7 +195,7 @@ __cold void io_uring_drop_tctx_refs(struct task_struct *task);
|
||||
|
||||
int io_ring_add_registered_file(struct io_uring_task *tctx, struct file *file,
|
||||
int start, int end);
|
||||
void io_req_queue_iowq(struct io_kiocb *req);
|
||||
void io_queue_iowq(struct io_kiocb *req);
|
||||
|
||||
int io_poll_issue(struct io_kiocb *req, io_tw_token_t tw);
|
||||
int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr);
|
||||
|
||||
+31
-8
@@ -16,8 +16,10 @@
|
||||
#include "zcrx.h"
|
||||
|
||||
static bool io_mem_alloc_compound(struct page **pages, int nr_pages,
|
||||
size_t size, gfp_t gfp)
|
||||
size_t size, gfp_t gfp,
|
||||
struct user_struct *user)
|
||||
{
|
||||
unsigned long nr_compound, extra;
|
||||
struct page *page;
|
||||
int i, order;
|
||||
|
||||
@@ -27,10 +29,23 @@ static bool io_mem_alloc_compound(struct page **pages, int nr_pages,
|
||||
else if (order)
|
||||
gfp |= __GFP_COMP;
|
||||
|
||||
page = alloc_pages(gfp, order);
|
||||
if (!page)
|
||||
/*
|
||||
* get_order() rounds a non power of two size up, so the allocation
|
||||
* can hold more pages than the region exposes. Account those too,
|
||||
* and leave the compound allocation alone if they do not fit.
|
||||
*/
|
||||
nr_compound = 1UL << order;
|
||||
extra = nr_compound - nr_pages;
|
||||
if (extra && user && __io_account_mem(user, extra))
|
||||
return false;
|
||||
|
||||
page = alloc_pages(gfp, order);
|
||||
if (!page) {
|
||||
if (extra && user)
|
||||
__io_unaccount_mem(user, extra);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_pages; i++)
|
||||
pages[i] = page + i;
|
||||
|
||||
@@ -105,8 +120,15 @@ void io_free_region(struct user_struct *user, struct io_mapped_region *mr)
|
||||
}
|
||||
if ((mr->flags & IO_REGION_F_VMAP) && mr->ptr)
|
||||
vunmap(mr->ptr);
|
||||
if (mr->nr_pages && user)
|
||||
__io_unaccount_mem(user, mr->nr_pages);
|
||||
if (mr->nr_pages && user) {
|
||||
unsigned long nr_accounted = mr->nr_pages;
|
||||
|
||||
/* a compound region was accounted for the whole allocation */
|
||||
if (mr->flags & IO_REGION_F_SINGLE_REF)
|
||||
nr_accounted = 1UL << get_order(io_region_size(mr));
|
||||
|
||||
__io_unaccount_mem(user, nr_accounted);
|
||||
}
|
||||
|
||||
memset(mr, 0, sizeof(*mr));
|
||||
}
|
||||
@@ -151,7 +173,8 @@ static int io_region_pin_pages(struct io_mapped_region *mr,
|
||||
|
||||
static int io_region_allocate_pages(struct io_mapped_region *mr,
|
||||
struct io_uring_region_desc *reg,
|
||||
unsigned long mmap_offset)
|
||||
unsigned long mmap_offset,
|
||||
struct user_struct *user)
|
||||
{
|
||||
gfp_t gfp = GFP_KERNEL_ACCOUNT | __GFP_ZERO | __GFP_NOWARN;
|
||||
size_t size = io_region_size(mr);
|
||||
@@ -162,7 +185,7 @@ static int io_region_allocate_pages(struct io_mapped_region *mr,
|
||||
if (!pages)
|
||||
return -ENOMEM;
|
||||
|
||||
if (io_mem_alloc_compound(pages, mr->nr_pages, size, gfp)) {
|
||||
if (io_mem_alloc_compound(pages, mr->nr_pages, size, gfp, user)) {
|
||||
mr->flags |= IO_REGION_F_SINGLE_REF;
|
||||
goto done;
|
||||
}
|
||||
@@ -217,7 +240,7 @@ int io_create_region(struct io_ring_ctx *ctx, struct io_mapped_region *mr,
|
||||
if (reg->flags & IORING_MEM_REGION_TYPE_USER)
|
||||
ret = io_region_pin_pages(mr, reg);
|
||||
else
|
||||
ret = io_region_allocate_pages(mr, reg, mmap_offset);
|
||||
ret = io_region_allocate_pages(mr, reg, mmap_offset, ctx->user);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
|
||||
+16
-47
@@ -236,39 +236,6 @@ static int io_net_import_vec(struct io_kiocb *req, struct io_async_msghdr *iomsg
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int io_compat_msg_copy_hdr(struct io_kiocb *req,
|
||||
struct io_async_msghdr *iomsg,
|
||||
struct compat_msghdr *msg, int ddir,
|
||||
struct sockaddr __user **save_addr)
|
||||
{
|
||||
struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);
|
||||
struct compat_iovec __user *uiov;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(msg, sr->umsg_compat, sizeof(*msg)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = __get_compat_msghdr(&iomsg->msg, msg, save_addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uiov = compat_ptr(msg->msg_iov);
|
||||
if (req->flags & REQ_F_BUFFER_SELECT) {
|
||||
if (msg->msg_iovlen == 0) {
|
||||
sr->len = 0;
|
||||
} else if (msg->msg_iovlen > 1) {
|
||||
return -EINVAL;
|
||||
} else {
|
||||
struct compat_iovec tmp_iov;
|
||||
|
||||
if (copy_from_user(&tmp_iov, uiov, sizeof(tmp_iov)))
|
||||
return -EFAULT;
|
||||
sr->len = tmp_iov.iov_len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int io_copy_msghdr_from_user(struct user_msghdr *msg,
|
||||
struct user_msghdr __user *umsg)
|
||||
{
|
||||
@@ -288,11 +255,10 @@ ua_end:
|
||||
}
|
||||
|
||||
static int io_msg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg,
|
||||
struct user_msghdr *msg, int ddir,
|
||||
struct user_msghdr *msg,
|
||||
struct sockaddr __user **save_addr)
|
||||
{
|
||||
struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);
|
||||
struct user_msghdr __user *umsg = sr->umsg;
|
||||
int ret;
|
||||
|
||||
iomsg->msg.msg_name = &iomsg->addr;
|
||||
@@ -301,7 +267,10 @@ static int io_msg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg,
|
||||
if (io_is_compat(req->ctx)) {
|
||||
struct compat_msghdr cmsg;
|
||||
|
||||
ret = io_compat_msg_copy_hdr(req, iomsg, &cmsg, ddir, save_addr);
|
||||
if (copy_from_user(&cmsg, sr->umsg_compat, sizeof(cmsg)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = __get_compat_msghdr(&iomsg->msg, &cmsg, save_addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -310,10 +279,8 @@ static int io_msg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg,
|
||||
msg->msg_controllen = cmsg.msg_controllen;
|
||||
msg->msg_iov = compat_ptr(cmsg.msg_iov);
|
||||
msg->msg_iovlen = cmsg.msg_iovlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = io_copy_msghdr_from_user(msg, umsg);
|
||||
} else {
|
||||
ret = io_copy_msghdr_from_user(msg, sr->umsg);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
@@ -322,6 +289,7 @@ static int io_msg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg,
|
||||
ret = __copy_msghdr(&iomsg->msg, msg, save_addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (req->flags & REQ_F_BUFFER_SELECT) {
|
||||
if (msg->msg_iovlen == 0) {
|
||||
@@ -329,12 +297,13 @@ static int io_msg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg,
|
||||
} else if (msg->msg_iovlen > 1) {
|
||||
return -EINVAL;
|
||||
} else {
|
||||
struct iovec __user *uiov = msg->msg_iov;
|
||||
struct iovec tmp_iov;
|
||||
struct iovec fast_iov, *iov;
|
||||
|
||||
if (copy_from_user(&tmp_iov, uiov, sizeof(tmp_iov)))
|
||||
return -EFAULT;
|
||||
sr->len = tmp_iov.iov_len;
|
||||
iov = iovec_from_user(msg->msg_iov, 1, 1, &fast_iov,
|
||||
io_is_compat(req->ctx));
|
||||
if (IS_ERR(iov))
|
||||
return PTR_ERR(iov);
|
||||
sr->len = iov->iov_len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -402,7 +371,7 @@ static int io_sendmsg_setup(struct io_kiocb *req, const struct io_uring_sqe *sqe
|
||||
|
||||
sr->flags |= IORING_SEND_VECTORIZED;
|
||||
sr->umsg = u64_to_user_ptr(READ_ONCE(sqe->addr));
|
||||
ret = io_msg_copy_hdr(req, kmsg, &msg, ITER_SOURCE, NULL);
|
||||
ret = io_msg_copy_hdr(req, kmsg, &msg, NULL);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
/* save msg_control as sys_sendmsg() overwrites it */
|
||||
@@ -761,7 +730,7 @@ static int io_recvmsg_copy_hdr(struct io_kiocb *req,
|
||||
struct user_msghdr msg;
|
||||
int ret;
|
||||
|
||||
ret = io_msg_copy_hdr(req, iomsg, &msg, ITER_DEST, &iomsg->uaddr);
|
||||
ret = io_msg_copy_hdr(req, iomsg, &msg, &iomsg->uaddr);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
|
||||
+1
-1
@@ -467,7 +467,7 @@ const struct io_issue_def io_issue_defs[] = {
|
||||
},
|
||||
[IORING_OP_FUTEX_WAIT] = {
|
||||
#if defined(CONFIG_FUTEX)
|
||||
.prep = io_futex_prep,
|
||||
.prep = io_futex_wait_prep,
|
||||
.issue = io_futex_wait,
|
||||
#else
|
||||
.prep = io_eopnotsupp_prep,
|
||||
|
||||
+12
-11
@@ -208,9 +208,9 @@ enum {
|
||||
IOU_POLL_REQUEUE = 4,
|
||||
};
|
||||
|
||||
static void __io_poll_execute(struct io_kiocb *req, int mask)
|
||||
static void __io_poll_execute(struct io_kiocb *req, int mask, unsigned tw_flags)
|
||||
{
|
||||
unsigned flags = 0;
|
||||
unsigned flags = tw_flags;
|
||||
|
||||
io_req_set_res(req, mask, 0);
|
||||
req->io_task_work.func = io_poll_task_func;
|
||||
@@ -218,14 +218,15 @@ static void __io_poll_execute(struct io_kiocb *req, int mask)
|
||||
trace_io_uring_task_add(req, mask);
|
||||
|
||||
if (!(req->flags & REQ_F_POLL_NO_LAZY))
|
||||
flags = IOU_F_TWQ_LAZY_WAKE;
|
||||
flags |= IOU_F_TWQ_LAZY_WAKE;
|
||||
__io_req_task_work_add(req, flags);
|
||||
}
|
||||
|
||||
static inline void io_poll_execute(struct io_kiocb *req, int res)
|
||||
static inline void io_poll_execute(struct io_kiocb *req, int res,
|
||||
unsigned tw_flags)
|
||||
{
|
||||
if (io_poll_get_ownership(req))
|
||||
__io_poll_execute(req, res);
|
||||
__io_poll_execute(req, res, tw_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -344,7 +345,7 @@ void io_poll_task_func(struct io_tw_req tw_req, io_tw_token_t tw)
|
||||
if (ret == IOU_POLL_NO_ACTION) {
|
||||
return;
|
||||
} else if (ret == IOU_POLL_REQUEUE) {
|
||||
__io_poll_execute(req, 0);
|
||||
__io_poll_execute(req, 0, 0);
|
||||
return;
|
||||
}
|
||||
io_poll_remove_entries(req);
|
||||
@@ -383,7 +384,7 @@ static void io_poll_cancel_req(struct io_kiocb *req)
|
||||
{
|
||||
io_poll_mark_cancelled(req);
|
||||
/* kick tw, which should complete the request */
|
||||
io_poll_execute(req, 0);
|
||||
io_poll_execute(req, 0, 0);
|
||||
}
|
||||
|
||||
#define IO_ASYNC_POLL_COMMON (EPOLLONESHOT | EPOLLPRI)
|
||||
@@ -392,7 +393,7 @@ static __cold int io_pollfree_wake(struct io_kiocb *req, struct io_poll *poll)
|
||||
{
|
||||
io_poll_mark_cancelled(req);
|
||||
/* we have to kick tw in case it's not already */
|
||||
io_poll_execute(req, 0);
|
||||
io_poll_execute(req, 0, IOU_F_TWQ_IN_WAKE);
|
||||
io_poll_remove_waitq(poll);
|
||||
return 1;
|
||||
}
|
||||
@@ -430,7 +431,7 @@ static int io_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync,
|
||||
else
|
||||
req->flags &= ~REQ_F_SINGLE_POLL;
|
||||
}
|
||||
__io_poll_execute(req, mask);
|
||||
__io_poll_execute(req, mask, IOU_F_TWQ_IN_WAKE);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -618,7 +619,7 @@ static int __io_arm_poll_handler(struct io_kiocb *req,
|
||||
|
||||
if (mask && (poll->events & EPOLLET) &&
|
||||
io_poll_can_finish_inline(req, ipt)) {
|
||||
__io_poll_execute(req, mask);
|
||||
__io_poll_execute(req, mask, 0);
|
||||
return 0;
|
||||
}
|
||||
io_napi_add(req);
|
||||
@@ -629,7 +630,7 @@ static int __io_arm_poll_handler(struct io_kiocb *req,
|
||||
* poll was waken up, queue up a tw, it'll deal with it.
|
||||
*/
|
||||
if (atomic_cmpxchg(&req->poll_refs, 1, 0) != 1)
|
||||
__io_poll_execute(req, 0);
|
||||
__io_poll_execute(req, 0, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ static ssize_t io_query_zcrx(union io_query_data *data)
|
||||
e->register_flags = ZCRX_SUPPORTED_REG_FLAGS;
|
||||
e->area_flags = IORING_ZCRX_AREA_DMABUF;
|
||||
e->nr_ctrl_opcodes = __ZCRX_CTRL_LAST;
|
||||
e->rq_hdr_size = sizeof(struct io_uring);
|
||||
e->rq_hdr_size = sizeof(struct zcrx_rq_hdr);
|
||||
e->rq_hdr_alignment = L1_CACHE_BYTES;
|
||||
e->features = ZCRX_FEATURES;
|
||||
e->__resv2 = 0;
|
||||
|
||||
+1
-1
@@ -1477,7 +1477,7 @@ static int io_vec_fill_bvec(int ddir, struct iov_iter *iter,
|
||||
struct iovec *iovec, unsigned nr_iovs,
|
||||
struct iou_vec *vec)
|
||||
{
|
||||
unsigned long folio_size = 1 << imu->folio_shift;
|
||||
unsigned long folio_size = 1UL << imu->folio_shift;
|
||||
unsigned long folio_mask = folio_size - 1;
|
||||
struct bio_vec *res_bvec = vec->bvec;
|
||||
size_t total_len = 0;
|
||||
|
||||
+5
-19
@@ -9,7 +9,6 @@
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/io_uring/cmd.h>
|
||||
#include <linux/indirect_call_wrapper.h>
|
||||
|
||||
@@ -50,33 +49,20 @@ static bool io_file_supports_nowait(struct io_kiocb *req, __poll_t mask)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int io_iov_compat_buffer_select_prep(struct io_rw *rw)
|
||||
{
|
||||
struct compat_iovec __user *uiov = u64_to_user_ptr(rw->addr);
|
||||
struct compat_iovec iov;
|
||||
|
||||
if (copy_from_user(&iov, uiov, sizeof(iov)))
|
||||
return -EFAULT;
|
||||
rw->len = iov.iov_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int io_iov_buffer_select_prep(struct io_kiocb *req)
|
||||
{
|
||||
struct iovec __user *uiov;
|
||||
struct iovec iov;
|
||||
struct iovec fast_iov, *iov;
|
||||
struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
|
||||
|
||||
if (rw->len != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (io_is_compat(req->ctx))
|
||||
return io_iov_compat_buffer_select_prep(rw);
|
||||
|
||||
uiov = u64_to_user_ptr(rw->addr);
|
||||
if (copy_from_user(&iov, uiov, sizeof(*uiov)))
|
||||
return -EFAULT;
|
||||
rw->len = iov.iov_len;
|
||||
iov = iovec_from_user(uiov, 1, 1, &fast_iov, io_is_compat(req->ctx));
|
||||
if (IS_ERR(iov))
|
||||
return PTR_ERR(iov);
|
||||
rw->len = iov->iov_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -13,6 +13,7 @@
|
||||
#include <linux/cpuset.h>
|
||||
#include <linux/sched/cputime.h>
|
||||
#include <linux/io_uring.h>
|
||||
#include <linux/kcov.h>
|
||||
|
||||
#include <uapi/linux/io_uring.h>
|
||||
|
||||
@@ -332,10 +333,14 @@ static int io_sq_thread(void *data)
|
||||
|
||||
cap_entries = !list_is_singular(&sqd->ctx_list);
|
||||
list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
|
||||
int ret = __io_sq_thread(ctx, sqd, cap_entries, &ist);
|
||||
int ret;
|
||||
|
||||
kcov_remote_start_common(ctx->kcov_handle);
|
||||
ret = __io_sq_thread(ctx, sqd, cap_entries, &ist);
|
||||
|
||||
if (!sqt_spin && (ret > 0 || !list_empty(&ctx->iopoll_list)))
|
||||
sqt_spin = true;
|
||||
kcov_remote_stop();
|
||||
}
|
||||
if (io_sq_tw(IORING_TW_CAP_ENTRIES_VALUE))
|
||||
sqt_spin = true;
|
||||
|
||||
+1
-1
@@ -173,7 +173,7 @@ void io_req_local_work_add(struct io_kiocb *req, unsigned flags)
|
||||
if (mpscq_push(&ctx->work_list, &req->io_task_work.node)) {
|
||||
io_ctx_mark_taskrun(ctx);
|
||||
if (data_race(ctx->int_flags) & IO_RING_F_HAS_EVFD)
|
||||
io_eventfd_signal(ctx, false);
|
||||
io_eventfd_signal(ctx, false, flags & IOU_F_TWQ_IN_WAKE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -38,6 +38,8 @@ static void io_req_uring_cleanup(struct io_kiocb *req, unsigned int issue_flags)
|
||||
if (io_alloc_cache_put(&req->ctx->cmd_cache, ac)) {
|
||||
ioucmd->sqe = NULL;
|
||||
io_req_async_data_clear(req, REQ_F_NEED_CLEANUP);
|
||||
} else {
|
||||
io_vec_free(&ac->vec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +210,8 @@ int io_uring_cmd_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
||||
ac = io_uring_alloc_async_data(&req->ctx->cmd_cache, req);
|
||||
if (!ac)
|
||||
return -ENOMEM;
|
||||
if (ac->vec.iovec)
|
||||
req->flags |= REQ_F_NEED_CLEANUP;
|
||||
ioucmd->sqe = sqe;
|
||||
return 0;
|
||||
}
|
||||
@@ -269,10 +273,6 @@ int io_uring_cmd(struct io_kiocb *req, unsigned int issue_flags)
|
||||
}
|
||||
|
||||
ret = file->f_op->uring_cmd(ioucmd, issue_flags);
|
||||
if (ioucmd->flags & IORING_URING_CMD_MULTISHOT) {
|
||||
if (ret >= 0)
|
||||
return IOU_ISSUE_SKIP_COMPLETE;
|
||||
}
|
||||
if (ret == -EAGAIN) {
|
||||
ioucmd->flags |= IORING_URING_CMD_REISSUE;
|
||||
return ret;
|
||||
@@ -326,7 +326,7 @@ void io_uring_cmd_issue_blocking(struct io_uring_cmd *ioucmd)
|
||||
{
|
||||
struct io_kiocb *req = cmd_to_io_kiocb(ioucmd);
|
||||
|
||||
io_req_queue_iowq(req);
|
||||
io_queue_iowq(req);
|
||||
}
|
||||
|
||||
int io_cmd_poll_multishot(struct io_uring_cmd *cmd,
|
||||
|
||||
+1
-1
@@ -253,7 +253,7 @@ static int io_waitid_wait(struct wait_queue_entry *wait, unsigned mode,
|
||||
return 1;
|
||||
|
||||
req->io_task_work.func = io_waitid_cb;
|
||||
io_req_task_work_add(req);
|
||||
__io_req_task_work_add(req, IOU_F_TWQ_IN_WAKE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+282
-110
@@ -28,10 +28,28 @@
|
||||
#include "zcrx.h"
|
||||
#include "rsrc.h"
|
||||
|
||||
#define ZCRX_MAX_FRAGS_PER_PAGE MAX(PAGE_SIZE / 1024, 1)
|
||||
/*
|
||||
* We need a reasonable limit to be able to fill in 64 entries on average
|
||||
* for 1500 byte MTU. Over-estimate it to keep it pow2.
|
||||
*/
|
||||
#define ZCRX_REFILL_CAP MIN(64 * ZCRX_MAX_FRAGS_PER_PAGE, 1024)
|
||||
|
||||
#define IO_ZCRX_AREA_SUPPORTED_FLAGS (IORING_ZCRX_AREA_DMABUF)
|
||||
#define ZCRX_MAX_AREAS 1024
|
||||
|
||||
#define IO_DMA_ATTR (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING)
|
||||
|
||||
static inline u32 zcrx_next_area_id(struct io_zcrx_ifq *zcrx)
|
||||
{
|
||||
return zcrx->nr_areas;
|
||||
}
|
||||
|
||||
static inline u64 zcrx_area_id_to_token(u32 area_id)
|
||||
{
|
||||
return (u64)area_id << IORING_ZCRX_AREA_SHIFT;
|
||||
}
|
||||
|
||||
static inline struct io_zcrx_ifq *io_pp_to_ifq(struct page_pool *pp)
|
||||
{
|
||||
return pp->mp_priv;
|
||||
@@ -109,27 +127,31 @@ static int io_populate_area_dma(struct io_zcrx_ifq *ifq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void io_release_dmabuf(struct io_zcrx_mem *mem)
|
||||
static void io_unmap_dmabuf(struct io_zcrx_mem *mem)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_DMA_SHARED_BUFFER))
|
||||
return;
|
||||
|
||||
if (mem->sgt)
|
||||
dma_buf_unmap_attachment_unlocked(mem->attach, mem->sgt,
|
||||
DMA_FROM_DEVICE);
|
||||
if (mem->attach)
|
||||
dma_buf_detach(mem->dmabuf, mem->attach);
|
||||
if (mem->dmabuf)
|
||||
dma_buf_put(mem->dmabuf);
|
||||
|
||||
mem->sgt = NULL;
|
||||
mem->attach = NULL;
|
||||
}
|
||||
|
||||
static void io_release_dmabuf(struct io_zcrx_mem *mem)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_DMA_SHARED_BUFFER))
|
||||
return;
|
||||
if (mem->dmabuf)
|
||||
dma_buf_put(mem->dmabuf);
|
||||
mem->dmabuf = NULL;
|
||||
}
|
||||
|
||||
static int io_import_dmabuf(struct io_zcrx_ifq *ifq,
|
||||
struct io_zcrx_mem *mem,
|
||||
struct io_uring_zcrx_area_reg *area_reg)
|
||||
const struct io_uring_zcrx_area_reg *area_reg)
|
||||
{
|
||||
unsigned long off = (unsigned long)area_reg->addr;
|
||||
unsigned long len = (unsigned long)area_reg->len;
|
||||
@@ -178,6 +200,7 @@ static int io_import_dmabuf(struct io_zcrx_ifq *ifq,
|
||||
mem->size = len;
|
||||
return 0;
|
||||
err:
|
||||
io_unmap_dmabuf(mem);
|
||||
io_release_dmabuf(mem);
|
||||
return ret;
|
||||
}
|
||||
@@ -201,7 +224,7 @@ static unsigned long io_count_account_pages(struct page **pages, unsigned nr_pag
|
||||
|
||||
static int io_import_umem(struct io_zcrx_ifq *ifq,
|
||||
struct io_zcrx_mem *mem,
|
||||
struct io_uring_zcrx_area_reg *area_reg)
|
||||
const struct io_uring_zcrx_area_reg *area_reg)
|
||||
{
|
||||
struct page **pages;
|
||||
int nr_pages, ret;
|
||||
@@ -267,7 +290,7 @@ static void io_release_area_mem(struct io_zcrx_mem *mem)
|
||||
|
||||
static int io_import_area(struct io_zcrx_ifq *ifq,
|
||||
struct io_zcrx_mem *mem,
|
||||
struct io_uring_zcrx_area_reg *area_reg)
|
||||
const struct io_uring_zcrx_area_reg *area_reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -289,13 +312,14 @@ static int io_import_area(struct io_zcrx_ifq *ifq,
|
||||
return io_import_umem(ifq, mem, area_reg);
|
||||
}
|
||||
|
||||
static void io_zcrx_unmap_area(struct io_zcrx_ifq *ifq,
|
||||
static void __io_zcrx_unmap_area(struct io_zcrx_ifq *ifq,
|
||||
struct io_zcrx_area *area)
|
||||
{
|
||||
int i;
|
||||
|
||||
guard(mutex)(&ifq->pp_lock);
|
||||
if (!area->is_mapped)
|
||||
lockdep_assert_held(&ifq->pp_lock);
|
||||
|
||||
if (!area || !area->is_mapped)
|
||||
return;
|
||||
area->is_mapped = false;
|
||||
|
||||
@@ -305,13 +329,23 @@ static void io_zcrx_unmap_area(struct io_zcrx_ifq *ifq,
|
||||
}
|
||||
|
||||
if (area->mem.is_dmabuf) {
|
||||
io_release_dmabuf(&area->mem);
|
||||
io_unmap_dmabuf(&area->mem);
|
||||
} else {
|
||||
dma_unmap_sgtable(ifq->dev, &area->mem.page_sg_table,
|
||||
DMA_FROM_DEVICE, IO_DMA_ATTR);
|
||||
}
|
||||
}
|
||||
|
||||
static void io_zcrx_unmap_areas(struct io_zcrx_ifq *ifq)
|
||||
{
|
||||
unsigned area_idx;
|
||||
|
||||
lockdep_assert_held(&ifq->pp_lock);
|
||||
|
||||
for (area_idx = 0; area_idx < ifq->nr_areas; area_idx++)
|
||||
__io_zcrx_unmap_area(ifq, ifq->areas[area_idx]);
|
||||
}
|
||||
|
||||
static void zcrx_sync_for_device(struct page_pool *pp, struct io_zcrx_ifq *zcrx,
|
||||
netmem_ref *netmems, unsigned nr)
|
||||
{
|
||||
@@ -351,16 +385,16 @@ static inline atomic_t *io_get_user_counter(struct net_iov *niov)
|
||||
return &area->user_refs[net_iov_idx(niov)];
|
||||
}
|
||||
|
||||
static bool io_zcrx_put_niov_uref(struct net_iov *niov)
|
||||
static bool io_zcrx_put_niov_uref(struct net_iov *niov, unsigned refs)
|
||||
{
|
||||
atomic_t *uref = io_get_user_counter(niov);
|
||||
int old;
|
||||
|
||||
old = atomic_read(uref);
|
||||
do {
|
||||
if (unlikely(old == 0))
|
||||
if (unlikely(old < refs))
|
||||
return false;
|
||||
} while (!atomic_try_cmpxchg(uref, &old, old - 1));
|
||||
} while (!atomic_try_cmpxchg(uref, &old, old - refs));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -372,9 +406,9 @@ static void io_zcrx_get_niov_uref(struct net_iov *niov)
|
||||
|
||||
static void io_fill_zcrx_offsets(struct io_uring_zcrx_offsets *offsets)
|
||||
{
|
||||
offsets->head = offsetof(struct io_uring, head);
|
||||
offsets->tail = offsetof(struct io_uring, tail);
|
||||
offsets->rqes = ALIGN(sizeof(struct io_uring), L1_CACHE_BYTES);
|
||||
offsets->head = offsetof(struct zcrx_rq_hdr, head);
|
||||
offsets->tail = offsetof(struct zcrx_rq_hdr, tail);
|
||||
offsets->rqes = ALIGN(sizeof(struct zcrx_rq_hdr), L1_CACHE_BYTES);
|
||||
}
|
||||
|
||||
static int io_allocate_rbuf_ring(struct io_ring_ctx *ctx,
|
||||
@@ -402,7 +436,7 @@ static int io_allocate_rbuf_ring(struct io_ring_ctx *ctx,
|
||||
return ret;
|
||||
|
||||
ptr = io_region_get_ptr(&ifq->rq_region);
|
||||
ifq->rq.ring = (struct io_uring *)ptr;
|
||||
ifq->rq.ring = (struct zcrx_rq_hdr *)ptr;
|
||||
ifq->rq.rqes = (struct io_uring_zcrx_rqe *)(ptr + off);
|
||||
|
||||
memset(ifq->rq.ring, 0, sizeof(*ifq->rq.ring));
|
||||
@@ -420,7 +454,8 @@ static void io_free_rbuf_ring(struct io_zcrx_ifq *ifq)
|
||||
static void io_zcrx_free_area(struct io_zcrx_ifq *ifq,
|
||||
struct io_zcrx_area *area)
|
||||
{
|
||||
io_zcrx_unmap_area(ifq, area);
|
||||
if (WARN_ON_ONCE(area->is_mapped))
|
||||
return;
|
||||
io_release_area_mem(&area->mem);
|
||||
|
||||
if (area->mem.account_pages)
|
||||
@@ -437,31 +472,52 @@ static int io_zcrx_append_area(struct io_zcrx_ifq *ifq,
|
||||
struct io_zcrx_area *area)
|
||||
{
|
||||
bool kern_readable = !area->mem.is_dmabuf;
|
||||
struct io_zcrx_area **areas, **old_areas;
|
||||
unsigned old_nr;
|
||||
|
||||
if (WARN_ON_ONCE(ifq->area))
|
||||
if (ifq->kern_readable != kern_readable)
|
||||
return -EINVAL;
|
||||
if (WARN_ON_ONCE(ifq->kern_readable != kern_readable))
|
||||
if (ifq->nr_areas + 1 > ZCRX_MAX_AREAS)
|
||||
return -EINVAL;
|
||||
|
||||
ifq->area = area;
|
||||
old_areas = ifq->areas;
|
||||
old_nr = ifq->nr_areas;
|
||||
|
||||
areas = kmalloc_array(old_nr + 1, sizeof(areas[0]),
|
||||
GFP_KERNEL_ACCOUNT | __GFP_ZERO);
|
||||
if (!areas)
|
||||
return -ENOMEM;
|
||||
if (old_areas)
|
||||
memcpy(areas, old_areas, old_nr * sizeof(areas[0]));
|
||||
areas[old_nr] = area;
|
||||
|
||||
scoped_guard(spinlock_bh, &ifq->rq.lock) {
|
||||
guard(spinlock_bh)(&ifq->alloc_lock);
|
||||
ifq->areas = areas;
|
||||
ifq->nr_areas = old_nr + 1;
|
||||
}
|
||||
kfree(old_areas);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int io_zcrx_create_area(struct io_zcrx_ifq *ifq,
|
||||
static int __zcrx_create_area(struct io_zcrx_ifq *ifq,
|
||||
struct io_uring_zcrx_area_reg *area_reg,
|
||||
struct io_uring_zcrx_ifq_reg *reg)
|
||||
u32 rx_buf_len)
|
||||
{
|
||||
int buf_size_shift = PAGE_SHIFT;
|
||||
struct io_zcrx_area *area;
|
||||
unsigned nr_iovs;
|
||||
int i, ret;
|
||||
|
||||
if (reg->rx_buf_len) {
|
||||
if (!is_power_of_2(reg->rx_buf_len) ||
|
||||
reg->rx_buf_len < PAGE_SIZE)
|
||||
lockdep_assert_held(&ifq->pp_lock);
|
||||
|
||||
if (rx_buf_len) {
|
||||
if (!is_power_of_2(rx_buf_len) || rx_buf_len < PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
buf_size_shift = ilog2(reg->rx_buf_len);
|
||||
buf_size_shift = ilog2(rx_buf_len);
|
||||
}
|
||||
if (ifq->niov_shift && ifq->niov_shift != buf_size_shift)
|
||||
return -EINVAL;
|
||||
if (!ifq->dev && buf_size_shift != PAGE_SHIFT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -518,19 +574,28 @@ static int io_zcrx_create_area(struct io_zcrx_ifq *ifq,
|
||||
|
||||
area->free_count = nr_iovs;
|
||||
/* we're only supporting one area per ifq for now */
|
||||
area->area_id = 0;
|
||||
area_reg->rq_area_token = (u64)area->area_id << IORING_ZCRX_AREA_SHIFT;
|
||||
spin_lock_init(&area->freelist_lock);
|
||||
area->area_id = zcrx_next_area_id(ifq);
|
||||
area_reg->rq_area_token = zcrx_area_id_to_token(area->area_id);
|
||||
|
||||
ret = io_zcrx_append_area(ifq, area);
|
||||
if (!ret)
|
||||
return 0;
|
||||
err:
|
||||
if (area)
|
||||
if (area) {
|
||||
__io_zcrx_unmap_area(ifq, area);
|
||||
io_zcrx_free_area(ifq, area);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int io_zcrx_create_area(struct io_zcrx_ifq *ifq,
|
||||
struct io_uring_zcrx_area_reg *area_reg,
|
||||
struct io_uring_zcrx_ifq_reg *reg)
|
||||
{
|
||||
guard(mutex)(&ifq->pp_lock);
|
||||
return __zcrx_create_area(ifq, area_reg, reg->rx_buf_len);
|
||||
}
|
||||
|
||||
static struct io_zcrx_ifq *io_zcrx_ifq_alloc(struct io_ring_ctx *ctx)
|
||||
{
|
||||
struct io_zcrx_ifq *ifq;
|
||||
@@ -542,6 +607,7 @@ static struct io_zcrx_ifq *io_zcrx_ifq_alloc(struct io_ring_ctx *ctx)
|
||||
ifq->if_rxq = -1;
|
||||
spin_lock_init(&ifq->ctx_lock);
|
||||
spin_lock_init(&ifq->rq.lock);
|
||||
spin_lock_init(&ifq->alloc_lock);
|
||||
mutex_init(&ifq->pp_lock);
|
||||
refcount_set(&ifq->refs, 1);
|
||||
refcount_set(&ifq->user_refs, 1);
|
||||
@@ -550,7 +616,7 @@ static struct io_zcrx_ifq *io_zcrx_ifq_alloc(struct io_ring_ctx *ctx)
|
||||
|
||||
static void io_zcrx_drop_netdev(struct io_zcrx_ifq *ifq)
|
||||
{
|
||||
guard(mutex)(&ifq->pp_lock);
|
||||
lockdep_assert_held(&ifq->pp_lock);
|
||||
|
||||
if (!ifq->netdev)
|
||||
return;
|
||||
@@ -574,11 +640,13 @@ static void io_close_queue(struct io_zcrx_ifq *ifq)
|
||||
}
|
||||
|
||||
if (netdev) {
|
||||
if (ifq->if_rxq != -1) {
|
||||
netdev_lock(netdev);
|
||||
if (ifq->if_rxq != -1)
|
||||
netif_mp_close_rxq(netdev, ifq->if_rxq, &p);
|
||||
|
||||
scoped_guard(mutex, &ifq->pp_lock)
|
||||
io_zcrx_unmap_areas(ifq);
|
||||
netdev_unlock(netdev);
|
||||
}
|
||||
netdev_put(netdev, &netdev_tracker);
|
||||
}
|
||||
ifq->if_rxq = -1;
|
||||
@@ -586,6 +654,8 @@ static void io_close_queue(struct io_zcrx_ifq *ifq)
|
||||
|
||||
static void io_zcrx_ifq_free(struct io_zcrx_ifq *ifq)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (WARN_ON_ONCE(ifq->if_rxq != -1))
|
||||
return;
|
||||
if (WARN_ON_ONCE(ifq->netdev != NULL))
|
||||
@@ -593,8 +663,8 @@ static void io_zcrx_ifq_free(struct io_zcrx_ifq *ifq)
|
||||
if (WARN_ON_ONCE(ifq->master_ctx))
|
||||
return;
|
||||
|
||||
if (ifq->area)
|
||||
io_zcrx_free_area(ifq, ifq->area);
|
||||
for (i = 0; i < ifq->nr_areas; i++)
|
||||
io_zcrx_free_area(ifq, ifq->areas[i]);
|
||||
if (ifq->mm_account)
|
||||
mmdrop(ifq->mm_account);
|
||||
if (ifq->dev)
|
||||
@@ -603,6 +673,7 @@ static void io_zcrx_ifq_free(struct io_zcrx_ifq *ifq)
|
||||
io_free_rbuf_ring(ifq);
|
||||
free_uid(ifq->user);
|
||||
mutex_destroy(&ifq->pp_lock);
|
||||
kfree(ifq->areas);
|
||||
kfree(ifq);
|
||||
}
|
||||
|
||||
@@ -615,8 +686,9 @@ static void io_put_zcrx_ifq(struct io_zcrx_ifq *ifq)
|
||||
static void io_zcrx_return_niov_freelist(struct net_iov *niov)
|
||||
{
|
||||
struct io_zcrx_area *area = io_zcrx_iov_to_area(niov);
|
||||
struct io_zcrx_ifq *ifq = area->ifq;
|
||||
|
||||
guard(spinlock_bh)(&area->freelist_lock);
|
||||
guard(spinlock_bh)(&ifq->alloc_lock);
|
||||
if (WARN_ON_ONCE(area->free_count >= area->nia.num_niovs))
|
||||
return;
|
||||
area->freelist[area->free_count++] = net_iov_idx(niov);
|
||||
@@ -626,7 +698,7 @@ static struct net_iov *zcrx_get_free_niov(struct io_zcrx_area *area)
|
||||
{
|
||||
unsigned niov_idx;
|
||||
|
||||
lockdep_assert_held(&area->freelist_lock);
|
||||
lockdep_assert_held(&area->ifq->alloc_lock);
|
||||
|
||||
if (unlikely(!area->free_count))
|
||||
return NULL;
|
||||
@@ -647,14 +719,10 @@ static void io_zcrx_return_niov(struct net_iov *niov)
|
||||
page_pool_put_unrefed_netmem(niov->desc.pp, netmem, -1, false);
|
||||
}
|
||||
|
||||
static void io_zcrx_scrub(struct io_zcrx_ifq *ifq)
|
||||
static void io_zcrx_scrub_area(struct io_zcrx_ifq *ifq, struct io_zcrx_area *area)
|
||||
{
|
||||
struct io_zcrx_area *area = ifq->area;
|
||||
int i;
|
||||
|
||||
if (!area)
|
||||
return;
|
||||
|
||||
/* Reclaim back all buffers given to the user space. */
|
||||
for (i = 0; i < area->nia.num_niovs; i++) {
|
||||
struct net_iov *niov = &area->nia.niovs[i];
|
||||
@@ -668,6 +736,15 @@ static void io_zcrx_scrub(struct io_zcrx_ifq *ifq)
|
||||
}
|
||||
}
|
||||
|
||||
static void io_zcrx_scrub(struct io_zcrx_ifq *ifq)
|
||||
{
|
||||
int i;
|
||||
|
||||
guard(mutex)(&ifq->pp_lock);
|
||||
for (i = 0; i < ifq->nr_areas; i++)
|
||||
io_zcrx_scrub_area(ifq, ifq->areas[i]);
|
||||
}
|
||||
|
||||
static void zcrx_unregister_user(struct io_zcrx_ifq *ifq, struct io_ring_ctx *ctx)
|
||||
{
|
||||
scoped_guard(spinlock_bh, &ifq->ctx_lock) {
|
||||
@@ -927,6 +1004,8 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
|
||||
|
||||
if (copy_from_user(&area, u64_to_user_ptr(reg.area_ptr), sizeof(area)))
|
||||
return -EFAULT;
|
||||
if (area.rq_area_token)
|
||||
return -EINVAL;
|
||||
|
||||
memset(¬if, 0, sizeof(notif));
|
||||
if (reg.event_desc && copy_from_user(¬if, u64_to_user_ptr(reg.event_desc),
|
||||
@@ -989,6 +1068,8 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
|
||||
goto err;
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(!ifq->niov_shift);
|
||||
|
||||
reg.zcrx_id = id;
|
||||
|
||||
scoped_guard(mutex, &ctx->mmap_lock) {
|
||||
@@ -1081,12 +1162,22 @@ void io_unregister_zcrx(struct io_ring_ctx *ctx)
|
||||
xa_destroy(&ctx->zcrx_ctxs);
|
||||
}
|
||||
|
||||
struct zcrx_rq_iter {
|
||||
int rqes_left;
|
||||
bool flushed;
|
||||
};
|
||||
|
||||
static inline u32 __zcrx_rq_entries(struct zcrx_rq *rq)
|
||||
{
|
||||
u32 entries = rq->cached_tail - rq->cached_head;
|
||||
|
||||
return min(entries, rq->nr_entries);
|
||||
}
|
||||
|
||||
static inline u32 zcrx_rq_entries(struct zcrx_rq *rq)
|
||||
{
|
||||
u32 entries;
|
||||
|
||||
entries = smp_load_acquire(&rq->ring->tail) - rq->cached_head;
|
||||
return min(entries, rq->nr_entries);
|
||||
rq->cached_tail = smp_load_acquire(&rq->ring->tail);
|
||||
return __zcrx_rq_entries(rq);
|
||||
}
|
||||
|
||||
static struct io_uring_zcrx_rqe *zcrx_next_rqe(struct zcrx_rq *rq, unsigned mask)
|
||||
@@ -1096,6 +1187,33 @@ static struct io_uring_zcrx_rqe *zcrx_next_rqe(struct zcrx_rq *rq, unsigned mask
|
||||
return &rq->rqes[idx];
|
||||
}
|
||||
|
||||
static inline void zcrx_rq_iter_init(struct zcrx_rq_iter *it,
|
||||
struct zcrx_rq *rq)
|
||||
{
|
||||
it->rqes_left = min_t(unsigned, __zcrx_rq_entries(rq), ZCRX_REFILL_CAP);
|
||||
it->flushed = false;
|
||||
}
|
||||
|
||||
static inline bool zcrx_rq_iter_next(struct zcrx_rq_iter *it,
|
||||
struct zcrx_rq *rq,
|
||||
struct io_uring_zcrx_rqe **rqe)
|
||||
{
|
||||
it->rqes_left--;
|
||||
if (unlikely(it->rqes_left < 0)) {
|
||||
if (it->flushed)
|
||||
return false;
|
||||
rq->cached_tail = smp_load_acquire(&rq->ring->tail);
|
||||
it->rqes_left = min_t(unsigned, __zcrx_rq_entries(rq),
|
||||
ZCRX_REFILL_CAP);
|
||||
it->flushed = true;
|
||||
if (--it->rqes_left < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
*rqe = zcrx_next_rqe(rq, rq->nr_entries - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool io_parse_rqe(struct io_uring_zcrx_rqe *rqe,
|
||||
struct io_zcrx_ifq *ifq,
|
||||
struct net_iov **ret_niov)
|
||||
@@ -1104,12 +1222,15 @@ static inline bool io_parse_rqe(struct io_uring_zcrx_rqe *rqe,
|
||||
unsigned niov_idx, area_idx;
|
||||
struct io_zcrx_area *area;
|
||||
|
||||
lockdep_assert_held(&ifq->rq.lock);
|
||||
|
||||
area_idx = off >> IORING_ZCRX_AREA_SHIFT;
|
||||
niov_idx = (off & ~IORING_ZCRX_AREA_MASK) >> ifq->niov_shift;
|
||||
|
||||
if (unlikely(rqe->__pad || area_idx))
|
||||
if (unlikely(rqe->__pad || area_idx >= ifq->nr_areas))
|
||||
return false;
|
||||
area = ifq->area;
|
||||
area_idx = array_index_nospec(area_idx, ifq->nr_areas);
|
||||
area = ifq->areas[area_idx];
|
||||
|
||||
if (unlikely(niov_idx >= area->nia.num_niovs))
|
||||
return false;
|
||||
@@ -1119,44 +1240,58 @@ static inline bool io_parse_rqe(struct io_uring_zcrx_rqe *rqe,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool zcrx_put_refill_niov(struct net_iov *niov, struct page_pool *pp,
|
||||
unsigned refs)
|
||||
{
|
||||
netmem_ref netmem = net_iov_to_netmem(niov);
|
||||
|
||||
if (!io_zcrx_put_niov_uref(niov, refs))
|
||||
return false;
|
||||
if (page_pool_unref_netmem(netmem, refs) != 0)
|
||||
return false;
|
||||
if (unlikely(niov->desc.pp != pp)) {
|
||||
io_zcrx_return_niov(niov);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned io_zcrx_ring_refill(struct page_pool *pp,
|
||||
struct io_zcrx_ifq *ifq,
|
||||
netmem_ref *netmems, unsigned to_alloc)
|
||||
{
|
||||
struct zcrx_rq *rq = &ifq->rq;
|
||||
unsigned int mask = rq->nr_entries - 1;
|
||||
unsigned int entries;
|
||||
struct io_uring_zcrx_rqe *rqe;
|
||||
struct zcrx_rq_iter it;
|
||||
struct net_iov *niov = NULL;
|
||||
unsigned niov_refs = 0;
|
||||
unsigned allocated = 0;
|
||||
|
||||
guard(spinlock_bh)(&rq->lock);
|
||||
|
||||
entries = zcrx_rq_entries(rq);
|
||||
entries = min_t(unsigned, entries, to_alloc);
|
||||
if (unlikely(!entries))
|
||||
return 0;
|
||||
zcrx_rq_iter_init(&it, rq);
|
||||
|
||||
do {
|
||||
struct io_uring_zcrx_rqe *rqe = zcrx_next_rqe(rq, mask);
|
||||
struct net_iov *niov;
|
||||
netmem_ref netmem;
|
||||
while (allocated < to_alloc - 1 && zcrx_rq_iter_next(&it, rq, &rqe)) {
|
||||
struct net_iov *next_niov;
|
||||
|
||||
if (!io_parse_rqe(rqe, ifq, &niov))
|
||||
if (!io_parse_rqe(rqe, ifq, &next_niov))
|
||||
continue;
|
||||
if (!io_zcrx_put_niov_uref(niov))
|
||||
continue;
|
||||
|
||||
netmem = net_iov_to_netmem(niov);
|
||||
if (!page_pool_unref_and_test(netmem))
|
||||
continue;
|
||||
|
||||
if (unlikely(niov->desc.pp != pp)) {
|
||||
io_zcrx_return_niov(niov);
|
||||
if (niov == next_niov) {
|
||||
niov_refs++;
|
||||
continue;
|
||||
}
|
||||
|
||||
netmems[allocated] = netmem;
|
||||
if (niov && zcrx_put_refill_niov(niov, pp, niov_refs)) {
|
||||
netmems[allocated] = net_iov_to_netmem(niov);
|
||||
allocated++;
|
||||
} while (--entries);
|
||||
}
|
||||
niov = next_niov;
|
||||
niov_refs = 1;
|
||||
}
|
||||
|
||||
if (niov && zcrx_put_refill_niov(niov, pp, niov_refs)) {
|
||||
netmems[allocated] = net_iov_to_netmem(niov);
|
||||
allocated++;
|
||||
}
|
||||
|
||||
smp_store_release(&rq->ring->head, rq->cached_head);
|
||||
return allocated;
|
||||
@@ -1165,18 +1300,24 @@ static unsigned io_zcrx_ring_refill(struct page_pool *pp,
|
||||
static unsigned io_zcrx_refill_slow(struct page_pool *pp, struct io_zcrx_ifq *ifq,
|
||||
netmem_ref *netmems, unsigned to_alloc)
|
||||
{
|
||||
struct io_zcrx_area *area = ifq->area;
|
||||
unsigned area_idx = 0;
|
||||
unsigned allocated = 0;
|
||||
|
||||
guard(spinlock_bh)(&area->freelist_lock);
|
||||
guard(spinlock_bh)(&ifq->alloc_lock);
|
||||
|
||||
for (allocated = 0; allocated < to_alloc; allocated++) {
|
||||
struct net_iov *niov = zcrx_get_free_niov(area);
|
||||
while (allocated < to_alloc) {
|
||||
struct net_iov *niov = zcrx_get_free_niov(ifq->areas[area_idx]);
|
||||
|
||||
if (!niov)
|
||||
if (!niov) {
|
||||
area_idx++;
|
||||
if (area_idx >= ifq->nr_areas)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
net_mp_niov_set_page_pool(pp, niov);
|
||||
netmems[allocated] = net_iov_to_netmem(niov);
|
||||
allocated++;
|
||||
}
|
||||
return allocated;
|
||||
}
|
||||
@@ -1320,9 +1461,9 @@ static void io_pp_uninstall(void *mp_priv, struct netdev_rx_queue *rxq)
|
||||
struct pp_memory_provider_params *p = &rxq->mp_params;
|
||||
struct io_zcrx_ifq *ifq = mp_priv;
|
||||
|
||||
guard(mutex)(&ifq->pp_lock);
|
||||
io_zcrx_unmap_areas(ifq);
|
||||
io_zcrx_drop_netdev(ifq);
|
||||
if (ifq->area)
|
||||
io_zcrx_unmap_area(ifq, ifq->area);
|
||||
|
||||
p->mp_ops = NULL;
|
||||
p->mp_priv = NULL;
|
||||
@@ -1367,7 +1508,7 @@ static void zcrx_return_buffers(netmem_ref *netmems, unsigned nr)
|
||||
netmem_ref netmem = netmems[i];
|
||||
struct net_iov *niov = netmem_to_net_iov(netmem);
|
||||
|
||||
if (!io_zcrx_put_niov_uref(niov))
|
||||
if (!io_zcrx_put_niov_uref(niov, 1))
|
||||
continue;
|
||||
if (!page_pool_unref_and_test(netmem))
|
||||
continue;
|
||||
@@ -1423,6 +1564,34 @@ static int zcrx_arm_notif(struct io_ring_ctx *ctx, struct io_zcrx_ifq *zcrx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zcrx_ctrl_add_area(struct io_ring_ctx *ctx, struct io_zcrx_ifq *ifq,
|
||||
struct zcrx_ctrl *ctrl)
|
||||
{
|
||||
struct zcrx_ctrl_add_area *ctrl_add = &ctrl->zc_area;
|
||||
struct io_uring_zcrx_area_reg __user *area_uptr;
|
||||
struct io_uring_zcrx_area_reg area_reg;
|
||||
|
||||
area_uptr = u64_to_user_ptr(ctrl_add->area_ptr);
|
||||
if (copy_from_user(&area_reg, area_uptr, sizeof(area_reg)))
|
||||
return -EFAULT;
|
||||
if (!mem_is_zero(&ctrl_add->__resv, sizeof(ctrl_add->__resv)))
|
||||
return -EINVAL;
|
||||
if (area_reg.rq_area_token)
|
||||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&ifq->pp_lock);
|
||||
if (ifq->dev && !ifq->netdev)
|
||||
return -EFAULT;
|
||||
|
||||
/* we can't safely roll back area append, copy it out first */
|
||||
area_reg.rq_area_token = zcrx_area_id_to_token(zcrx_next_area_id(ifq));
|
||||
if (copy_to_user(area_uptr, &area_reg, sizeof(area_reg)))
|
||||
return -EFAULT;
|
||||
area_reg.rq_area_token = 0;
|
||||
|
||||
return __zcrx_create_area(ifq, &area_reg, 1U << ifq->niov_shift);
|
||||
}
|
||||
|
||||
int io_zcrx_ctrl(struct io_ring_ctx *ctx, void __user *arg, unsigned nr_args)
|
||||
{
|
||||
struct zcrx_ctrl ctrl;
|
||||
@@ -1449,6 +1618,8 @@ int io_zcrx_ctrl(struct io_ring_ctx *ctx, void __user *arg, unsigned nr_args)
|
||||
return zcrx_export(ctx, zcrx, &ctrl, arg);
|
||||
case ZCRX_CTRL_ARM_EVENT:
|
||||
return zcrx_arm_notif(ctx, zcrx, &ctrl);
|
||||
case ZCRX_CTRL_ADD_AREA:
|
||||
return zcrx_ctrl_add_area(ctx, zcrx, &ctrl);
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
@@ -1475,26 +1646,31 @@ static bool io_zcrx_queue_cqe(struct io_kiocb *req, struct net_iov *niov,
|
||||
area = io_zcrx_iov_to_area(niov);
|
||||
offset = off + (net_iov_idx(niov) << ifq->niov_shift);
|
||||
rcqe = (struct io_uring_zcrx_cqe *)(cqe + 1);
|
||||
rcqe->off = offset + ((u64)area->area_id << IORING_ZCRX_AREA_SHIFT);
|
||||
rcqe->off = offset + zcrx_area_id_to_token(area->area_id);
|
||||
rcqe->__pad = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct net_iov *io_alloc_fallback_niov(struct io_zcrx_ifq *ifq)
|
||||
{
|
||||
struct io_zcrx_area *area = ifq->area;
|
||||
struct net_iov *niov = NULL;
|
||||
unsigned area_idx;
|
||||
|
||||
if (!ifq->kern_readable)
|
||||
return NULL;
|
||||
|
||||
scoped_guard(spinlock_bh, &area->freelist_lock)
|
||||
niov = zcrx_get_free_niov(area);
|
||||
guard(spinlock_bh)(&ifq->alloc_lock);
|
||||
|
||||
if (niov)
|
||||
for (area_idx = 0; area_idx < ifq->nr_areas; area_idx++) {
|
||||
niov = zcrx_get_free_niov(ifq->areas[area_idx]);
|
||||
if (niov) {
|
||||
page_pool_fragment_netmem(net_iov_to_netmem(niov), 1);
|
||||
return niov;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct io_copy_cache {
|
||||
struct page *page;
|
||||
@@ -1635,8 +1811,8 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb,
|
||||
struct io_kiocb *req = args->req;
|
||||
struct sk_buff *frag_iter;
|
||||
unsigned start, start_off = offset;
|
||||
int i, copy, end, off;
|
||||
int ret = 0;
|
||||
struct skb_shared_info *shi;
|
||||
int i, ret = 0;
|
||||
|
||||
len = min_t(size_t, len, desc->count);
|
||||
/*
|
||||
@@ -1671,23 +1847,20 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb,
|
||||
}
|
||||
|
||||
start = skb_headlen(skb);
|
||||
shi = skb_shinfo(skb);
|
||||
|
||||
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
||||
const skb_frag_t *frag;
|
||||
for (i = 0; i < shi->nr_frags; i++) {
|
||||
const skb_frag_t *frag = &shi->frags[i];
|
||||
unsigned frag_end = start + skb_frag_size(frag);
|
||||
|
||||
if (WARN_ON(start > offset + len))
|
||||
return -EFAULT;
|
||||
|
||||
frag = &skb_shinfo(skb)->frags[i];
|
||||
end = start + skb_frag_size(frag);
|
||||
if (offset < frag_end) {
|
||||
unsigned copy = min(frag_end - offset, len);
|
||||
unsigned frag_off = offset - start;
|
||||
|
||||
if (offset < end) {
|
||||
copy = end - offset;
|
||||
if (copy > len)
|
||||
copy = len;
|
||||
|
||||
off = offset - start;
|
||||
ret = io_zcrx_recv_frag(req, ifq, frag, off, copy);
|
||||
ret = io_zcrx_recv_frag(req, ifq, frag, frag_off, copy);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
@@ -1696,24 +1869,23 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb,
|
||||
if (len == 0 || ret != copy)
|
||||
goto out;
|
||||
}
|
||||
start = end;
|
||||
start = frag_end;
|
||||
}
|
||||
|
||||
skb_walk_frags(skb, frag_iter) {
|
||||
unsigned frag_end;
|
||||
|
||||
if (WARN_ON(start > offset + len))
|
||||
return -EFAULT;
|
||||
|
||||
end = start + frag_iter->len;
|
||||
if (offset < end) {
|
||||
frag_end = start + frag_iter->len;
|
||||
if (offset < frag_end) {
|
||||
unsigned copy = min(frag_end - offset, len);
|
||||
unsigned frag_off = offset - start;
|
||||
size_t count;
|
||||
|
||||
copy = end - offset;
|
||||
if (copy > len)
|
||||
copy = len;
|
||||
|
||||
off = offset - start;
|
||||
count = desc->count;
|
||||
ret = io_zcrx_recv_skb(desc, frag_iter, off, copy);
|
||||
ret = io_zcrx_recv_skb(desc, frag_iter, frag_off, copy);
|
||||
desc->count = count;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@@ -1723,7 +1895,7 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb,
|
||||
if (len == 0 || ret != copy)
|
||||
goto out;
|
||||
}
|
||||
start = end;
|
||||
start = frag_end;
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
+12
-3
@@ -37,29 +37,38 @@ struct io_zcrx_area {
|
||||
u16 area_id;
|
||||
|
||||
/* freelist */
|
||||
spinlock_t freelist_lock ____cacheline_aligned_in_smp;
|
||||
u32 free_count;
|
||||
u32 *freelist;
|
||||
|
||||
struct io_zcrx_mem mem;
|
||||
};
|
||||
|
||||
struct zcrx_rq_hdr {
|
||||
u32 head ____cacheline_aligned_in_smp;
|
||||
u32 tail ____cacheline_aligned_in_smp;
|
||||
};
|
||||
|
||||
struct zcrx_rq {
|
||||
spinlock_t lock;
|
||||
struct io_uring *ring;
|
||||
struct zcrx_rq_hdr *ring;
|
||||
struct io_uring_zcrx_rqe *rqes;
|
||||
u32 cached_head;
|
||||
u32 cached_tail;
|
||||
u32 nr_entries;
|
||||
};
|
||||
|
||||
struct io_zcrx_ifq {
|
||||
struct io_zcrx_area *area;
|
||||
/* read-protected by any of: ->pp_lock, ->alloc_lock, ->rq.lock */
|
||||
struct io_zcrx_area **areas;
|
||||
unsigned nr_areas;
|
||||
|
||||
unsigned niov_shift;
|
||||
struct user_struct *user;
|
||||
struct mm_struct *mm_account;
|
||||
bool kern_readable;
|
||||
|
||||
struct zcrx_rq rq ____cacheline_aligned_in_smp;
|
||||
spinlock_t alloc_lock ____cacheline_aligned_in_smp;
|
||||
|
||||
u32 if_rxq;
|
||||
struct device *dev;
|
||||
|
||||
Reference in New Issue
Block a user