xref: /linux-6.15/io_uring/napi.c (revision a5e26f49)
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 
41*a5e26f49SOlivier 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)
48*a5e26f49SOlivier Langlois 		return -EINVAL;
498d0c12a8SStefan Roesch 
508d0c12a8SStefan Roesch 	hash_list = &ctx->napi_ht[hash_min(napi_id, HASH_BITS(ctx->napi_ht))];
518d0c12a8SStefan Roesch 
528d0c12a8SStefan Roesch 	rcu_read_lock();
538d0c12a8SStefan Roesch 	e = io_napi_hash_find(hash_list, napi_id);
548d0c12a8SStefan Roesch 	if (e) {
552f3cc8e4SOlivier Langlois 		WRITE_ONCE(e->timeout, jiffies + NAPI_TIMEOUT);
568d0c12a8SStefan Roesch 		rcu_read_unlock();
57*a5e26f49SOlivier Langlois 		return -EEXIST;
588d0c12a8SStefan Roesch 	}
598d0c12a8SStefan Roesch 	rcu_read_unlock();
608d0c12a8SStefan Roesch 
618d0c12a8SStefan Roesch 	e = kmalloc(sizeof(*e), GFP_NOWAIT);
628d0c12a8SStefan Roesch 	if (!e)
63*a5e26f49SOlivier Langlois 		return -ENOMEM;
648d0c12a8SStefan Roesch 
658d0c12a8SStefan Roesch 	e->napi_id = napi_id;
668d0c12a8SStefan Roesch 	e->timeout = jiffies + NAPI_TIMEOUT;
678d0c12a8SStefan Roesch 
688d0c12a8SStefan Roesch 	spin_lock(&ctx->napi_lock);
698d0c12a8SStefan Roesch 	if (unlikely(io_napi_hash_find(hash_list, napi_id))) {
708d0c12a8SStefan Roesch 		spin_unlock(&ctx->napi_lock);
718d0c12a8SStefan Roesch 		kfree(e);
72*a5e26f49SOlivier Langlois 		return -EEXIST;
738d0c12a8SStefan Roesch 	}
748d0c12a8SStefan Roesch 
758d0c12a8SStefan Roesch 	hlist_add_tail_rcu(&e->node, hash_list);
7645b3941dSOlivier Langlois 	list_add_tail_rcu(&e->list, &ctx->napi_list);
778d0c12a8SStefan Roesch 	spin_unlock(&ctx->napi_lock);
78*a5e26f49SOlivier Langlois 	return 0;
798d0c12a8SStefan Roesch }
808d0c12a8SStefan Roesch 
818d0c12a8SStefan Roesch static void __io_napi_remove_stale(struct io_ring_ctx *ctx)
828d0c12a8SStefan Roesch {
838d0c12a8SStefan Roesch 	struct io_napi_entry *e;
848d0c12a8SStefan Roesch 
858d0c12a8SStefan Roesch 	spin_lock(&ctx->napi_lock);
8645b3941dSOlivier Langlois 	/*
8745b3941dSOlivier Langlois 	 * list_for_each_entry_safe() is not required as long as:
8845b3941dSOlivier Langlois 	 * 1. list_del_rcu() does not reset the deleted node next pointer
8945b3941dSOlivier Langlois 	 * 2. kfree_rcu() delays the memory freeing until the next quiescent
9045b3941dSOlivier Langlois 	 *    state
9145b3941dSOlivier Langlois 	 */
9245b3941dSOlivier Langlois 	list_for_each_entry(e, &ctx->napi_list, list) {
932f3cc8e4SOlivier Langlois 		if (time_after(jiffies, READ_ONCE(e->timeout))) {
9445b3941dSOlivier Langlois 			list_del_rcu(&e->list);
958d0c12a8SStefan Roesch 			hash_del_rcu(&e->node);
968d0c12a8SStefan Roesch 			kfree_rcu(e, rcu);
978d0c12a8SStefan Roesch 		}
988d0c12a8SStefan Roesch 	}
998d0c12a8SStefan Roesch 	spin_unlock(&ctx->napi_lock);
1008d0c12a8SStefan Roesch }
1018d0c12a8SStefan Roesch 
1028d0c12a8SStefan Roesch static inline void io_napi_remove_stale(struct io_ring_ctx *ctx, bool is_stale)
1038d0c12a8SStefan Roesch {
1048d0c12a8SStefan Roesch 	if (is_stale)
1058d0c12a8SStefan Roesch 		__io_napi_remove_stale(ctx);
1068d0c12a8SStefan Roesch }
1078d0c12a8SStefan Roesch 
108342b2e39SPavel Begunkov static inline bool io_napi_busy_loop_timeout(ktime_t start_time,
109342b2e39SPavel Begunkov 					     ktime_t bp)
1108d0c12a8SStefan Roesch {
111342b2e39SPavel Begunkov 	if (bp) {
112342b2e39SPavel Begunkov 		ktime_t end_time = ktime_add(start_time, bp);
113342b2e39SPavel Begunkov 		ktime_t now = net_to_ktime(busy_loop_current_time());
1148d0c12a8SStefan Roesch 
115342b2e39SPavel Begunkov 		return ktime_after(now, end_time);
1168d0c12a8SStefan Roesch 	}
1178d0c12a8SStefan Roesch 
1188d0c12a8SStefan Roesch 	return true;
1198d0c12a8SStefan Roesch }
1208d0c12a8SStefan Roesch 
1218d0c12a8SStefan Roesch static bool io_napi_busy_loop_should_end(void *data,
1228d0c12a8SStefan Roesch 					 unsigned long start_time)
1238d0c12a8SStefan Roesch {
1248d0c12a8SStefan Roesch 	struct io_wait_queue *iowq = data;
1258d0c12a8SStefan Roesch 
1268d0c12a8SStefan Roesch 	if (signal_pending(current))
1278d0c12a8SStefan Roesch 		return true;
128428f1382SJens Axboe 	if (io_should_wake(iowq) || io_has_work(iowq->ctx))
1298d0c12a8SStefan Roesch 		return true;
130342b2e39SPavel Begunkov 	if (io_napi_busy_loop_timeout(net_to_ktime(start_time),
131342b2e39SPavel Begunkov 				      iowq->napi_busy_poll_dt))
1328d0c12a8SStefan Roesch 		return true;
1338d0c12a8SStefan Roesch 
1348d0c12a8SStefan Roesch 	return false;
1358d0c12a8SStefan Roesch }
1368d0c12a8SStefan Roesch 
1378d0c12a8SStefan Roesch static bool __io_napi_do_busy_loop(struct io_ring_ctx *ctx,
1388d0c12a8SStefan Roesch 				   void *loop_end_arg)
1398d0c12a8SStefan Roesch {
1408d0c12a8SStefan Roesch 	struct io_napi_entry *e;
1418d0c12a8SStefan Roesch 	bool (*loop_end)(void *, unsigned long) = NULL;
1428d0c12a8SStefan Roesch 	bool is_stale = false;
1438d0c12a8SStefan Roesch 
1448d0c12a8SStefan Roesch 	if (loop_end_arg)
1458d0c12a8SStefan Roesch 		loop_end = io_napi_busy_loop_should_end;
1468d0c12a8SStefan Roesch 
1478d0c12a8SStefan Roesch 	list_for_each_entry_rcu(e, &ctx->napi_list, list) {
1488d0c12a8SStefan Roesch 		napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg,
1498d0c12a8SStefan Roesch 				   ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET);
1508d0c12a8SStefan Roesch 
1512f3cc8e4SOlivier Langlois 		if (time_after(jiffies, READ_ONCE(e->timeout)))
1528d0c12a8SStefan Roesch 			is_stale = true;
1538d0c12a8SStefan Roesch 	}
1548d0c12a8SStefan Roesch 
1558d0c12a8SStefan Roesch 	return is_stale;
1568d0c12a8SStefan Roesch }
1578d0c12a8SStefan Roesch 
1588d0c12a8SStefan Roesch static void io_napi_blocking_busy_loop(struct io_ring_ctx *ctx,
1598d0c12a8SStefan Roesch 				       struct io_wait_queue *iowq)
1608d0c12a8SStefan Roesch {
1618d0c12a8SStefan Roesch 	unsigned long start_time = busy_loop_current_time();
1628d0c12a8SStefan Roesch 	void *loop_end_arg = NULL;
1638d0c12a8SStefan Roesch 	bool is_stale = false;
1648d0c12a8SStefan Roesch 
1658d0c12a8SStefan Roesch 	/* Singular lists use a different napi loop end check function and are
1668d0c12a8SStefan Roesch 	 * only executed once.
1678d0c12a8SStefan Roesch 	 */
1688d0c12a8SStefan Roesch 	if (list_is_singular(&ctx->napi_list))
1698d0c12a8SStefan Roesch 		loop_end_arg = iowq;
1708d0c12a8SStefan Roesch 
1718d0c12a8SStefan Roesch 	rcu_read_lock();
1728d0c12a8SStefan Roesch 	do {
1738d0c12a8SStefan Roesch 		is_stale = __io_napi_do_busy_loop(ctx, loop_end_arg);
1748d0c12a8SStefan Roesch 	} while (!io_napi_busy_loop_should_end(iowq, start_time) && !loop_end_arg);
1758d0c12a8SStefan Roesch 	rcu_read_unlock();
1768d0c12a8SStefan Roesch 
1778d0c12a8SStefan Roesch 	io_napi_remove_stale(ctx, is_stale);
1788d0c12a8SStefan Roesch }
1798d0c12a8SStefan Roesch 
1808d0c12a8SStefan Roesch /*
1818d0c12a8SStefan Roesch  * io_napi_init() - Init napi settings
1828d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
1838d0c12a8SStefan Roesch  *
1848d0c12a8SStefan Roesch  * Init napi settings in the io-uring context.
1858d0c12a8SStefan Roesch  */
1868d0c12a8SStefan Roesch void io_napi_init(struct io_ring_ctx *ctx)
1878d0c12a8SStefan Roesch {
188342b2e39SPavel Begunkov 	u64 sys_dt = READ_ONCE(sysctl_net_busy_poll) * NSEC_PER_USEC;
189342b2e39SPavel Begunkov 
1908d0c12a8SStefan Roesch 	INIT_LIST_HEAD(&ctx->napi_list);
1918d0c12a8SStefan Roesch 	spin_lock_init(&ctx->napi_lock);
1928d0c12a8SStefan Roesch 	ctx->napi_prefer_busy_poll = false;
193342b2e39SPavel Begunkov 	ctx->napi_busy_poll_dt = ns_to_ktime(sys_dt);
1948d0c12a8SStefan Roesch }
1958d0c12a8SStefan Roesch 
1968d0c12a8SStefan Roesch /*
1978d0c12a8SStefan Roesch  * io_napi_free() - Deallocate napi
1988d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
1998d0c12a8SStefan Roesch  *
2008d0c12a8SStefan Roesch  * Free the napi list and the hash table in the io-uring context.
2018d0c12a8SStefan Roesch  */
2028d0c12a8SStefan Roesch void io_napi_free(struct io_ring_ctx *ctx)
2038d0c12a8SStefan Roesch {
2048d0c12a8SStefan Roesch 	struct io_napi_entry *e;
2058d0c12a8SStefan Roesch 
2068d0c12a8SStefan Roesch 	spin_lock(&ctx->napi_lock);
20745b3941dSOlivier Langlois 	list_for_each_entry(e, &ctx->napi_list, list) {
2088d0c12a8SStefan Roesch 		hash_del_rcu(&e->node);
2098d0c12a8SStefan Roesch 		kfree_rcu(e, rcu);
2108d0c12a8SStefan Roesch 	}
21145b3941dSOlivier Langlois 	INIT_LIST_HEAD_RCU(&ctx->napi_list);
2128d0c12a8SStefan Roesch 	spin_unlock(&ctx->napi_lock);
2138d0c12a8SStefan Roesch }
2148d0c12a8SStefan Roesch 
2158d0c12a8SStefan Roesch /*
216ef1186c1SStefan Roesch  * io_napi_register() - Register napi with io-uring
217ef1186c1SStefan Roesch  * @ctx: pointer to io-uring context structure
218ef1186c1SStefan Roesch  * @arg: pointer to io_uring_napi structure
219ef1186c1SStefan Roesch  *
220ef1186c1SStefan Roesch  * Register napi in the io-uring context.
221ef1186c1SStefan Roesch  */
222ef1186c1SStefan Roesch int io_register_napi(struct io_ring_ctx *ctx, void __user *arg)
223ef1186c1SStefan Roesch {
224ef1186c1SStefan Roesch 	const struct io_uring_napi curr = {
225342b2e39SPavel Begunkov 		.busy_poll_to 	  = ktime_to_us(ctx->napi_busy_poll_dt),
226ef1186c1SStefan Roesch 		.prefer_busy_poll = ctx->napi_prefer_busy_poll
227ef1186c1SStefan Roesch 	};
228ef1186c1SStefan Roesch 	struct io_uring_napi napi;
229ef1186c1SStefan Roesch 
230bd44d7e9SPavel Begunkov 	if (ctx->flags & IORING_SETUP_IOPOLL)
231bd44d7e9SPavel Begunkov 		return -EINVAL;
232ef1186c1SStefan Roesch 	if (copy_from_user(&napi, arg, sizeof(napi)))
233ef1186c1SStefan Roesch 		return -EFAULT;
234ef1186c1SStefan Roesch 	if (napi.pad[0] || napi.pad[1] || napi.pad[2] || napi.resv)
235ef1186c1SStefan Roesch 		return -EINVAL;
236ef1186c1SStefan Roesch 
237ef1186c1SStefan Roesch 	if (copy_to_user(arg, &curr, sizeof(curr)))
238ef1186c1SStefan Roesch 		return -EFAULT;
239ef1186c1SStefan Roesch 
240342b2e39SPavel Begunkov 	WRITE_ONCE(ctx->napi_busy_poll_dt, napi.busy_poll_to * NSEC_PER_USEC);
241b4ccc4ddSJens Axboe 	WRITE_ONCE(ctx->napi_prefer_busy_poll, !!napi.prefer_busy_poll);
242b4ccc4ddSJens Axboe 	WRITE_ONCE(ctx->napi_enabled, true);
243ef1186c1SStefan Roesch 	return 0;
244ef1186c1SStefan Roesch }
245ef1186c1SStefan Roesch 
246ef1186c1SStefan Roesch /*
247ef1186c1SStefan Roesch  * io_napi_unregister() - Unregister napi with io-uring
248ef1186c1SStefan Roesch  * @ctx: pointer to io-uring context structure
249ef1186c1SStefan Roesch  * @arg: pointer to io_uring_napi structure
250ef1186c1SStefan Roesch  *
251ef1186c1SStefan Roesch  * Unregister napi. If arg has been specified copy the busy poll timeout and
252ef1186c1SStefan Roesch  * prefer busy poll setting to the passed in structure.
253ef1186c1SStefan Roesch  */
254ef1186c1SStefan Roesch int io_unregister_napi(struct io_ring_ctx *ctx, void __user *arg)
255ef1186c1SStefan Roesch {
256ef1186c1SStefan Roesch 	const struct io_uring_napi curr = {
257342b2e39SPavel Begunkov 		.busy_poll_to 	  = ktime_to_us(ctx->napi_busy_poll_dt),
258ef1186c1SStefan Roesch 		.prefer_busy_poll = ctx->napi_prefer_busy_poll
259ef1186c1SStefan Roesch 	};
260ef1186c1SStefan Roesch 
261ef1186c1SStefan Roesch 	if (arg && copy_to_user(arg, &curr, sizeof(curr)))
262ef1186c1SStefan Roesch 		return -EFAULT;
263ef1186c1SStefan Roesch 
264342b2e39SPavel Begunkov 	WRITE_ONCE(ctx->napi_busy_poll_dt, 0);
265ef1186c1SStefan Roesch 	WRITE_ONCE(ctx->napi_prefer_busy_poll, false);
266b4ccc4ddSJens Axboe 	WRITE_ONCE(ctx->napi_enabled, false);
267ef1186c1SStefan Roesch 	return 0;
268ef1186c1SStefan Roesch }
269ef1186c1SStefan Roesch 
270ef1186c1SStefan Roesch /*
2718d0c12a8SStefan Roesch  * __io_napi_busy_loop() - execute busy poll loop
2728d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
2738d0c12a8SStefan Roesch  * @iowq: pointer to io wait queue
2748d0c12a8SStefan Roesch  *
2758d0c12a8SStefan Roesch  * Execute the busy poll loop and merge the spliced off list.
2768d0c12a8SStefan Roesch  */
2778d0c12a8SStefan Roesch void __io_napi_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq)
2788d0c12a8SStefan Roesch {
279489b8006SPavel Begunkov 	if (ctx->flags & IORING_SETUP_SQPOLL)
280489b8006SPavel Begunkov 		return;
2818d0c12a8SStefan Roesch 
282d5cce407SPavel Begunkov 	iowq->napi_busy_poll_dt = READ_ONCE(ctx->napi_busy_poll_dt);
283d5cce407SPavel Begunkov 	if (iowq->timeout != KTIME_MAX) {
2842b8e976bSPavel Begunkov 		ktime_t dt = ktime_sub(iowq->timeout, io_get_time(ctx));
285d5cce407SPavel Begunkov 
286d5cce407SPavel Begunkov 		iowq->napi_busy_poll_dt = min_t(u64, iowq->napi_busy_poll_dt, dt);
287d5cce407SPavel Begunkov 	}
288d5cce407SPavel Begunkov 
289489b8006SPavel Begunkov 	iowq->napi_prefer_busy_poll = READ_ONCE(ctx->napi_prefer_busy_poll);
2908d0c12a8SStefan Roesch 	io_napi_blocking_busy_loop(ctx, iowq);
2918d0c12a8SStefan Roesch }
2928d0c12a8SStefan Roesch 
293ff183d42SStefan Roesch /*
294ff183d42SStefan Roesch  * io_napi_sqpoll_busy_poll() - busy poll loop for sqpoll
295ff183d42SStefan Roesch  * @ctx: pointer to io-uring context structure
296ff183d42SStefan Roesch  *
297ff183d42SStefan Roesch  * Splice of the napi list and execute the napi busy poll loop.
298ff183d42SStefan Roesch  */
299ff183d42SStefan Roesch int io_napi_sqpoll_busy_poll(struct io_ring_ctx *ctx)
300ff183d42SStefan Roesch {
301ff183d42SStefan Roesch 	bool is_stale = false;
302ff183d42SStefan Roesch 
303342b2e39SPavel Begunkov 	if (!READ_ONCE(ctx->napi_busy_poll_dt))
304ff183d42SStefan Roesch 		return 0;
305ff183d42SStefan Roesch 	if (list_empty_careful(&ctx->napi_list))
306ff183d42SStefan Roesch 		return 0;
307ff183d42SStefan Roesch 
308ff183d42SStefan Roesch 	rcu_read_lock();
309ff183d42SStefan Roesch 	is_stale = __io_napi_do_busy_loop(ctx, NULL);
310ff183d42SStefan Roesch 	rcu_read_unlock();
311ff183d42SStefan Roesch 
312ff183d42SStefan Roesch 	io_napi_remove_stale(ctx, is_stale);
313ff183d42SStefan Roesch 	return 1;
314ff183d42SStefan Roesch }
315ff183d42SStefan Roesch 
3168d0c12a8SStefan Roesch #endif
317