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 418d0c12a8SStefan Roesch void __io_napi_add(struct io_ring_ctx *ctx, struct socket *sock) 428d0c12a8SStefan Roesch { 438d0c12a8SStefan Roesch struct hlist_head *hash_list; 448d0c12a8SStefan Roesch unsigned int napi_id; 458d0c12a8SStefan Roesch struct sock *sk; 468d0c12a8SStefan Roesch struct io_napi_entry *e; 478d0c12a8SStefan Roesch 488d0c12a8SStefan Roesch sk = sock->sk; 498d0c12a8SStefan Roesch if (!sk) 508d0c12a8SStefan Roesch return; 518d0c12a8SStefan Roesch 528d0c12a8SStefan Roesch napi_id = READ_ONCE(sk->sk_napi_id); 538d0c12a8SStefan Roesch 548d0c12a8SStefan Roesch /* Non-NAPI IDs can be rejected. */ 558d0c12a8SStefan Roesch if (napi_id < MIN_NAPI_ID) 568d0c12a8SStefan Roesch return; 578d0c12a8SStefan Roesch 588d0c12a8SStefan Roesch hash_list = &ctx->napi_ht[hash_min(napi_id, HASH_BITS(ctx->napi_ht))]; 598d0c12a8SStefan Roesch 608d0c12a8SStefan Roesch rcu_read_lock(); 618d0c12a8SStefan Roesch e = io_napi_hash_find(hash_list, napi_id); 628d0c12a8SStefan Roesch if (e) { 632f3cc8e4SOlivier Langlois WRITE_ONCE(e->timeout, jiffies + NAPI_TIMEOUT); 648d0c12a8SStefan Roesch rcu_read_unlock(); 658d0c12a8SStefan Roesch return; 668d0c12a8SStefan Roesch } 678d0c12a8SStefan Roesch rcu_read_unlock(); 688d0c12a8SStefan Roesch 698d0c12a8SStefan Roesch e = kmalloc(sizeof(*e), GFP_NOWAIT); 708d0c12a8SStefan Roesch if (!e) 718d0c12a8SStefan Roesch return; 728d0c12a8SStefan Roesch 738d0c12a8SStefan Roesch e->napi_id = napi_id; 748d0c12a8SStefan Roesch e->timeout = jiffies + NAPI_TIMEOUT; 758d0c12a8SStefan Roesch 768d0c12a8SStefan Roesch spin_lock(&ctx->napi_lock); 778d0c12a8SStefan Roesch if (unlikely(io_napi_hash_find(hash_list, napi_id))) { 788d0c12a8SStefan Roesch spin_unlock(&ctx->napi_lock); 798d0c12a8SStefan Roesch kfree(e); 808d0c12a8SStefan Roesch return; 818d0c12a8SStefan Roesch } 828d0c12a8SStefan Roesch 838d0c12a8SStefan Roesch hlist_add_tail_rcu(&e->node, hash_list); 84*45b3941dSOlivier Langlois list_add_tail_rcu(&e->list, &ctx->napi_list); 858d0c12a8SStefan Roesch spin_unlock(&ctx->napi_lock); 868d0c12a8SStefan Roesch } 878d0c12a8SStefan Roesch 888d0c12a8SStefan Roesch static void __io_napi_remove_stale(struct io_ring_ctx *ctx) 898d0c12a8SStefan Roesch { 908d0c12a8SStefan Roesch struct io_napi_entry *e; 918d0c12a8SStefan Roesch 928d0c12a8SStefan Roesch spin_lock(&ctx->napi_lock); 93*45b3941dSOlivier Langlois /* 94*45b3941dSOlivier Langlois * list_for_each_entry_safe() is not required as long as: 95*45b3941dSOlivier Langlois * 1. list_del_rcu() does not reset the deleted node next pointer 96*45b3941dSOlivier Langlois * 2. kfree_rcu() delays the memory freeing until the next quiescent 97*45b3941dSOlivier Langlois * state 98*45b3941dSOlivier Langlois */ 99*45b3941dSOlivier Langlois list_for_each_entry(e, &ctx->napi_list, list) { 1002f3cc8e4SOlivier Langlois if (time_after(jiffies, READ_ONCE(e->timeout))) { 101*45b3941dSOlivier Langlois list_del_rcu(&e->list); 1028d0c12a8SStefan Roesch hash_del_rcu(&e->node); 1038d0c12a8SStefan Roesch kfree_rcu(e, rcu); 1048d0c12a8SStefan Roesch } 1058d0c12a8SStefan Roesch } 1068d0c12a8SStefan Roesch spin_unlock(&ctx->napi_lock); 1078d0c12a8SStefan Roesch } 1088d0c12a8SStefan Roesch 1098d0c12a8SStefan Roesch static inline void io_napi_remove_stale(struct io_ring_ctx *ctx, bool is_stale) 1108d0c12a8SStefan Roesch { 1118d0c12a8SStefan Roesch if (is_stale) 1128d0c12a8SStefan Roesch __io_napi_remove_stale(ctx); 1138d0c12a8SStefan Roesch } 1148d0c12a8SStefan Roesch 115342b2e39SPavel Begunkov static inline bool io_napi_busy_loop_timeout(ktime_t start_time, 116342b2e39SPavel Begunkov ktime_t bp) 1178d0c12a8SStefan Roesch { 118342b2e39SPavel Begunkov if (bp) { 119342b2e39SPavel Begunkov ktime_t end_time = ktime_add(start_time, bp); 120342b2e39SPavel Begunkov ktime_t now = net_to_ktime(busy_loop_current_time()); 1218d0c12a8SStefan Roesch 122342b2e39SPavel Begunkov return ktime_after(now, end_time); 1238d0c12a8SStefan Roesch } 1248d0c12a8SStefan Roesch 1258d0c12a8SStefan Roesch return true; 1268d0c12a8SStefan Roesch } 1278d0c12a8SStefan Roesch 1288d0c12a8SStefan Roesch static bool io_napi_busy_loop_should_end(void *data, 1298d0c12a8SStefan Roesch unsigned long start_time) 1308d0c12a8SStefan Roesch { 1318d0c12a8SStefan Roesch struct io_wait_queue *iowq = data; 1328d0c12a8SStefan Roesch 1338d0c12a8SStefan Roesch if (signal_pending(current)) 1348d0c12a8SStefan Roesch return true; 135428f1382SJens Axboe if (io_should_wake(iowq) || io_has_work(iowq->ctx)) 1368d0c12a8SStefan Roesch return true; 137342b2e39SPavel Begunkov if (io_napi_busy_loop_timeout(net_to_ktime(start_time), 138342b2e39SPavel Begunkov iowq->napi_busy_poll_dt)) 1398d0c12a8SStefan Roesch return true; 1408d0c12a8SStefan Roesch 1418d0c12a8SStefan Roesch return false; 1428d0c12a8SStefan Roesch } 1438d0c12a8SStefan Roesch 1448d0c12a8SStefan Roesch static bool __io_napi_do_busy_loop(struct io_ring_ctx *ctx, 1458d0c12a8SStefan Roesch void *loop_end_arg) 1468d0c12a8SStefan Roesch { 1478d0c12a8SStefan Roesch struct io_napi_entry *e; 1488d0c12a8SStefan Roesch bool (*loop_end)(void *, unsigned long) = NULL; 1498d0c12a8SStefan Roesch bool is_stale = false; 1508d0c12a8SStefan Roesch 1518d0c12a8SStefan Roesch if (loop_end_arg) 1528d0c12a8SStefan Roesch loop_end = io_napi_busy_loop_should_end; 1538d0c12a8SStefan Roesch 1548d0c12a8SStefan Roesch list_for_each_entry_rcu(e, &ctx->napi_list, list) { 1558d0c12a8SStefan Roesch napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg, 1568d0c12a8SStefan Roesch ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET); 1578d0c12a8SStefan Roesch 1582f3cc8e4SOlivier Langlois if (time_after(jiffies, READ_ONCE(e->timeout))) 1598d0c12a8SStefan Roesch is_stale = true; 1608d0c12a8SStefan Roesch } 1618d0c12a8SStefan Roesch 1628d0c12a8SStefan Roesch return is_stale; 1638d0c12a8SStefan Roesch } 1648d0c12a8SStefan Roesch 1658d0c12a8SStefan Roesch static void io_napi_blocking_busy_loop(struct io_ring_ctx *ctx, 1668d0c12a8SStefan Roesch struct io_wait_queue *iowq) 1678d0c12a8SStefan Roesch { 1688d0c12a8SStefan Roesch unsigned long start_time = busy_loop_current_time(); 1698d0c12a8SStefan Roesch void *loop_end_arg = NULL; 1708d0c12a8SStefan Roesch bool is_stale = false; 1718d0c12a8SStefan Roesch 1728d0c12a8SStefan Roesch /* Singular lists use a different napi loop end check function and are 1738d0c12a8SStefan Roesch * only executed once. 1748d0c12a8SStefan Roesch */ 1758d0c12a8SStefan Roesch if (list_is_singular(&ctx->napi_list)) 1768d0c12a8SStefan Roesch loop_end_arg = iowq; 1778d0c12a8SStefan Roesch 1788d0c12a8SStefan Roesch rcu_read_lock(); 1798d0c12a8SStefan Roesch do { 1808d0c12a8SStefan Roesch is_stale = __io_napi_do_busy_loop(ctx, loop_end_arg); 1818d0c12a8SStefan Roesch } while (!io_napi_busy_loop_should_end(iowq, start_time) && !loop_end_arg); 1828d0c12a8SStefan Roesch rcu_read_unlock(); 1838d0c12a8SStefan Roesch 1848d0c12a8SStefan Roesch io_napi_remove_stale(ctx, is_stale); 1858d0c12a8SStefan Roesch } 1868d0c12a8SStefan Roesch 1878d0c12a8SStefan Roesch /* 1888d0c12a8SStefan Roesch * io_napi_init() - Init napi settings 1898d0c12a8SStefan Roesch * @ctx: pointer to io-uring context structure 1908d0c12a8SStefan Roesch * 1918d0c12a8SStefan Roesch * Init napi settings in the io-uring context. 1928d0c12a8SStefan Roesch */ 1938d0c12a8SStefan Roesch void io_napi_init(struct io_ring_ctx *ctx) 1948d0c12a8SStefan Roesch { 195342b2e39SPavel Begunkov u64 sys_dt = READ_ONCE(sysctl_net_busy_poll) * NSEC_PER_USEC; 196342b2e39SPavel Begunkov 1978d0c12a8SStefan Roesch INIT_LIST_HEAD(&ctx->napi_list); 1988d0c12a8SStefan Roesch spin_lock_init(&ctx->napi_lock); 1998d0c12a8SStefan Roesch ctx->napi_prefer_busy_poll = false; 200342b2e39SPavel Begunkov ctx->napi_busy_poll_dt = ns_to_ktime(sys_dt); 2018d0c12a8SStefan Roesch } 2028d0c12a8SStefan Roesch 2038d0c12a8SStefan Roesch /* 2048d0c12a8SStefan Roesch * io_napi_free() - Deallocate napi 2058d0c12a8SStefan Roesch * @ctx: pointer to io-uring context structure 2068d0c12a8SStefan Roesch * 2078d0c12a8SStefan Roesch * Free the napi list and the hash table in the io-uring context. 2088d0c12a8SStefan Roesch */ 2098d0c12a8SStefan Roesch void io_napi_free(struct io_ring_ctx *ctx) 2108d0c12a8SStefan Roesch { 2118d0c12a8SStefan Roesch struct io_napi_entry *e; 2128d0c12a8SStefan Roesch 2138d0c12a8SStefan Roesch spin_lock(&ctx->napi_lock); 214*45b3941dSOlivier Langlois list_for_each_entry(e, &ctx->napi_list, list) { 2158d0c12a8SStefan Roesch hash_del_rcu(&e->node); 2168d0c12a8SStefan Roesch kfree_rcu(e, rcu); 2178d0c12a8SStefan Roesch } 218*45b3941dSOlivier Langlois INIT_LIST_HEAD_RCU(&ctx->napi_list); 2198d0c12a8SStefan Roesch spin_unlock(&ctx->napi_lock); 2208d0c12a8SStefan Roesch } 2218d0c12a8SStefan Roesch 2228d0c12a8SStefan Roesch /* 223ef1186c1SStefan Roesch * io_napi_register() - Register napi with io-uring 224ef1186c1SStefan Roesch * @ctx: pointer to io-uring context structure 225ef1186c1SStefan Roesch * @arg: pointer to io_uring_napi structure 226ef1186c1SStefan Roesch * 227ef1186c1SStefan Roesch * Register napi in the io-uring context. 228ef1186c1SStefan Roesch */ 229ef1186c1SStefan Roesch int io_register_napi(struct io_ring_ctx *ctx, void __user *arg) 230ef1186c1SStefan Roesch { 231ef1186c1SStefan Roesch const struct io_uring_napi curr = { 232342b2e39SPavel Begunkov .busy_poll_to = ktime_to_us(ctx->napi_busy_poll_dt), 233ef1186c1SStefan Roesch .prefer_busy_poll = ctx->napi_prefer_busy_poll 234ef1186c1SStefan Roesch }; 235ef1186c1SStefan Roesch struct io_uring_napi napi; 236ef1186c1SStefan Roesch 237bd44d7e9SPavel Begunkov if (ctx->flags & IORING_SETUP_IOPOLL) 238bd44d7e9SPavel Begunkov return -EINVAL; 239ef1186c1SStefan Roesch if (copy_from_user(&napi, arg, sizeof(napi))) 240ef1186c1SStefan Roesch return -EFAULT; 241ef1186c1SStefan Roesch if (napi.pad[0] || napi.pad[1] || napi.pad[2] || napi.resv) 242ef1186c1SStefan Roesch return -EINVAL; 243ef1186c1SStefan Roesch 244ef1186c1SStefan Roesch if (copy_to_user(arg, &curr, sizeof(curr))) 245ef1186c1SStefan Roesch return -EFAULT; 246ef1186c1SStefan Roesch 247342b2e39SPavel Begunkov WRITE_ONCE(ctx->napi_busy_poll_dt, napi.busy_poll_to * NSEC_PER_USEC); 248b4ccc4ddSJens Axboe WRITE_ONCE(ctx->napi_prefer_busy_poll, !!napi.prefer_busy_poll); 249b4ccc4ddSJens Axboe WRITE_ONCE(ctx->napi_enabled, true); 250ef1186c1SStefan Roesch return 0; 251ef1186c1SStefan Roesch } 252ef1186c1SStefan Roesch 253ef1186c1SStefan Roesch /* 254ef1186c1SStefan Roesch * io_napi_unregister() - Unregister napi with io-uring 255ef1186c1SStefan Roesch * @ctx: pointer to io-uring context structure 256ef1186c1SStefan Roesch * @arg: pointer to io_uring_napi structure 257ef1186c1SStefan Roesch * 258ef1186c1SStefan Roesch * Unregister napi. If arg has been specified copy the busy poll timeout and 259ef1186c1SStefan Roesch * prefer busy poll setting to the passed in structure. 260ef1186c1SStefan Roesch */ 261ef1186c1SStefan Roesch int io_unregister_napi(struct io_ring_ctx *ctx, void __user *arg) 262ef1186c1SStefan Roesch { 263ef1186c1SStefan Roesch const struct io_uring_napi curr = { 264342b2e39SPavel Begunkov .busy_poll_to = ktime_to_us(ctx->napi_busy_poll_dt), 265ef1186c1SStefan Roesch .prefer_busy_poll = ctx->napi_prefer_busy_poll 266ef1186c1SStefan Roesch }; 267ef1186c1SStefan Roesch 268ef1186c1SStefan Roesch if (arg && copy_to_user(arg, &curr, sizeof(curr))) 269ef1186c1SStefan Roesch return -EFAULT; 270ef1186c1SStefan Roesch 271342b2e39SPavel Begunkov WRITE_ONCE(ctx->napi_busy_poll_dt, 0); 272ef1186c1SStefan Roesch WRITE_ONCE(ctx->napi_prefer_busy_poll, false); 273b4ccc4ddSJens Axboe WRITE_ONCE(ctx->napi_enabled, false); 274ef1186c1SStefan Roesch return 0; 275ef1186c1SStefan Roesch } 276ef1186c1SStefan Roesch 277ef1186c1SStefan Roesch /* 2788d0c12a8SStefan Roesch * __io_napi_busy_loop() - execute busy poll loop 2798d0c12a8SStefan Roesch * @ctx: pointer to io-uring context structure 2808d0c12a8SStefan Roesch * @iowq: pointer to io wait queue 2818d0c12a8SStefan Roesch * 2828d0c12a8SStefan Roesch * Execute the busy poll loop and merge the spliced off list. 2838d0c12a8SStefan Roesch */ 2848d0c12a8SStefan Roesch void __io_napi_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq) 2858d0c12a8SStefan Roesch { 286489b8006SPavel Begunkov if (ctx->flags & IORING_SETUP_SQPOLL) 287489b8006SPavel Begunkov return; 2888d0c12a8SStefan Roesch 289d5cce407SPavel Begunkov iowq->napi_busy_poll_dt = READ_ONCE(ctx->napi_busy_poll_dt); 290d5cce407SPavel Begunkov if (iowq->timeout != KTIME_MAX) { 2912b8e976bSPavel Begunkov ktime_t dt = ktime_sub(iowq->timeout, io_get_time(ctx)); 292d5cce407SPavel Begunkov 293d5cce407SPavel Begunkov iowq->napi_busy_poll_dt = min_t(u64, iowq->napi_busy_poll_dt, dt); 294d5cce407SPavel Begunkov } 295d5cce407SPavel Begunkov 296489b8006SPavel Begunkov iowq->napi_prefer_busy_poll = READ_ONCE(ctx->napi_prefer_busy_poll); 2978d0c12a8SStefan Roesch io_napi_blocking_busy_loop(ctx, iowq); 2988d0c12a8SStefan Roesch } 2998d0c12a8SStefan Roesch 300ff183d42SStefan Roesch /* 301ff183d42SStefan Roesch * io_napi_sqpoll_busy_poll() - busy poll loop for sqpoll 302ff183d42SStefan Roesch * @ctx: pointer to io-uring context structure 303ff183d42SStefan Roesch * 304ff183d42SStefan Roesch * Splice of the napi list and execute the napi busy poll loop. 305ff183d42SStefan Roesch */ 306ff183d42SStefan Roesch int io_napi_sqpoll_busy_poll(struct io_ring_ctx *ctx) 307ff183d42SStefan Roesch { 308ff183d42SStefan Roesch bool is_stale = false; 309ff183d42SStefan Roesch 310342b2e39SPavel Begunkov if (!READ_ONCE(ctx->napi_busy_poll_dt)) 311ff183d42SStefan Roesch return 0; 312ff183d42SStefan Roesch if (list_empty_careful(&ctx->napi_list)) 313ff183d42SStefan Roesch return 0; 314ff183d42SStefan Roesch 315ff183d42SStefan Roesch rcu_read_lock(); 316ff183d42SStefan Roesch is_stale = __io_napi_do_busy_loop(ctx, NULL); 317ff183d42SStefan Roesch rcu_read_unlock(); 318ff183d42SStefan Roesch 319ff183d42SStefan Roesch io_napi_remove_stale(ctx, is_stale); 320ff183d42SStefan Roesch return 1; 321ff183d42SStefan Roesch } 322ff183d42SStefan Roesch 3238d0c12a8SStefan Roesch #endif 324