xref: /linux-6.15/io_uring/napi.c (revision 45b3941d)
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