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 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 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 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. */ 478d0c12a8SStefan Roesch if (napi_id < MIN_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 84*6bf90bd8SOlivier Langlois static int __io_napi_del_id(struct io_ring_ctx *ctx, unsigned int napi_id) 85*6bf90bd8SOlivier Langlois { 86*6bf90bd8SOlivier Langlois struct hlist_head *hash_list; 87*6bf90bd8SOlivier Langlois struct io_napi_entry *e; 88*6bf90bd8SOlivier Langlois 89*6bf90bd8SOlivier Langlois /* Non-NAPI IDs can be rejected. */ 90*6bf90bd8SOlivier Langlois if (napi_id < MIN_NAPI_ID) 91*6bf90bd8SOlivier Langlois return -EINVAL; 92*6bf90bd8SOlivier Langlois 93*6bf90bd8SOlivier Langlois hash_list = &ctx->napi_ht[hash_min(napi_id, HASH_BITS(ctx->napi_ht))]; 94*6bf90bd8SOlivier Langlois guard(spinlock)(&ctx->napi_lock); 95*6bf90bd8SOlivier Langlois e = io_napi_hash_find(hash_list, napi_id); 96*6bf90bd8SOlivier Langlois if (!e) 97*6bf90bd8SOlivier Langlois return -ENOENT; 98*6bf90bd8SOlivier Langlois 99*6bf90bd8SOlivier Langlois list_del_rcu(&e->list); 100*6bf90bd8SOlivier Langlois hash_del_rcu(&e->node); 101*6bf90bd8SOlivier Langlois kfree_rcu(e, rcu); 102*6bf90bd8SOlivier Langlois return 0; 103*6bf90bd8SOlivier Langlois } 104*6bf90bd8SOlivier Langlois 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 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 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 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 160*6bf90bd8SOlivier Langlois /* 161*6bf90bd8SOlivier Langlois * never report stale entries 162*6bf90bd8SOlivier Langlois */ 163*6bf90bd8SOlivier Langlois static bool static_tracking_do_busy_loop(struct io_ring_ctx *ctx, 164*6bf90bd8SOlivier Langlois bool (*loop_end)(void *, unsigned long), 165*6bf90bd8SOlivier Langlois void *loop_end_arg) 166*6bf90bd8SOlivier Langlois { 167*6bf90bd8SOlivier Langlois struct io_napi_entry *e; 168*6bf90bd8SOlivier Langlois 169*6bf90bd8SOlivier Langlois list_for_each_entry_rcu(e, &ctx->napi_list, list) 170*6bf90bd8SOlivier Langlois napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg, 171*6bf90bd8SOlivier Langlois ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET); 172*6bf90bd8SOlivier Langlois return false; 173*6bf90bd8SOlivier Langlois } 174*6bf90bd8SOlivier Langlois 175*6bf90bd8SOlivier Langlois static bool 176*6bf90bd8SOlivier 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 194*6bf90bd8SOlivier Langlois static inline bool 195*6bf90bd8SOlivier Langlois __io_napi_do_busy_loop(struct io_ring_ctx *ctx, 196*6bf90bd8SOlivier Langlois bool (*loop_end)(void *, unsigned long), 197*6bf90bd8SOlivier Langlois void *loop_end_arg) 198*6bf90bd8SOlivier Langlois { 199*6bf90bd8SOlivier Langlois if (READ_ONCE(ctx->napi_track_mode) == IO_URING_NAPI_TRACKING_STATIC) 200*6bf90bd8SOlivier Langlois return static_tracking_do_busy_loop(ctx, loop_end, loop_end_arg); 201*6bf90bd8SOlivier Langlois return dynamic_tracking_do_busy_loop(ctx, loop_end, loop_end_arg); 202*6bf90bd8SOlivier Langlois } 203*6bf90bd8SOlivier Langlois 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 */ 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); 245*6bf90bd8SOlivier 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 */ 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 266*6bf90bd8SOlivier Langlois static int io_napi_register_napi(struct io_ring_ctx *ctx, 267*6bf90bd8SOlivier Langlois struct io_uring_napi *napi) 268*6bf90bd8SOlivier Langlois { 269*6bf90bd8SOlivier Langlois switch (napi->op_param) { 270*6bf90bd8SOlivier Langlois case IO_URING_NAPI_TRACKING_DYNAMIC: 271*6bf90bd8SOlivier Langlois case IO_URING_NAPI_TRACKING_STATIC: 272*6bf90bd8SOlivier Langlois break; 273*6bf90bd8SOlivier Langlois default: 274*6bf90bd8SOlivier Langlois return -EINVAL; 275*6bf90bd8SOlivier Langlois } 276*6bf90bd8SOlivier Langlois /* clean the napi list for new settings */ 277*6bf90bd8SOlivier Langlois io_napi_free(ctx); 278*6bf90bd8SOlivier Langlois WRITE_ONCE(ctx->napi_track_mode, napi->op_param); 279*6bf90bd8SOlivier Langlois WRITE_ONCE(ctx->napi_busy_poll_dt, napi->busy_poll_to * NSEC_PER_USEC); 280*6bf90bd8SOlivier Langlois WRITE_ONCE(ctx->napi_prefer_busy_poll, !!napi->prefer_busy_poll); 281*6bf90bd8SOlivier Langlois return 0; 282*6bf90bd8SOlivier Langlois } 283*6bf90bd8SOlivier 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 */ 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), 295*6bf90bd8SOlivier Langlois .prefer_busy_poll = ctx->napi_prefer_busy_poll, 296*6bf90bd8SOlivier 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; 304*6bf90bd8SOlivier 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 310*6bf90bd8SOlivier Langlois switch (napi.opcode) { 311*6bf90bd8SOlivier Langlois case IO_URING_NAPI_REGISTER_OP: 312*6bf90bd8SOlivier Langlois return io_napi_register_napi(ctx, &napi); 313*6bf90bd8SOlivier Langlois case IO_URING_NAPI_STATIC_ADD_ID: 314*6bf90bd8SOlivier Langlois if (curr.op_param != IO_URING_NAPI_TRACKING_STATIC) 315*6bf90bd8SOlivier Langlois return -EINVAL; 316*6bf90bd8SOlivier Langlois return __io_napi_add_id(ctx, napi.op_param); 317*6bf90bd8SOlivier Langlois case IO_URING_NAPI_STATIC_DEL_ID: 318*6bf90bd8SOlivier Langlois if (curr.op_param != IO_URING_NAPI_TRACKING_STATIC) 319*6bf90bd8SOlivier Langlois return -EINVAL; 320*6bf90bd8SOlivier Langlois return __io_napi_del_id(ctx, napi.op_param); 321*6bf90bd8SOlivier Langlois default: 322*6bf90bd8SOlivier Langlois return -EINVAL; 323*6bf90bd8SOlivier 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 */ 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); 346*6bf90bd8SOlivier 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 */ 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 */ 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