xref: /linux-6.15/io_uring/napi.c (revision 71afd926)
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 
41a5e26f49SOlivier 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)
48a5e26f49SOlivier Langlois 		return -EINVAL;
498d0c12a8SStefan Roesch 
508d0c12a8SStefan Roesch 	hash_list = &ctx->napi_ht[hash_min(napi_id, HASH_BITS(ctx->napi_ht))];
518d0c12a8SStefan Roesch 
52db1e1adfSOlivier Langlois 	scoped_guard(rcu) {
538d0c12a8SStefan Roesch 		e = io_napi_hash_find(hash_list, napi_id);
548d0c12a8SStefan Roesch 		if (e) {
552f3cc8e4SOlivier Langlois 			WRITE_ONCE(e->timeout, jiffies + NAPI_TIMEOUT);
56a5e26f49SOlivier Langlois 			return -EEXIST;
578d0c12a8SStefan Roesch 		}
58db1e1adfSOlivier Langlois 	}
598d0c12a8SStefan Roesch 
608d0c12a8SStefan Roesch 	e = kmalloc(sizeof(*e), GFP_NOWAIT);
618d0c12a8SStefan Roesch 	if (!e)
62a5e26f49SOlivier Langlois 		return -ENOMEM;
638d0c12a8SStefan Roesch 
648d0c12a8SStefan Roesch 	e->napi_id = napi_id;
658d0c12a8SStefan Roesch 	e->timeout = jiffies + NAPI_TIMEOUT;
668d0c12a8SStefan Roesch 
67db1e1adfSOlivier Langlois 	/*
68db1e1adfSOlivier Langlois 	 * guard(spinlock) is not used to manually unlock it before calling
69db1e1adfSOlivier Langlois 	 * kfree()
70db1e1adfSOlivier Langlois 	 */
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);
75a5e26f49SOlivier Langlois 		return -EEXIST;
768d0c12a8SStefan Roesch 	}
778d0c12a8SStefan Roesch 
788d0c12a8SStefan Roesch 	hlist_add_tail_rcu(&e->node, hash_list);
7945b3941dSOlivier Langlois 	list_add_tail_rcu(&e->list, &ctx->napi_list);
808d0c12a8SStefan Roesch 	spin_unlock(&ctx->napi_lock);
81a5e26f49SOlivier Langlois 	return 0;
828d0c12a8SStefan Roesch }
838d0c12a8SStefan Roesch 
848d0c12a8SStefan Roesch static void __io_napi_remove_stale(struct io_ring_ctx *ctx)
858d0c12a8SStefan Roesch {
868d0c12a8SStefan Roesch 	struct io_napi_entry *e;
878d0c12a8SStefan Roesch 
88db1e1adfSOlivier Langlois 	guard(spinlock)(&ctx->napi_lock);
8945b3941dSOlivier Langlois 	/*
9045b3941dSOlivier Langlois 	 * list_for_each_entry_safe() is not required as long as:
9145b3941dSOlivier Langlois 	 * 1. list_del_rcu() does not reset the deleted node next pointer
9245b3941dSOlivier Langlois 	 * 2. kfree_rcu() delays the memory freeing until the next quiescent
9345b3941dSOlivier Langlois 	 *    state
9445b3941dSOlivier Langlois 	 */
9545b3941dSOlivier Langlois 	list_for_each_entry(e, &ctx->napi_list, list) {
962f3cc8e4SOlivier Langlois 		if (time_after(jiffies, READ_ONCE(e->timeout))) {
9745b3941dSOlivier Langlois 			list_del_rcu(&e->list);
988d0c12a8SStefan Roesch 			hash_del_rcu(&e->node);
998d0c12a8SStefan Roesch 			kfree_rcu(e, rcu);
1008d0c12a8SStefan Roesch 		}
1018d0c12a8SStefan Roesch 	}
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,
140*71afd926SOlivier Langlois 				   bool (*loop_end)(void *, unsigned long),
1418d0c12a8SStefan Roesch 				   void *loop_end_arg)
1428d0c12a8SStefan Roesch {
1438d0c12a8SStefan Roesch 	struct io_napi_entry *e;
1448d0c12a8SStefan Roesch 	bool is_stale = false;
1458d0c12a8SStefan Roesch 
1468d0c12a8SStefan Roesch 	list_for_each_entry_rcu(e, &ctx->napi_list, list) {
1478d0c12a8SStefan Roesch 		napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg,
1488d0c12a8SStefan Roesch 				   ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET);
1498d0c12a8SStefan Roesch 
1502f3cc8e4SOlivier Langlois 		if (time_after(jiffies, READ_ONCE(e->timeout)))
1518d0c12a8SStefan Roesch 			is_stale = true;
1528d0c12a8SStefan Roesch 	}
1538d0c12a8SStefan Roesch 
1548d0c12a8SStefan Roesch 	return is_stale;
1558d0c12a8SStefan Roesch }
1568d0c12a8SStefan Roesch 
1578d0c12a8SStefan Roesch static void io_napi_blocking_busy_loop(struct io_ring_ctx *ctx,
1588d0c12a8SStefan Roesch 				       struct io_wait_queue *iowq)
1598d0c12a8SStefan Roesch {
1608d0c12a8SStefan Roesch 	unsigned long start_time = busy_loop_current_time();
161*71afd926SOlivier Langlois 	bool (*loop_end)(void *, unsigned long) = NULL;
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 	 */
168*71afd926SOlivier Langlois 	if (list_is_singular(&ctx->napi_list)) {
169*71afd926SOlivier Langlois 		loop_end = io_napi_busy_loop_should_end;
1708d0c12a8SStefan Roesch 		loop_end_arg = iowq;
171*71afd926SOlivier Langlois 	}
1728d0c12a8SStefan Roesch 
173db1e1adfSOlivier Langlois 	scoped_guard(rcu) {
1748d0c12a8SStefan Roesch 		do {
175*71afd926SOlivier Langlois 			is_stale = __io_napi_do_busy_loop(ctx, loop_end,
176*71afd926SOlivier Langlois 							  loop_end_arg);
177db1e1adfSOlivier Langlois 		} while (!io_napi_busy_loop_should_end(iowq, start_time) &&
178db1e1adfSOlivier Langlois 			 !loop_end_arg);
179db1e1adfSOlivier Langlois 	}
1808d0c12a8SStefan Roesch 
1818d0c12a8SStefan Roesch 	io_napi_remove_stale(ctx, is_stale);
1828d0c12a8SStefan Roesch }
1838d0c12a8SStefan Roesch 
1848d0c12a8SStefan Roesch /*
1858d0c12a8SStefan Roesch  * io_napi_init() - Init napi settings
1868d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
1878d0c12a8SStefan Roesch  *
1888d0c12a8SStefan Roesch  * Init napi settings in the io-uring context.
1898d0c12a8SStefan Roesch  */
1908d0c12a8SStefan Roesch void io_napi_init(struct io_ring_ctx *ctx)
1918d0c12a8SStefan Roesch {
192342b2e39SPavel Begunkov 	u64 sys_dt = READ_ONCE(sysctl_net_busy_poll) * NSEC_PER_USEC;
193342b2e39SPavel Begunkov 
1948d0c12a8SStefan Roesch 	INIT_LIST_HEAD(&ctx->napi_list);
1958d0c12a8SStefan Roesch 	spin_lock_init(&ctx->napi_lock);
1968d0c12a8SStefan Roesch 	ctx->napi_prefer_busy_poll = false;
197342b2e39SPavel Begunkov 	ctx->napi_busy_poll_dt = ns_to_ktime(sys_dt);
1988d0c12a8SStefan Roesch }
1998d0c12a8SStefan Roesch 
2008d0c12a8SStefan Roesch /*
2018d0c12a8SStefan Roesch  * io_napi_free() - Deallocate napi
2028d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
2038d0c12a8SStefan Roesch  *
2048d0c12a8SStefan Roesch  * Free the napi list and the hash table in the io-uring context.
2058d0c12a8SStefan Roesch  */
2068d0c12a8SStefan Roesch void io_napi_free(struct io_ring_ctx *ctx)
2078d0c12a8SStefan Roesch {
2088d0c12a8SStefan Roesch 	struct io_napi_entry *e;
2098d0c12a8SStefan Roesch 
210db1e1adfSOlivier Langlois 	guard(spinlock)(&ctx->napi_lock);
21145b3941dSOlivier Langlois 	list_for_each_entry(e, &ctx->napi_list, list) {
2128d0c12a8SStefan Roesch 		hash_del_rcu(&e->node);
2138d0c12a8SStefan Roesch 		kfree_rcu(e, rcu);
2148d0c12a8SStefan Roesch 	}
21545b3941dSOlivier Langlois 	INIT_LIST_HEAD_RCU(&ctx->napi_list);
2168d0c12a8SStefan Roesch }
2178d0c12a8SStefan Roesch 
2188d0c12a8SStefan Roesch /*
219ef1186c1SStefan Roesch  * io_napi_register() - Register napi with io-uring
220ef1186c1SStefan Roesch  * @ctx: pointer to io-uring context structure
221ef1186c1SStefan Roesch  * @arg: pointer to io_uring_napi structure
222ef1186c1SStefan Roesch  *
223ef1186c1SStefan Roesch  * Register napi in the io-uring context.
224ef1186c1SStefan Roesch  */
225ef1186c1SStefan Roesch int io_register_napi(struct io_ring_ctx *ctx, void __user *arg)
226ef1186c1SStefan Roesch {
227ef1186c1SStefan Roesch 	const struct io_uring_napi curr = {
228342b2e39SPavel Begunkov 		.busy_poll_to 	  = ktime_to_us(ctx->napi_busy_poll_dt),
229ef1186c1SStefan Roesch 		.prefer_busy_poll = ctx->napi_prefer_busy_poll
230ef1186c1SStefan Roesch 	};
231ef1186c1SStefan Roesch 	struct io_uring_napi napi;
232ef1186c1SStefan Roesch 
233bd44d7e9SPavel Begunkov 	if (ctx->flags & IORING_SETUP_IOPOLL)
234bd44d7e9SPavel Begunkov 		return -EINVAL;
235ef1186c1SStefan Roesch 	if (copy_from_user(&napi, arg, sizeof(napi)))
236ef1186c1SStefan Roesch 		return -EFAULT;
237ef1186c1SStefan Roesch 	if (napi.pad[0] || napi.pad[1] || napi.pad[2] || napi.resv)
238ef1186c1SStefan Roesch 		return -EINVAL;
239ef1186c1SStefan Roesch 
240ef1186c1SStefan Roesch 	if (copy_to_user(arg, &curr, sizeof(curr)))
241ef1186c1SStefan Roesch 		return -EFAULT;
242ef1186c1SStefan Roesch 
243342b2e39SPavel Begunkov 	WRITE_ONCE(ctx->napi_busy_poll_dt, napi.busy_poll_to * NSEC_PER_USEC);
244b4ccc4ddSJens Axboe 	WRITE_ONCE(ctx->napi_prefer_busy_poll, !!napi.prefer_busy_poll);
245b4ccc4ddSJens Axboe 	WRITE_ONCE(ctx->napi_enabled, true);
246ef1186c1SStefan Roesch 	return 0;
247ef1186c1SStefan Roesch }
248ef1186c1SStefan Roesch 
249ef1186c1SStefan Roesch /*
250ef1186c1SStefan Roesch  * io_napi_unregister() - Unregister napi with io-uring
251ef1186c1SStefan Roesch  * @ctx: pointer to io-uring context structure
252ef1186c1SStefan Roesch  * @arg: pointer to io_uring_napi structure
253ef1186c1SStefan Roesch  *
254ef1186c1SStefan Roesch  * Unregister napi. If arg has been specified copy the busy poll timeout and
255ef1186c1SStefan Roesch  * prefer busy poll setting to the passed in structure.
256ef1186c1SStefan Roesch  */
257ef1186c1SStefan Roesch int io_unregister_napi(struct io_ring_ctx *ctx, void __user *arg)
258ef1186c1SStefan Roesch {
259ef1186c1SStefan Roesch 	const struct io_uring_napi curr = {
260342b2e39SPavel Begunkov 		.busy_poll_to 	  = ktime_to_us(ctx->napi_busy_poll_dt),
261ef1186c1SStefan Roesch 		.prefer_busy_poll = ctx->napi_prefer_busy_poll
262ef1186c1SStefan Roesch 	};
263ef1186c1SStefan Roesch 
264ef1186c1SStefan Roesch 	if (arg && copy_to_user(arg, &curr, sizeof(curr)))
265ef1186c1SStefan Roesch 		return -EFAULT;
266ef1186c1SStefan Roesch 
267342b2e39SPavel Begunkov 	WRITE_ONCE(ctx->napi_busy_poll_dt, 0);
268ef1186c1SStefan Roesch 	WRITE_ONCE(ctx->napi_prefer_busy_poll, false);
269b4ccc4ddSJens Axboe 	WRITE_ONCE(ctx->napi_enabled, false);
270ef1186c1SStefan Roesch 	return 0;
271ef1186c1SStefan Roesch }
272ef1186c1SStefan Roesch 
273ef1186c1SStefan Roesch /*
2748d0c12a8SStefan Roesch  * __io_napi_busy_loop() - execute busy poll loop
2758d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
2768d0c12a8SStefan Roesch  * @iowq: pointer to io wait queue
2778d0c12a8SStefan Roesch  *
2788d0c12a8SStefan Roesch  * Execute the busy poll loop and merge the spliced off list.
2798d0c12a8SStefan Roesch  */
2808d0c12a8SStefan Roesch void __io_napi_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq)
2818d0c12a8SStefan Roesch {
282489b8006SPavel Begunkov 	if (ctx->flags & IORING_SETUP_SQPOLL)
283489b8006SPavel Begunkov 		return;
2848d0c12a8SStefan Roesch 
285d5cce407SPavel Begunkov 	iowq->napi_busy_poll_dt = READ_ONCE(ctx->napi_busy_poll_dt);
286d5cce407SPavel Begunkov 	if (iowq->timeout != KTIME_MAX) {
2872b8e976bSPavel Begunkov 		ktime_t dt = ktime_sub(iowq->timeout, io_get_time(ctx));
288d5cce407SPavel Begunkov 
289d5cce407SPavel Begunkov 		iowq->napi_busy_poll_dt = min_t(u64, iowq->napi_busy_poll_dt, dt);
290d5cce407SPavel Begunkov 	}
291d5cce407SPavel Begunkov 
292489b8006SPavel Begunkov 	iowq->napi_prefer_busy_poll = READ_ONCE(ctx->napi_prefer_busy_poll);
2938d0c12a8SStefan Roesch 	io_napi_blocking_busy_loop(ctx, iowq);
2948d0c12a8SStefan Roesch }
2958d0c12a8SStefan Roesch 
296ff183d42SStefan Roesch /*
297ff183d42SStefan Roesch  * io_napi_sqpoll_busy_poll() - busy poll loop for sqpoll
298ff183d42SStefan Roesch  * @ctx: pointer to io-uring context structure
299ff183d42SStefan Roesch  *
300ff183d42SStefan Roesch  * Splice of the napi list and execute the napi busy poll loop.
301ff183d42SStefan Roesch  */
302ff183d42SStefan Roesch int io_napi_sqpoll_busy_poll(struct io_ring_ctx *ctx)
303ff183d42SStefan Roesch {
304ff183d42SStefan Roesch 	bool is_stale = false;
305ff183d42SStefan Roesch 
306342b2e39SPavel Begunkov 	if (!READ_ONCE(ctx->napi_busy_poll_dt))
307ff183d42SStefan Roesch 		return 0;
308ff183d42SStefan Roesch 	if (list_empty_careful(&ctx->napi_list))
309ff183d42SStefan Roesch 		return 0;
310ff183d42SStefan Roesch 
311db1e1adfSOlivier Langlois 	scoped_guard(rcu) {
312*71afd926SOlivier Langlois 		is_stale = __io_napi_do_busy_loop(ctx, NULL, NULL);
313db1e1adfSOlivier Langlois 	}
314ff183d42SStefan Roesch 
315ff183d42SStefan Roesch 	io_napi_remove_stale(ctx, is_stale);
316ff183d42SStefan Roesch 	return 1;
317ff183d42SStefan Roesch }
318ff183d42SStefan Roesch 
3198d0c12a8SStefan Roesch #endif
320