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) { 63*2f3cc8e4SOlivier 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); 848d0c12a8SStefan Roesch list_add_tail(&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 unsigned int i; 928d0c12a8SStefan Roesch 938d0c12a8SStefan Roesch spin_lock(&ctx->napi_lock); 948d0c12a8SStefan Roesch hash_for_each(ctx->napi_ht, i, e, node) { 95*2f3cc8e4SOlivier Langlois if (time_after(jiffies, READ_ONCE(e->timeout))) { 968d0c12a8SStefan Roesch list_del(&e->list); 978d0c12a8SStefan Roesch hash_del_rcu(&e->node); 988d0c12a8SStefan Roesch kfree_rcu(e, rcu); 998d0c12a8SStefan Roesch } 1008d0c12a8SStefan Roesch } 1018d0c12a8SStefan Roesch spin_unlock(&ctx->napi_lock); 1028d0c12a8SStefan Roesch } 1038d0c12a8SStefan Roesch 1048d0c12a8SStefan Roesch static inline void io_napi_remove_stale(struct io_ring_ctx *ctx, bool is_stale) 1058d0c12a8SStefan Roesch { 1068d0c12a8SStefan Roesch if (is_stale) 1078d0c12a8SStefan Roesch __io_napi_remove_stale(ctx); 1088d0c12a8SStefan Roesch } 1098d0c12a8SStefan Roesch 110342b2e39SPavel Begunkov static inline bool io_napi_busy_loop_timeout(ktime_t start_time, 111342b2e39SPavel Begunkov ktime_t bp) 1128d0c12a8SStefan Roesch { 113342b2e39SPavel Begunkov if (bp) { 114342b2e39SPavel Begunkov ktime_t end_time = ktime_add(start_time, bp); 115342b2e39SPavel Begunkov ktime_t now = net_to_ktime(busy_loop_current_time()); 1168d0c12a8SStefan Roesch 117342b2e39SPavel Begunkov return ktime_after(now, end_time); 1188d0c12a8SStefan Roesch } 1198d0c12a8SStefan Roesch 1208d0c12a8SStefan Roesch return true; 1218d0c12a8SStefan Roesch } 1228d0c12a8SStefan Roesch 1238d0c12a8SStefan Roesch static bool io_napi_busy_loop_should_end(void *data, 1248d0c12a8SStefan Roesch unsigned long start_time) 1258d0c12a8SStefan Roesch { 1268d0c12a8SStefan Roesch struct io_wait_queue *iowq = data; 1278d0c12a8SStefan Roesch 1288d0c12a8SStefan Roesch if (signal_pending(current)) 1298d0c12a8SStefan Roesch return true; 130428f1382SJens Axboe if (io_should_wake(iowq) || io_has_work(iowq->ctx)) 1318d0c12a8SStefan Roesch return true; 132342b2e39SPavel Begunkov if (io_napi_busy_loop_timeout(net_to_ktime(start_time), 133342b2e39SPavel Begunkov iowq->napi_busy_poll_dt)) 1348d0c12a8SStefan Roesch return true; 1358d0c12a8SStefan Roesch 1368d0c12a8SStefan Roesch return false; 1378d0c12a8SStefan Roesch } 1388d0c12a8SStefan Roesch 1398d0c12a8SStefan Roesch static bool __io_napi_do_busy_loop(struct io_ring_ctx *ctx, 1408d0c12a8SStefan Roesch void *loop_end_arg) 1418d0c12a8SStefan Roesch { 1428d0c12a8SStefan Roesch struct io_napi_entry *e; 1438d0c12a8SStefan Roesch bool (*loop_end)(void *, unsigned long) = NULL; 1448d0c12a8SStefan Roesch bool is_stale = false; 1458d0c12a8SStefan Roesch 1468d0c12a8SStefan Roesch if (loop_end_arg) 1478d0c12a8SStefan Roesch loop_end = io_napi_busy_loop_should_end; 1488d0c12a8SStefan Roesch 1498d0c12a8SStefan Roesch list_for_each_entry_rcu(e, &ctx->napi_list, list) { 1508d0c12a8SStefan Roesch napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg, 1518d0c12a8SStefan Roesch ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET); 1528d0c12a8SStefan Roesch 153*2f3cc8e4SOlivier Langlois if (time_after(jiffies, READ_ONCE(e->timeout))) 1548d0c12a8SStefan Roesch is_stale = true; 1558d0c12a8SStefan Roesch } 1568d0c12a8SStefan Roesch 1578d0c12a8SStefan Roesch return is_stale; 1588d0c12a8SStefan Roesch } 1598d0c12a8SStefan Roesch 1608d0c12a8SStefan Roesch static void io_napi_blocking_busy_loop(struct io_ring_ctx *ctx, 1618d0c12a8SStefan Roesch struct io_wait_queue *iowq) 1628d0c12a8SStefan Roesch { 1638d0c12a8SStefan Roesch unsigned long start_time = busy_loop_current_time(); 1648d0c12a8SStefan Roesch void *loop_end_arg = NULL; 1658d0c12a8SStefan Roesch bool is_stale = false; 1668d0c12a8SStefan Roesch 1678d0c12a8SStefan Roesch /* Singular lists use a different napi loop end check function and are 1688d0c12a8SStefan Roesch * only executed once. 1698d0c12a8SStefan Roesch */ 1708d0c12a8SStefan Roesch if (list_is_singular(&ctx->napi_list)) 1718d0c12a8SStefan Roesch loop_end_arg = iowq; 1728d0c12a8SStefan Roesch 1738d0c12a8SStefan Roesch rcu_read_lock(); 1748d0c12a8SStefan Roesch do { 1758d0c12a8SStefan Roesch is_stale = __io_napi_do_busy_loop(ctx, loop_end_arg); 1768d0c12a8SStefan Roesch } while (!io_napi_busy_loop_should_end(iowq, start_time) && !loop_end_arg); 1778d0c12a8SStefan Roesch rcu_read_unlock(); 1788d0c12a8SStefan Roesch 1798d0c12a8SStefan Roesch io_napi_remove_stale(ctx, is_stale); 1808d0c12a8SStefan Roesch } 1818d0c12a8SStefan Roesch 1828d0c12a8SStefan Roesch /* 1838d0c12a8SStefan Roesch * io_napi_init() - Init napi settings 1848d0c12a8SStefan Roesch * @ctx: pointer to io-uring context structure 1858d0c12a8SStefan Roesch * 1868d0c12a8SStefan Roesch * Init napi settings in the io-uring context. 1878d0c12a8SStefan Roesch */ 1888d0c12a8SStefan Roesch void io_napi_init(struct io_ring_ctx *ctx) 1898d0c12a8SStefan Roesch { 190342b2e39SPavel Begunkov u64 sys_dt = READ_ONCE(sysctl_net_busy_poll) * NSEC_PER_USEC; 191342b2e39SPavel Begunkov 1928d0c12a8SStefan Roesch INIT_LIST_HEAD(&ctx->napi_list); 1938d0c12a8SStefan Roesch spin_lock_init(&ctx->napi_lock); 1948d0c12a8SStefan Roesch ctx->napi_prefer_busy_poll = false; 195342b2e39SPavel Begunkov ctx->napi_busy_poll_dt = ns_to_ktime(sys_dt); 1968d0c12a8SStefan Roesch } 1978d0c12a8SStefan Roesch 1988d0c12a8SStefan Roesch /* 1998d0c12a8SStefan Roesch * io_napi_free() - Deallocate napi 2008d0c12a8SStefan Roesch * @ctx: pointer to io-uring context structure 2018d0c12a8SStefan Roesch * 2028d0c12a8SStefan Roesch * Free the napi list and the hash table in the io-uring context. 2038d0c12a8SStefan Roesch */ 2048d0c12a8SStefan Roesch void io_napi_free(struct io_ring_ctx *ctx) 2058d0c12a8SStefan Roesch { 2068d0c12a8SStefan Roesch struct io_napi_entry *e; 2078d0c12a8SStefan Roesch unsigned int i; 2088d0c12a8SStefan Roesch 2098d0c12a8SStefan Roesch spin_lock(&ctx->napi_lock); 2108d0c12a8SStefan Roesch hash_for_each(ctx->napi_ht, i, e, node) { 2118d0c12a8SStefan Roesch hash_del_rcu(&e->node); 2128d0c12a8SStefan Roesch kfree_rcu(e, rcu); 2138d0c12a8SStefan Roesch } 2148d0c12a8SStefan Roesch spin_unlock(&ctx->napi_lock); 2158d0c12a8SStefan Roesch } 2168d0c12a8SStefan Roesch 2178d0c12a8SStefan Roesch /* 218ef1186c1SStefan Roesch * io_napi_register() - Register napi with io-uring 219ef1186c1SStefan Roesch * @ctx: pointer to io-uring context structure 220ef1186c1SStefan Roesch * @arg: pointer to io_uring_napi structure 221ef1186c1SStefan Roesch * 222ef1186c1SStefan Roesch * Register napi in the io-uring context. 223ef1186c1SStefan Roesch */ 224ef1186c1SStefan Roesch int io_register_napi(struct io_ring_ctx *ctx, void __user *arg) 225ef1186c1SStefan Roesch { 226ef1186c1SStefan Roesch const struct io_uring_napi curr = { 227342b2e39SPavel Begunkov .busy_poll_to = ktime_to_us(ctx->napi_busy_poll_dt), 228ef1186c1SStefan Roesch .prefer_busy_poll = ctx->napi_prefer_busy_poll 229ef1186c1SStefan Roesch }; 230ef1186c1SStefan Roesch struct io_uring_napi napi; 231ef1186c1SStefan Roesch 232bd44d7e9SPavel Begunkov if (ctx->flags & IORING_SETUP_IOPOLL) 233bd44d7e9SPavel Begunkov return -EINVAL; 234ef1186c1SStefan Roesch if (copy_from_user(&napi, arg, sizeof(napi))) 235ef1186c1SStefan Roesch return -EFAULT; 236ef1186c1SStefan Roesch if (napi.pad[0] || napi.pad[1] || napi.pad[2] || napi.resv) 237ef1186c1SStefan Roesch return -EINVAL; 238ef1186c1SStefan Roesch 239ef1186c1SStefan Roesch if (copy_to_user(arg, &curr, sizeof(curr))) 240ef1186c1SStefan Roesch return -EFAULT; 241ef1186c1SStefan Roesch 242342b2e39SPavel Begunkov WRITE_ONCE(ctx->napi_busy_poll_dt, napi.busy_poll_to * NSEC_PER_USEC); 243b4ccc4ddSJens Axboe WRITE_ONCE(ctx->napi_prefer_busy_poll, !!napi.prefer_busy_poll); 244b4ccc4ddSJens Axboe WRITE_ONCE(ctx->napi_enabled, true); 245ef1186c1SStefan Roesch return 0; 246ef1186c1SStefan Roesch } 247ef1186c1SStefan Roesch 248ef1186c1SStefan Roesch /* 249ef1186c1SStefan Roesch * io_napi_unregister() - Unregister napi with io-uring 250ef1186c1SStefan Roesch * @ctx: pointer to io-uring context structure 251ef1186c1SStefan Roesch * @arg: pointer to io_uring_napi structure 252ef1186c1SStefan Roesch * 253ef1186c1SStefan Roesch * Unregister napi. If arg has been specified copy the busy poll timeout and 254ef1186c1SStefan Roesch * prefer busy poll setting to the passed in structure. 255ef1186c1SStefan Roesch */ 256ef1186c1SStefan Roesch int io_unregister_napi(struct io_ring_ctx *ctx, void __user *arg) 257ef1186c1SStefan Roesch { 258ef1186c1SStefan Roesch const struct io_uring_napi curr = { 259342b2e39SPavel Begunkov .busy_poll_to = ktime_to_us(ctx->napi_busy_poll_dt), 260ef1186c1SStefan Roesch .prefer_busy_poll = ctx->napi_prefer_busy_poll 261ef1186c1SStefan Roesch }; 262ef1186c1SStefan Roesch 263ef1186c1SStefan Roesch if (arg && copy_to_user(arg, &curr, sizeof(curr))) 264ef1186c1SStefan Roesch return -EFAULT; 265ef1186c1SStefan Roesch 266342b2e39SPavel Begunkov WRITE_ONCE(ctx->napi_busy_poll_dt, 0); 267ef1186c1SStefan Roesch WRITE_ONCE(ctx->napi_prefer_busy_poll, false); 268b4ccc4ddSJens Axboe WRITE_ONCE(ctx->napi_enabled, false); 269ef1186c1SStefan Roesch return 0; 270ef1186c1SStefan Roesch } 271ef1186c1SStefan Roesch 272ef1186c1SStefan Roesch /* 2738d0c12a8SStefan Roesch * __io_napi_busy_loop() - execute busy poll loop 2748d0c12a8SStefan Roesch * @ctx: pointer to io-uring context structure 2758d0c12a8SStefan Roesch * @iowq: pointer to io wait queue 2768d0c12a8SStefan Roesch * 2778d0c12a8SStefan Roesch * Execute the busy poll loop and merge the spliced off list. 2788d0c12a8SStefan Roesch */ 2798d0c12a8SStefan Roesch void __io_napi_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq) 2808d0c12a8SStefan Roesch { 281489b8006SPavel Begunkov if (ctx->flags & IORING_SETUP_SQPOLL) 282489b8006SPavel Begunkov return; 2838d0c12a8SStefan Roesch 284d5cce407SPavel Begunkov iowq->napi_busy_poll_dt = READ_ONCE(ctx->napi_busy_poll_dt); 285d5cce407SPavel Begunkov if (iowq->timeout != KTIME_MAX) { 2862b8e976bSPavel Begunkov ktime_t dt = ktime_sub(iowq->timeout, io_get_time(ctx)); 287d5cce407SPavel Begunkov 288d5cce407SPavel Begunkov iowq->napi_busy_poll_dt = min_t(u64, iowq->napi_busy_poll_dt, dt); 289d5cce407SPavel Begunkov } 290d5cce407SPavel Begunkov 291489b8006SPavel Begunkov iowq->napi_prefer_busy_poll = READ_ONCE(ctx->napi_prefer_busy_poll); 2928d0c12a8SStefan Roesch io_napi_blocking_busy_loop(ctx, iowq); 2938d0c12a8SStefan Roesch } 2948d0c12a8SStefan Roesch 295ff183d42SStefan Roesch /* 296ff183d42SStefan Roesch * io_napi_sqpoll_busy_poll() - busy poll loop for sqpoll 297ff183d42SStefan Roesch * @ctx: pointer to io-uring context structure 298ff183d42SStefan Roesch * 299ff183d42SStefan Roesch * Splice of the napi list and execute the napi busy poll loop. 300ff183d42SStefan Roesch */ 301ff183d42SStefan Roesch int io_napi_sqpoll_busy_poll(struct io_ring_ctx *ctx) 302ff183d42SStefan Roesch { 303ff183d42SStefan Roesch bool is_stale = false; 304ff183d42SStefan Roesch 305342b2e39SPavel Begunkov if (!READ_ONCE(ctx->napi_busy_poll_dt)) 306ff183d42SStefan Roesch return 0; 307ff183d42SStefan Roesch if (list_empty_careful(&ctx->napi_list)) 308ff183d42SStefan Roesch return 0; 309ff183d42SStefan Roesch 310ff183d42SStefan Roesch rcu_read_lock(); 311ff183d42SStefan Roesch is_stale = __io_napi_do_busy_loop(ctx, NULL); 312ff183d42SStefan Roesch rcu_read_unlock(); 313ff183d42SStefan Roesch 314ff183d42SStefan Roesch io_napi_remove_stale(ctx, is_stale); 315ff183d42SStefan Roesch return 1; 316ff183d42SStefan Roesch } 317ff183d42SStefan Roesch 3188d0c12a8SStefan Roesch #endif 319