xref: /linux-6.15/io_uring/napi.c (revision ff183d42)
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 		e->timeout = jiffies + NAPI_TIMEOUT;
308d0c12a8SStefan Roesch 		return e;
318d0c12a8SStefan Roesch 	}
328d0c12a8SStefan Roesch 
338d0c12a8SStefan Roesch 	return NULL;
348d0c12a8SStefan Roesch }
358d0c12a8SStefan Roesch 
368d0c12a8SStefan Roesch void __io_napi_add(struct io_ring_ctx *ctx, struct socket *sock)
378d0c12a8SStefan Roesch {
388d0c12a8SStefan Roesch 	struct hlist_head *hash_list;
398d0c12a8SStefan Roesch 	unsigned int napi_id;
408d0c12a8SStefan Roesch 	struct sock *sk;
418d0c12a8SStefan Roesch 	struct io_napi_entry *e;
428d0c12a8SStefan Roesch 
438d0c12a8SStefan Roesch 	sk = sock->sk;
448d0c12a8SStefan Roesch 	if (!sk)
458d0c12a8SStefan Roesch 		return;
468d0c12a8SStefan Roesch 
478d0c12a8SStefan Roesch 	napi_id = READ_ONCE(sk->sk_napi_id);
488d0c12a8SStefan Roesch 
498d0c12a8SStefan Roesch 	/* Non-NAPI IDs can be rejected. */
508d0c12a8SStefan Roesch 	if (napi_id < MIN_NAPI_ID)
518d0c12a8SStefan Roesch 		return;
528d0c12a8SStefan Roesch 
538d0c12a8SStefan Roesch 	hash_list = &ctx->napi_ht[hash_min(napi_id, HASH_BITS(ctx->napi_ht))];
548d0c12a8SStefan Roesch 
558d0c12a8SStefan Roesch 	rcu_read_lock();
568d0c12a8SStefan Roesch 	e = io_napi_hash_find(hash_list, napi_id);
578d0c12a8SStefan Roesch 	if (e) {
588d0c12a8SStefan Roesch 		e->timeout = jiffies + NAPI_TIMEOUT;
598d0c12a8SStefan Roesch 		rcu_read_unlock();
608d0c12a8SStefan Roesch 		return;
618d0c12a8SStefan Roesch 	}
628d0c12a8SStefan Roesch 	rcu_read_unlock();
638d0c12a8SStefan Roesch 
648d0c12a8SStefan Roesch 	e = kmalloc(sizeof(*e), GFP_NOWAIT);
658d0c12a8SStefan Roesch 	if (!e)
668d0c12a8SStefan Roesch 		return;
678d0c12a8SStefan Roesch 
688d0c12a8SStefan Roesch 	e->napi_id = napi_id;
698d0c12a8SStefan Roesch 	e->timeout = jiffies + NAPI_TIMEOUT;
708d0c12a8SStefan Roesch 
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);
758d0c12a8SStefan Roesch 		return;
768d0c12a8SStefan Roesch 	}
778d0c12a8SStefan Roesch 
788d0c12a8SStefan Roesch 	hlist_add_tail_rcu(&e->node, hash_list);
798d0c12a8SStefan Roesch 	list_add_tail(&e->list, &ctx->napi_list);
808d0c12a8SStefan Roesch 	spin_unlock(&ctx->napi_lock);
818d0c12a8SStefan Roesch }
828d0c12a8SStefan Roesch 
838d0c12a8SStefan Roesch static void __io_napi_remove_stale(struct io_ring_ctx *ctx)
848d0c12a8SStefan Roesch {
858d0c12a8SStefan Roesch 	struct io_napi_entry *e;
868d0c12a8SStefan Roesch 	unsigned int i;
878d0c12a8SStefan Roesch 
888d0c12a8SStefan Roesch 	spin_lock(&ctx->napi_lock);
898d0c12a8SStefan Roesch 	hash_for_each(ctx->napi_ht, i, e, node) {
908d0c12a8SStefan Roesch 		if (time_after(jiffies, e->timeout)) {
918d0c12a8SStefan Roesch 			list_del(&e->list);
928d0c12a8SStefan Roesch 			hash_del_rcu(&e->node);
938d0c12a8SStefan Roesch 			kfree_rcu(e, rcu);
948d0c12a8SStefan Roesch 		}
958d0c12a8SStefan Roesch 	}
968d0c12a8SStefan Roesch 	spin_unlock(&ctx->napi_lock);
978d0c12a8SStefan Roesch }
988d0c12a8SStefan Roesch 
998d0c12a8SStefan Roesch static inline void io_napi_remove_stale(struct io_ring_ctx *ctx, bool is_stale)
1008d0c12a8SStefan Roesch {
1018d0c12a8SStefan Roesch 	if (is_stale)
1028d0c12a8SStefan Roesch 		__io_napi_remove_stale(ctx);
1038d0c12a8SStefan Roesch }
1048d0c12a8SStefan Roesch 
1058d0c12a8SStefan Roesch static inline bool io_napi_busy_loop_timeout(unsigned long start_time,
1068d0c12a8SStefan Roesch 					     unsigned long bp_usec)
1078d0c12a8SStefan Roesch {
1088d0c12a8SStefan Roesch 	if (bp_usec) {
1098d0c12a8SStefan Roesch 		unsigned long end_time = start_time + bp_usec;
1108d0c12a8SStefan Roesch 		unsigned long now = busy_loop_current_time();
1118d0c12a8SStefan Roesch 
1128d0c12a8SStefan Roesch 		return time_after(now, end_time);
1138d0c12a8SStefan Roesch 	}
1148d0c12a8SStefan Roesch 
1158d0c12a8SStefan Roesch 	return true;
1168d0c12a8SStefan Roesch }
1178d0c12a8SStefan Roesch 
1188d0c12a8SStefan Roesch static bool io_napi_busy_loop_should_end(void *data,
1198d0c12a8SStefan Roesch 					 unsigned long start_time)
1208d0c12a8SStefan Roesch {
1218d0c12a8SStefan Roesch 	struct io_wait_queue *iowq = data;
1228d0c12a8SStefan Roesch 
1238d0c12a8SStefan Roesch 	if (signal_pending(current))
1248d0c12a8SStefan Roesch 		return true;
1258d0c12a8SStefan Roesch 	if (io_should_wake(iowq))
1268d0c12a8SStefan Roesch 		return true;
1278d0c12a8SStefan Roesch 	if (io_napi_busy_loop_timeout(start_time, iowq->napi_busy_poll_to))
1288d0c12a8SStefan Roesch 		return true;
1298d0c12a8SStefan Roesch 
1308d0c12a8SStefan Roesch 	return false;
1318d0c12a8SStefan Roesch }
1328d0c12a8SStefan Roesch 
1338d0c12a8SStefan Roesch static bool __io_napi_do_busy_loop(struct io_ring_ctx *ctx,
1348d0c12a8SStefan Roesch 				   void *loop_end_arg)
1358d0c12a8SStefan Roesch {
1368d0c12a8SStefan Roesch 	struct io_napi_entry *e;
1378d0c12a8SStefan Roesch 	bool (*loop_end)(void *, unsigned long) = NULL;
1388d0c12a8SStefan Roesch 	bool is_stale = false;
1398d0c12a8SStefan Roesch 
1408d0c12a8SStefan Roesch 	if (loop_end_arg)
1418d0c12a8SStefan Roesch 		loop_end = io_napi_busy_loop_should_end;
1428d0c12a8SStefan Roesch 
1438d0c12a8SStefan Roesch 	list_for_each_entry_rcu(e, &ctx->napi_list, list) {
1448d0c12a8SStefan Roesch 		napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg,
1458d0c12a8SStefan Roesch 				   ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET);
1468d0c12a8SStefan Roesch 
1478d0c12a8SStefan Roesch 		if (time_after(jiffies, e->timeout))
1488d0c12a8SStefan Roesch 			is_stale = true;
1498d0c12a8SStefan Roesch 	}
1508d0c12a8SStefan Roesch 
1518d0c12a8SStefan Roesch 	return is_stale;
1528d0c12a8SStefan Roesch }
1538d0c12a8SStefan Roesch 
1548d0c12a8SStefan Roesch static void io_napi_blocking_busy_loop(struct io_ring_ctx *ctx,
1558d0c12a8SStefan Roesch 				       struct io_wait_queue *iowq)
1568d0c12a8SStefan Roesch {
1578d0c12a8SStefan Roesch 	unsigned long start_time = busy_loop_current_time();
1588d0c12a8SStefan Roesch 	void *loop_end_arg = NULL;
1598d0c12a8SStefan Roesch 	bool is_stale = false;
1608d0c12a8SStefan Roesch 
1618d0c12a8SStefan Roesch 	/* Singular lists use a different napi loop end check function and are
1628d0c12a8SStefan Roesch 	 * only executed once.
1638d0c12a8SStefan Roesch 	 */
1648d0c12a8SStefan Roesch 	if (list_is_singular(&ctx->napi_list))
1658d0c12a8SStefan Roesch 		loop_end_arg = iowq;
1668d0c12a8SStefan Roesch 
1678d0c12a8SStefan Roesch 	rcu_read_lock();
1688d0c12a8SStefan Roesch 	do {
1698d0c12a8SStefan Roesch 		is_stale = __io_napi_do_busy_loop(ctx, loop_end_arg);
1708d0c12a8SStefan Roesch 	} while (!io_napi_busy_loop_should_end(iowq, start_time) && !loop_end_arg);
1718d0c12a8SStefan Roesch 	rcu_read_unlock();
1728d0c12a8SStefan Roesch 
1738d0c12a8SStefan Roesch 	io_napi_remove_stale(ctx, is_stale);
1748d0c12a8SStefan Roesch }
1758d0c12a8SStefan Roesch 
1768d0c12a8SStefan Roesch /*
1778d0c12a8SStefan Roesch  * io_napi_init() - Init napi settings
1788d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
1798d0c12a8SStefan Roesch  *
1808d0c12a8SStefan Roesch  * Init napi settings in the io-uring context.
1818d0c12a8SStefan Roesch  */
1828d0c12a8SStefan Roesch void io_napi_init(struct io_ring_ctx *ctx)
1838d0c12a8SStefan Roesch {
1848d0c12a8SStefan Roesch 	INIT_LIST_HEAD(&ctx->napi_list);
1858d0c12a8SStefan Roesch 	spin_lock_init(&ctx->napi_lock);
1868d0c12a8SStefan Roesch 	ctx->napi_prefer_busy_poll = false;
1878d0c12a8SStefan Roesch 	ctx->napi_busy_poll_to = READ_ONCE(sysctl_net_busy_poll);
1888d0c12a8SStefan Roesch }
1898d0c12a8SStefan Roesch 
1908d0c12a8SStefan Roesch /*
1918d0c12a8SStefan Roesch  * io_napi_free() - Deallocate napi
1928d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
1938d0c12a8SStefan Roesch  *
1948d0c12a8SStefan Roesch  * Free the napi list and the hash table in the io-uring context.
1958d0c12a8SStefan Roesch  */
1968d0c12a8SStefan Roesch void io_napi_free(struct io_ring_ctx *ctx)
1978d0c12a8SStefan Roesch {
1988d0c12a8SStefan Roesch 	struct io_napi_entry *e;
1998d0c12a8SStefan Roesch 	LIST_HEAD(napi_list);
2008d0c12a8SStefan Roesch 	unsigned int i;
2018d0c12a8SStefan Roesch 
2028d0c12a8SStefan Roesch 	spin_lock(&ctx->napi_lock);
2038d0c12a8SStefan Roesch 	hash_for_each(ctx->napi_ht, i, e, node) {
2048d0c12a8SStefan Roesch 		hash_del_rcu(&e->node);
2058d0c12a8SStefan Roesch 		kfree_rcu(e, rcu);
2068d0c12a8SStefan Roesch 	}
2078d0c12a8SStefan Roesch 	spin_unlock(&ctx->napi_lock);
2088d0c12a8SStefan Roesch }
2098d0c12a8SStefan Roesch 
2108d0c12a8SStefan Roesch /*
2118d0c12a8SStefan Roesch  * __io_napi_adjust_timeout() - Add napi id to the busy poll list
2128d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
2138d0c12a8SStefan Roesch  * @iowq: pointer to io wait queue
2148d0c12a8SStefan Roesch  * @ts: pointer to timespec or NULL
2158d0c12a8SStefan Roesch  *
2168d0c12a8SStefan Roesch  * Adjust the busy loop timeout according to timespec and busy poll timeout.
2178d0c12a8SStefan Roesch  */
2188d0c12a8SStefan Roesch void __io_napi_adjust_timeout(struct io_ring_ctx *ctx, struct io_wait_queue *iowq,
2198d0c12a8SStefan Roesch 			      struct timespec64 *ts)
2208d0c12a8SStefan Roesch {
2218d0c12a8SStefan Roesch 	unsigned int poll_to = READ_ONCE(ctx->napi_busy_poll_to);
2228d0c12a8SStefan Roesch 
2238d0c12a8SStefan Roesch 	if (ts) {
2248d0c12a8SStefan Roesch 		struct timespec64 poll_to_ts = ns_to_timespec64(1000 * (s64)poll_to);
2258d0c12a8SStefan Roesch 
2268d0c12a8SStefan Roesch 		if (timespec64_compare(ts, &poll_to_ts) > 0) {
2278d0c12a8SStefan Roesch 			*ts = timespec64_sub(*ts, poll_to_ts);
2288d0c12a8SStefan Roesch 		} else {
2298d0c12a8SStefan Roesch 			u64 to = timespec64_to_ns(ts);
2308d0c12a8SStefan Roesch 
2318d0c12a8SStefan Roesch 			do_div(to, 1000);
2328d0c12a8SStefan Roesch 			ts->tv_sec = 0;
2338d0c12a8SStefan Roesch 			ts->tv_nsec = 0;
2348d0c12a8SStefan Roesch 		}
2358d0c12a8SStefan Roesch 	}
2368d0c12a8SStefan Roesch 
2378d0c12a8SStefan Roesch 	iowq->napi_busy_poll_to = poll_to;
2388d0c12a8SStefan Roesch }
2398d0c12a8SStefan Roesch 
2408d0c12a8SStefan Roesch /*
2418d0c12a8SStefan Roesch  * __io_napi_busy_loop() - execute busy poll loop
2428d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
2438d0c12a8SStefan Roesch  * @iowq: pointer to io wait queue
2448d0c12a8SStefan Roesch  *
2458d0c12a8SStefan Roesch  * Execute the busy poll loop and merge the spliced off list.
2468d0c12a8SStefan Roesch  */
2478d0c12a8SStefan Roesch void __io_napi_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq)
2488d0c12a8SStefan Roesch {
2498d0c12a8SStefan Roesch 	iowq->napi_prefer_busy_poll = READ_ONCE(ctx->napi_prefer_busy_poll);
2508d0c12a8SStefan Roesch 
2518d0c12a8SStefan Roesch 	if (!(ctx->flags & IORING_SETUP_SQPOLL) && iowq->napi_busy_poll_to)
2528d0c12a8SStefan Roesch 		io_napi_blocking_busy_loop(ctx, iowq);
2538d0c12a8SStefan Roesch }
2548d0c12a8SStefan Roesch 
255*ff183d42SStefan Roesch /*
256*ff183d42SStefan Roesch  * io_napi_sqpoll_busy_poll() - busy poll loop for sqpoll
257*ff183d42SStefan Roesch  * @ctx: pointer to io-uring context structure
258*ff183d42SStefan Roesch  *
259*ff183d42SStefan Roesch  * Splice of the napi list and execute the napi busy poll loop.
260*ff183d42SStefan Roesch  */
261*ff183d42SStefan Roesch int io_napi_sqpoll_busy_poll(struct io_ring_ctx *ctx)
262*ff183d42SStefan Roesch {
263*ff183d42SStefan Roesch 	LIST_HEAD(napi_list);
264*ff183d42SStefan Roesch 	bool is_stale = false;
265*ff183d42SStefan Roesch 
266*ff183d42SStefan Roesch 	if (!READ_ONCE(ctx->napi_busy_poll_to))
267*ff183d42SStefan Roesch 		return 0;
268*ff183d42SStefan Roesch 	if (list_empty_careful(&ctx->napi_list))
269*ff183d42SStefan Roesch 		return 0;
270*ff183d42SStefan Roesch 
271*ff183d42SStefan Roesch 	rcu_read_lock();
272*ff183d42SStefan Roesch 	is_stale = __io_napi_do_busy_loop(ctx, NULL);
273*ff183d42SStefan Roesch 	rcu_read_unlock();
274*ff183d42SStefan Roesch 
275*ff183d42SStefan Roesch 	io_napi_remove_stale(ctx, is_stale);
276*ff183d42SStefan Roesch 	return 1;
277*ff183d42SStefan Roesch }
278*ff183d42SStefan Roesch 
2798d0c12a8SStefan Roesch #endif
280