18d0c12a8SStefan Roesch // SPDX-License-Identifier: GPL-2.0
28d0c12a8SStefan Roesch
38d0c12a8SStefan Roesch #include "io_uring.h"
48d0c12a8SStefan Roesch #include "napi.h"
58d0c12a8SStefan Roesch
68d0c12a8SStefan Roesch #ifdef CONFIG_NET_RX_BUSY_POLL
78d0c12a8SStefan Roesch
88d0c12a8SStefan Roesch /* Timeout for cleanout of stale entries. */
98d0c12a8SStefan Roesch #define NAPI_TIMEOUT (60 * SEC_CONVERSION)
108d0c12a8SStefan Roesch
118d0c12a8SStefan Roesch struct io_napi_entry {
128d0c12a8SStefan Roesch unsigned int napi_id;
138d0c12a8SStefan Roesch struct list_head list;
148d0c12a8SStefan Roesch
158d0c12a8SStefan Roesch unsigned long timeout;
168d0c12a8SStefan Roesch struct hlist_node node;
178d0c12a8SStefan Roesch
188d0c12a8SStefan Roesch struct rcu_head rcu;
198d0c12a8SStefan Roesch };
208d0c12a8SStefan Roesch
io_napi_hash_find(struct hlist_head * hash_list,unsigned int napi_id)218d0c12a8SStefan Roesch static struct io_napi_entry *io_napi_hash_find(struct hlist_head *hash_list,
228d0c12a8SStefan Roesch unsigned int napi_id)
238d0c12a8SStefan Roesch {
248d0c12a8SStefan Roesch struct io_napi_entry *e;
258d0c12a8SStefan Roesch
268d0c12a8SStefan Roesch hlist_for_each_entry_rcu(e, hash_list, node) {
278d0c12a8SStefan Roesch if (e->napi_id != napi_id)
288d0c12a8SStefan Roesch continue;
298d0c12a8SStefan Roesch return e;
308d0c12a8SStefan Roesch }
318d0c12a8SStefan Roesch
328d0c12a8SStefan Roesch return NULL;
338d0c12a8SStefan Roesch }
348d0c12a8SStefan Roesch
net_to_ktime(unsigned long t)35342b2e39SPavel Begunkov static inline ktime_t net_to_ktime(unsigned long t)
36342b2e39SPavel Begunkov {
37342b2e39SPavel Begunkov /* napi approximating usecs, reverse busy_loop_current_time */
38342b2e39SPavel Begunkov return ns_to_ktime(t << 10);
39342b2e39SPavel Begunkov }
40342b2e39SPavel Begunkov
__io_napi_add_id(struct io_ring_ctx * ctx,unsigned int napi_id)41a5e26f49SOlivier Langlois int __io_napi_add_id(struct io_ring_ctx *ctx, unsigned int napi_id)
428d0c12a8SStefan Roesch {
438d0c12a8SStefan Roesch struct hlist_head *hash_list;
448d0c12a8SStefan Roesch struct io_napi_entry *e;
458d0c12a8SStefan Roesch
468d0c12a8SStefan Roesch /* Non-NAPI IDs can be rejected. */
47*b9d75210SStefano Jordhani if (!napi_id_valid(napi_id))
48a5e26f49SOlivier Langlois return -EINVAL;
498d0c12a8SStefan Roesch
508d0c12a8SStefan Roesch hash_list = &ctx->napi_ht[hash_min(napi_id, HASH_BITS(ctx->napi_ht))];
518d0c12a8SStefan Roesch
52db1e1adfSOlivier Langlois scoped_guard(rcu) {
538d0c12a8SStefan Roesch e = io_napi_hash_find(hash_list, napi_id);
548d0c12a8SStefan Roesch if (e) {
552f3cc8e4SOlivier Langlois WRITE_ONCE(e->timeout, jiffies + NAPI_TIMEOUT);
56a5e26f49SOlivier Langlois return -EEXIST;
578d0c12a8SStefan Roesch }
58db1e1adfSOlivier Langlois }
598d0c12a8SStefan Roesch
608d0c12a8SStefan Roesch e = kmalloc(sizeof(*e), GFP_NOWAIT);
618d0c12a8SStefan Roesch if (!e)
62a5e26f49SOlivier Langlois return -ENOMEM;
638d0c12a8SStefan Roesch
648d0c12a8SStefan Roesch e->napi_id = napi_id;
658d0c12a8SStefan Roesch e->timeout = jiffies + NAPI_TIMEOUT;
668d0c12a8SStefan Roesch
67db1e1adfSOlivier Langlois /*
68db1e1adfSOlivier Langlois * guard(spinlock) is not used to manually unlock it before calling
69db1e1adfSOlivier Langlois * kfree()
70db1e1adfSOlivier Langlois */
718d0c12a8SStefan Roesch spin_lock(&ctx->napi_lock);
728d0c12a8SStefan Roesch if (unlikely(io_napi_hash_find(hash_list, napi_id))) {
738d0c12a8SStefan Roesch spin_unlock(&ctx->napi_lock);
748d0c12a8SStefan Roesch kfree(e);
75a5e26f49SOlivier Langlois return -EEXIST;
768d0c12a8SStefan Roesch }
778d0c12a8SStefan Roesch
788d0c12a8SStefan Roesch hlist_add_tail_rcu(&e->node, hash_list);
7945b3941dSOlivier Langlois list_add_tail_rcu(&e->list, &ctx->napi_list);
808d0c12a8SStefan Roesch spin_unlock(&ctx->napi_lock);
81a5e26f49SOlivier Langlois return 0;
828d0c12a8SStefan Roesch }
838d0c12a8SStefan Roesch
__io_napi_del_id(struct io_ring_ctx * ctx,unsigned int napi_id)846bf90bd8SOlivier Langlois static int __io_napi_del_id(struct io_ring_ctx *ctx, unsigned int napi_id)
856bf90bd8SOlivier Langlois {
866bf90bd8SOlivier Langlois struct hlist_head *hash_list;
876bf90bd8SOlivier Langlois struct io_napi_entry *e;
886bf90bd8SOlivier Langlois
896bf90bd8SOlivier Langlois /* Non-NAPI IDs can be rejected. */
90*b9d75210SStefano Jordhani if (!napi_id_valid(napi_id))
916bf90bd8SOlivier Langlois return -EINVAL;
926bf90bd8SOlivier Langlois
936bf90bd8SOlivier Langlois hash_list = &ctx->napi_ht[hash_min(napi_id, HASH_BITS(ctx->napi_ht))];
946bf90bd8SOlivier Langlois guard(spinlock)(&ctx->napi_lock);
956bf90bd8SOlivier Langlois e = io_napi_hash_find(hash_list, napi_id);
966bf90bd8SOlivier Langlois if (!e)
976bf90bd8SOlivier Langlois return -ENOENT;
986bf90bd8SOlivier Langlois
996bf90bd8SOlivier Langlois list_del_rcu(&e->list);
1006bf90bd8SOlivier Langlois hash_del_rcu(&e->node);
1016bf90bd8SOlivier Langlois kfree_rcu(e, rcu);
1026bf90bd8SOlivier Langlois return 0;
1036bf90bd8SOlivier Langlois }
1046bf90bd8SOlivier Langlois
__io_napi_remove_stale(struct io_ring_ctx * ctx)1058d0c12a8SStefan Roesch static void __io_napi_remove_stale(struct io_ring_ctx *ctx)
1068d0c12a8SStefan Roesch {
1078d0c12a8SStefan Roesch struct io_napi_entry *e;
1088d0c12a8SStefan Roesch
109db1e1adfSOlivier Langlois guard(spinlock)(&ctx->napi_lock);
11045b3941dSOlivier Langlois /*
11145b3941dSOlivier Langlois * list_for_each_entry_safe() is not required as long as:
11245b3941dSOlivier Langlois * 1. list_del_rcu() does not reset the deleted node next pointer
11345b3941dSOlivier Langlois * 2. kfree_rcu() delays the memory freeing until the next quiescent
11445b3941dSOlivier Langlois * state
11545b3941dSOlivier Langlois */
11645b3941dSOlivier Langlois list_for_each_entry(e, &ctx->napi_list, list) {
1172f3cc8e4SOlivier Langlois if (time_after(jiffies, READ_ONCE(e->timeout))) {
11845b3941dSOlivier Langlois list_del_rcu(&e->list);
1198d0c12a8SStefan Roesch hash_del_rcu(&e->node);
1208d0c12a8SStefan Roesch kfree_rcu(e, rcu);
1218d0c12a8SStefan Roesch }
1228d0c12a8SStefan Roesch }
1238d0c12a8SStefan Roesch }
1248d0c12a8SStefan Roesch
io_napi_remove_stale(struct io_ring_ctx * ctx,bool is_stale)1258d0c12a8SStefan Roesch static inline void io_napi_remove_stale(struct io_ring_ctx *ctx, bool is_stale)
1268d0c12a8SStefan Roesch {
1278d0c12a8SStefan Roesch if (is_stale)
1288d0c12a8SStefan Roesch __io_napi_remove_stale(ctx);
1298d0c12a8SStefan Roesch }
1308d0c12a8SStefan Roesch
io_napi_busy_loop_timeout(ktime_t start_time,ktime_t bp)131342b2e39SPavel Begunkov static inline bool io_napi_busy_loop_timeout(ktime_t start_time,
132342b2e39SPavel Begunkov ktime_t bp)
1338d0c12a8SStefan Roesch {
134342b2e39SPavel Begunkov if (bp) {
135342b2e39SPavel Begunkov ktime_t end_time = ktime_add(start_time, bp);
136342b2e39SPavel Begunkov ktime_t now = net_to_ktime(busy_loop_current_time());
1378d0c12a8SStefan Roesch
138342b2e39SPavel Begunkov return ktime_after(now, end_time);
1398d0c12a8SStefan Roesch }
1408d0c12a8SStefan Roesch
1418d0c12a8SStefan Roesch return true;
1428d0c12a8SStefan Roesch }
1438d0c12a8SStefan Roesch
io_napi_busy_loop_should_end(void * data,unsigned long start_time)1448d0c12a8SStefan Roesch static bool io_napi_busy_loop_should_end(void *data,
1458d0c12a8SStefan Roesch unsigned long start_time)
1468d0c12a8SStefan Roesch {
1478d0c12a8SStefan Roesch struct io_wait_queue *iowq = data;
1488d0c12a8SStefan Roesch
1498d0c12a8SStefan Roesch if (signal_pending(current))
1508d0c12a8SStefan Roesch return true;
151428f1382SJens Axboe if (io_should_wake(iowq) || io_has_work(iowq->ctx))
1528d0c12a8SStefan Roesch return true;
153342b2e39SPavel Begunkov if (io_napi_busy_loop_timeout(net_to_ktime(start_time),
154342b2e39SPavel Begunkov iowq->napi_busy_poll_dt))
1558d0c12a8SStefan Roesch return true;
1568d0c12a8SStefan Roesch
1578d0c12a8SStefan Roesch return false;
1588d0c12a8SStefan Roesch }
1598d0c12a8SStefan Roesch
1606bf90bd8SOlivier Langlois /*
1616bf90bd8SOlivier Langlois * never report stale entries
1626bf90bd8SOlivier Langlois */
static_tracking_do_busy_loop(struct io_ring_ctx * ctx,bool (* loop_end)(void *,unsigned long),void * loop_end_arg)1636bf90bd8SOlivier Langlois static bool static_tracking_do_busy_loop(struct io_ring_ctx *ctx,
1646bf90bd8SOlivier Langlois bool (*loop_end)(void *, unsigned long),
1656bf90bd8SOlivier Langlois void *loop_end_arg)
1666bf90bd8SOlivier Langlois {
1676bf90bd8SOlivier Langlois struct io_napi_entry *e;
1686bf90bd8SOlivier Langlois
1696bf90bd8SOlivier Langlois list_for_each_entry_rcu(e, &ctx->napi_list, list)
1706bf90bd8SOlivier Langlois napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg,
1716bf90bd8SOlivier Langlois ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET);
1726bf90bd8SOlivier Langlois return false;
1736bf90bd8SOlivier Langlois }
1746bf90bd8SOlivier Langlois
1756bf90bd8SOlivier Langlois static bool
dynamic_tracking_do_busy_loop(struct io_ring_ctx * ctx,bool (* loop_end)(void *,unsigned long),void * loop_end_arg)1766bf90bd8SOlivier Langlois dynamic_tracking_do_busy_loop(struct io_ring_ctx *ctx,
17771afd926SOlivier Langlois bool (*loop_end)(void *, unsigned long),
1788d0c12a8SStefan Roesch void *loop_end_arg)
1798d0c12a8SStefan Roesch {
1808d0c12a8SStefan Roesch struct io_napi_entry *e;
1818d0c12a8SStefan Roesch bool is_stale = false;
1828d0c12a8SStefan Roesch
1838d0c12a8SStefan Roesch list_for_each_entry_rcu(e, &ctx->napi_list, list) {
1848d0c12a8SStefan Roesch napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg,
1858d0c12a8SStefan Roesch ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET);
1868d0c12a8SStefan Roesch
1872f3cc8e4SOlivier Langlois if (time_after(jiffies, READ_ONCE(e->timeout)))
1888d0c12a8SStefan Roesch is_stale = true;
1898d0c12a8SStefan Roesch }
1908d0c12a8SStefan Roesch
1918d0c12a8SStefan Roesch return is_stale;
1928d0c12a8SStefan Roesch }
1938d0c12a8SStefan Roesch
1946bf90bd8SOlivier Langlois static inline bool
__io_napi_do_busy_loop(struct io_ring_ctx * ctx,bool (* loop_end)(void *,unsigned long),void * loop_end_arg)1956bf90bd8SOlivier Langlois __io_napi_do_busy_loop(struct io_ring_ctx *ctx,
1966bf90bd8SOlivier Langlois bool (*loop_end)(void *, unsigned long),
1976bf90bd8SOlivier Langlois void *loop_end_arg)
1986bf90bd8SOlivier Langlois {
1996bf90bd8SOlivier Langlois if (READ_ONCE(ctx->napi_track_mode) == IO_URING_NAPI_TRACKING_STATIC)
2006bf90bd8SOlivier Langlois return static_tracking_do_busy_loop(ctx, loop_end, loop_end_arg);
2016bf90bd8SOlivier Langlois return dynamic_tracking_do_busy_loop(ctx, loop_end, loop_end_arg);
2026bf90bd8SOlivier Langlois }
2036bf90bd8SOlivier Langlois
io_napi_blocking_busy_loop(struct io_ring_ctx * ctx,struct io_wait_queue * iowq)2048d0c12a8SStefan Roesch static void io_napi_blocking_busy_loop(struct io_ring_ctx *ctx,
2058d0c12a8SStefan Roesch struct io_wait_queue *iowq)
2068d0c12a8SStefan Roesch {
2078d0c12a8SStefan Roesch unsigned long start_time = busy_loop_current_time();
20871afd926SOlivier Langlois bool (*loop_end)(void *, unsigned long) = NULL;
2098d0c12a8SStefan Roesch void *loop_end_arg = NULL;
2108d0c12a8SStefan Roesch bool is_stale = false;
2118d0c12a8SStefan Roesch
2128d0c12a8SStefan Roesch /* Singular lists use a different napi loop end check function and are
2138d0c12a8SStefan Roesch * only executed once.
2148d0c12a8SStefan Roesch */
21571afd926SOlivier Langlois if (list_is_singular(&ctx->napi_list)) {
21671afd926SOlivier Langlois loop_end = io_napi_busy_loop_should_end;
2178d0c12a8SStefan Roesch loop_end_arg = iowq;
21871afd926SOlivier Langlois }
2198d0c12a8SStefan Roesch
220db1e1adfSOlivier Langlois scoped_guard(rcu) {
2218d0c12a8SStefan Roesch do {
22271afd926SOlivier Langlois is_stale = __io_napi_do_busy_loop(ctx, loop_end,
22371afd926SOlivier Langlois loop_end_arg);
224db1e1adfSOlivier Langlois } while (!io_napi_busy_loop_should_end(iowq, start_time) &&
225db1e1adfSOlivier Langlois !loop_end_arg);
226db1e1adfSOlivier Langlois }
2278d0c12a8SStefan Roesch
2288d0c12a8SStefan Roesch io_napi_remove_stale(ctx, is_stale);
2298d0c12a8SStefan Roesch }
2308d0c12a8SStefan Roesch
2318d0c12a8SStefan Roesch /*
2328d0c12a8SStefan Roesch * io_napi_init() - Init napi settings
2338d0c12a8SStefan Roesch * @ctx: pointer to io-uring context structure
2348d0c12a8SStefan Roesch *
2358d0c12a8SStefan Roesch * Init napi settings in the io-uring context.
2368d0c12a8SStefan Roesch */
io_napi_init(struct io_ring_ctx * ctx)2378d0c12a8SStefan Roesch void io_napi_init(struct io_ring_ctx *ctx)
2388d0c12a8SStefan Roesch {
239342b2e39SPavel Begunkov u64 sys_dt = READ_ONCE(sysctl_net_busy_poll) * NSEC_PER_USEC;
240342b2e39SPavel Begunkov
2418d0c12a8SStefan Roesch INIT_LIST_HEAD(&ctx->napi_list);
2428d0c12a8SStefan Roesch spin_lock_init(&ctx->napi_lock);
2438d0c12a8SStefan Roesch ctx->napi_prefer_busy_poll = false;
244342b2e39SPavel Begunkov ctx->napi_busy_poll_dt = ns_to_ktime(sys_dt);
2456bf90bd8SOlivier Langlois ctx->napi_track_mode = IO_URING_NAPI_TRACKING_INACTIVE;
2468d0c12a8SStefan Roesch }
2478d0c12a8SStefan Roesch
2488d0c12a8SStefan Roesch /*
2498d0c12a8SStefan Roesch * io_napi_free() - Deallocate napi
2508d0c12a8SStefan Roesch * @ctx: pointer to io-uring context structure
2518d0c12a8SStefan Roesch *
2528d0c12a8SStefan Roesch * Free the napi list and the hash table in the io-uring context.
2538d0c12a8SStefan Roesch */
io_napi_free(struct io_ring_ctx * ctx)2548d0c12a8SStefan Roesch void io_napi_free(struct io_ring_ctx *ctx)
2558d0c12a8SStefan Roesch {
2568d0c12a8SStefan Roesch struct io_napi_entry *e;
2578d0c12a8SStefan Roesch
258db1e1adfSOlivier Langlois guard(spinlock)(&ctx->napi_lock);
25945b3941dSOlivier Langlois list_for_each_entry(e, &ctx->napi_list, list) {
2608d0c12a8SStefan Roesch hash_del_rcu(&e->node);
2618d0c12a8SStefan Roesch kfree_rcu(e, rcu);
2628d0c12a8SStefan Roesch }
26345b3941dSOlivier Langlois INIT_LIST_HEAD_RCU(&ctx->napi_list);
2648d0c12a8SStefan Roesch }
2658d0c12a8SStefan Roesch
io_napi_register_napi(struct io_ring_ctx * ctx,struct io_uring_napi * napi)2666bf90bd8SOlivier Langlois static int io_napi_register_napi(struct io_ring_ctx *ctx,
2676bf90bd8SOlivier Langlois struct io_uring_napi *napi)
2686bf90bd8SOlivier Langlois {
2696bf90bd8SOlivier Langlois switch (napi->op_param) {
2706bf90bd8SOlivier Langlois case IO_URING_NAPI_TRACKING_DYNAMIC:
2716bf90bd8SOlivier Langlois case IO_URING_NAPI_TRACKING_STATIC:
2726bf90bd8SOlivier Langlois break;
2736bf90bd8SOlivier Langlois default:
2746bf90bd8SOlivier Langlois return -EINVAL;
2756bf90bd8SOlivier Langlois }
2766bf90bd8SOlivier Langlois /* clean the napi list for new settings */
2776bf90bd8SOlivier Langlois io_napi_free(ctx);
2786bf90bd8SOlivier Langlois WRITE_ONCE(ctx->napi_track_mode, napi->op_param);
2796bf90bd8SOlivier Langlois WRITE_ONCE(ctx->napi_busy_poll_dt, napi->busy_poll_to * NSEC_PER_USEC);
2806bf90bd8SOlivier Langlois WRITE_ONCE(ctx->napi_prefer_busy_poll, !!napi->prefer_busy_poll);
2816bf90bd8SOlivier Langlois return 0;
2826bf90bd8SOlivier Langlois }
2836bf90bd8SOlivier Langlois
2848d0c12a8SStefan Roesch /*
285ef1186c1SStefan Roesch * io_napi_register() - Register napi with io-uring
286ef1186c1SStefan Roesch * @ctx: pointer to io-uring context structure
287ef1186c1SStefan Roesch * @arg: pointer to io_uring_napi structure
288ef1186c1SStefan Roesch *
289ef1186c1SStefan Roesch * Register napi in the io-uring context.
290ef1186c1SStefan Roesch */
io_register_napi(struct io_ring_ctx * ctx,void __user * arg)291ef1186c1SStefan Roesch int io_register_napi(struct io_ring_ctx *ctx, void __user *arg)
292ef1186c1SStefan Roesch {
293ef1186c1SStefan Roesch const struct io_uring_napi curr = {
294342b2e39SPavel Begunkov .busy_poll_to = ktime_to_us(ctx->napi_busy_poll_dt),
2956bf90bd8SOlivier Langlois .prefer_busy_poll = ctx->napi_prefer_busy_poll,
2966bf90bd8SOlivier Langlois .op_param = ctx->napi_track_mode
297ef1186c1SStefan Roesch };
298ef1186c1SStefan Roesch struct io_uring_napi napi;
299ef1186c1SStefan Roesch
300bd44d7e9SPavel Begunkov if (ctx->flags & IORING_SETUP_IOPOLL)
301bd44d7e9SPavel Begunkov return -EINVAL;
302ef1186c1SStefan Roesch if (copy_from_user(&napi, arg, sizeof(napi)))
303ef1186c1SStefan Roesch return -EFAULT;
3046bf90bd8SOlivier Langlois if (napi.pad[0] || napi.pad[1] || napi.resv)
305ef1186c1SStefan Roesch return -EINVAL;
306ef1186c1SStefan Roesch
307ef1186c1SStefan Roesch if (copy_to_user(arg, &curr, sizeof(curr)))
308ef1186c1SStefan Roesch return -EFAULT;
309ef1186c1SStefan Roesch
3106bf90bd8SOlivier Langlois switch (napi.opcode) {
3116bf90bd8SOlivier Langlois case IO_URING_NAPI_REGISTER_OP:
3126bf90bd8SOlivier Langlois return io_napi_register_napi(ctx, &napi);
3136bf90bd8SOlivier Langlois case IO_URING_NAPI_STATIC_ADD_ID:
3146bf90bd8SOlivier Langlois if (curr.op_param != IO_URING_NAPI_TRACKING_STATIC)
3156bf90bd8SOlivier Langlois return -EINVAL;
3166bf90bd8SOlivier Langlois return __io_napi_add_id(ctx, napi.op_param);
3176bf90bd8SOlivier Langlois case IO_URING_NAPI_STATIC_DEL_ID:
3186bf90bd8SOlivier Langlois if (curr.op_param != IO_URING_NAPI_TRACKING_STATIC)
3196bf90bd8SOlivier Langlois return -EINVAL;
3206bf90bd8SOlivier Langlois return __io_napi_del_id(ctx, napi.op_param);
3216bf90bd8SOlivier Langlois default:
3226bf90bd8SOlivier Langlois return -EINVAL;
3236bf90bd8SOlivier Langlois }
324ef1186c1SStefan Roesch }
325ef1186c1SStefan Roesch
326ef1186c1SStefan Roesch /*
327ef1186c1SStefan Roesch * io_napi_unregister() - Unregister napi with io-uring
328ef1186c1SStefan Roesch * @ctx: pointer to io-uring context structure
329ef1186c1SStefan Roesch * @arg: pointer to io_uring_napi structure
330ef1186c1SStefan Roesch *
331ef1186c1SStefan Roesch * Unregister napi. If arg has been specified copy the busy poll timeout and
332ef1186c1SStefan Roesch * prefer busy poll setting to the passed in structure.
333ef1186c1SStefan Roesch */
io_unregister_napi(struct io_ring_ctx * ctx,void __user * arg)334ef1186c1SStefan Roesch int io_unregister_napi(struct io_ring_ctx *ctx, void __user *arg)
335ef1186c1SStefan Roesch {
336ef1186c1SStefan Roesch const struct io_uring_napi curr = {
337342b2e39SPavel Begunkov .busy_poll_to = ktime_to_us(ctx->napi_busy_poll_dt),
338ef1186c1SStefan Roesch .prefer_busy_poll = ctx->napi_prefer_busy_poll
339ef1186c1SStefan Roesch };
340ef1186c1SStefan Roesch
341ef1186c1SStefan Roesch if (arg && copy_to_user(arg, &curr, sizeof(curr)))
342ef1186c1SStefan Roesch return -EFAULT;
343ef1186c1SStefan Roesch
344342b2e39SPavel Begunkov WRITE_ONCE(ctx->napi_busy_poll_dt, 0);
345ef1186c1SStefan Roesch WRITE_ONCE(ctx->napi_prefer_busy_poll, false);
3466bf90bd8SOlivier Langlois WRITE_ONCE(ctx->napi_track_mode, IO_URING_NAPI_TRACKING_INACTIVE);
347ef1186c1SStefan Roesch return 0;
348ef1186c1SStefan Roesch }
349ef1186c1SStefan Roesch
350ef1186c1SStefan Roesch /*
3518d0c12a8SStefan Roesch * __io_napi_busy_loop() - execute busy poll loop
3528d0c12a8SStefan Roesch * @ctx: pointer to io-uring context structure
3538d0c12a8SStefan Roesch * @iowq: pointer to io wait queue
3548d0c12a8SStefan Roesch *
3558d0c12a8SStefan Roesch * Execute the busy poll loop and merge the spliced off list.
3568d0c12a8SStefan Roesch */
__io_napi_busy_loop(struct io_ring_ctx * ctx,struct io_wait_queue * iowq)3578d0c12a8SStefan Roesch void __io_napi_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq)
3588d0c12a8SStefan Roesch {
359489b8006SPavel Begunkov if (ctx->flags & IORING_SETUP_SQPOLL)
360489b8006SPavel Begunkov return;
3618d0c12a8SStefan Roesch
362d5cce407SPavel Begunkov iowq->napi_busy_poll_dt = READ_ONCE(ctx->napi_busy_poll_dt);
363d5cce407SPavel Begunkov if (iowq->timeout != KTIME_MAX) {
3642b8e976bSPavel Begunkov ktime_t dt = ktime_sub(iowq->timeout, io_get_time(ctx));
365d5cce407SPavel Begunkov
366d5cce407SPavel Begunkov iowq->napi_busy_poll_dt = min_t(u64, iowq->napi_busy_poll_dt, dt);
367d5cce407SPavel Begunkov }
368d5cce407SPavel Begunkov
369489b8006SPavel Begunkov iowq->napi_prefer_busy_poll = READ_ONCE(ctx->napi_prefer_busy_poll);
3708d0c12a8SStefan Roesch io_napi_blocking_busy_loop(ctx, iowq);
3718d0c12a8SStefan Roesch }
3728d0c12a8SStefan Roesch
373ff183d42SStefan Roesch /*
374ff183d42SStefan Roesch * io_napi_sqpoll_busy_poll() - busy poll loop for sqpoll
375ff183d42SStefan Roesch * @ctx: pointer to io-uring context structure
376ff183d42SStefan Roesch *
377ff183d42SStefan Roesch * Splice of the napi list and execute the napi busy poll loop.
378ff183d42SStefan Roesch */
io_napi_sqpoll_busy_poll(struct io_ring_ctx * ctx)379ff183d42SStefan Roesch int io_napi_sqpoll_busy_poll(struct io_ring_ctx *ctx)
380ff183d42SStefan Roesch {
381ff183d42SStefan Roesch bool is_stale = false;
382ff183d42SStefan Roesch
383342b2e39SPavel Begunkov if (!READ_ONCE(ctx->napi_busy_poll_dt))
384ff183d42SStefan Roesch return 0;
385ff183d42SStefan Roesch if (list_empty_careful(&ctx->napi_list))
386ff183d42SStefan Roesch return 0;
387ff183d42SStefan Roesch
388db1e1adfSOlivier Langlois scoped_guard(rcu) {
38971afd926SOlivier Langlois is_stale = __io_napi_do_busy_loop(ctx, NULL, NULL);
390db1e1adfSOlivier Langlois }
391ff183d42SStefan Roesch
392ff183d42SStefan Roesch io_napi_remove_stale(ctx, is_stale);
393ff183d42SStefan Roesch return 1;
394ff183d42SStefan Roesch }
395ff183d42SStefan Roesch
3968d0c12a8SStefan Roesch #endif
397