xref: /linux-6.15/io_uring/napi.c (revision 6bf90bd8)
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 
84*6bf90bd8SOlivier Langlois static int __io_napi_del_id(struct io_ring_ctx *ctx, unsigned int napi_id)
85*6bf90bd8SOlivier Langlois {
86*6bf90bd8SOlivier Langlois 	struct hlist_head *hash_list;
87*6bf90bd8SOlivier Langlois 	struct io_napi_entry *e;
88*6bf90bd8SOlivier Langlois 
89*6bf90bd8SOlivier Langlois 	/* Non-NAPI IDs can be rejected. */
90*6bf90bd8SOlivier Langlois 	if (napi_id < MIN_NAPI_ID)
91*6bf90bd8SOlivier Langlois 		return -EINVAL;
92*6bf90bd8SOlivier Langlois 
93*6bf90bd8SOlivier Langlois 	hash_list = &ctx->napi_ht[hash_min(napi_id, HASH_BITS(ctx->napi_ht))];
94*6bf90bd8SOlivier Langlois 	guard(spinlock)(&ctx->napi_lock);
95*6bf90bd8SOlivier Langlois 	e = io_napi_hash_find(hash_list, napi_id);
96*6bf90bd8SOlivier Langlois 	if (!e)
97*6bf90bd8SOlivier Langlois 		return -ENOENT;
98*6bf90bd8SOlivier Langlois 
99*6bf90bd8SOlivier Langlois 	list_del_rcu(&e->list);
100*6bf90bd8SOlivier Langlois 	hash_del_rcu(&e->node);
101*6bf90bd8SOlivier Langlois 	kfree_rcu(e, rcu);
102*6bf90bd8SOlivier Langlois 	return 0;
103*6bf90bd8SOlivier Langlois }
104*6bf90bd8SOlivier Langlois 
1058d0c12a8SStefan Roesch static void __io_napi_remove_stale(struct io_ring_ctx *ctx)
1068d0c12a8SStefan Roesch {
1078d0c12a8SStefan Roesch 	struct io_napi_entry *e;
1088d0c12a8SStefan Roesch 
109db1e1adfSOlivier Langlois 	guard(spinlock)(&ctx->napi_lock);
11045b3941dSOlivier Langlois 	/*
11145b3941dSOlivier Langlois 	 * list_for_each_entry_safe() is not required as long as:
11245b3941dSOlivier Langlois 	 * 1. list_del_rcu() does not reset the deleted node next pointer
11345b3941dSOlivier Langlois 	 * 2. kfree_rcu() delays the memory freeing until the next quiescent
11445b3941dSOlivier Langlois 	 *    state
11545b3941dSOlivier Langlois 	 */
11645b3941dSOlivier Langlois 	list_for_each_entry(e, &ctx->napi_list, list) {
1172f3cc8e4SOlivier Langlois 		if (time_after(jiffies, READ_ONCE(e->timeout))) {
11845b3941dSOlivier Langlois 			list_del_rcu(&e->list);
1198d0c12a8SStefan Roesch 			hash_del_rcu(&e->node);
1208d0c12a8SStefan Roesch 			kfree_rcu(e, rcu);
1218d0c12a8SStefan Roesch 		}
1228d0c12a8SStefan Roesch 	}
1238d0c12a8SStefan Roesch }
1248d0c12a8SStefan Roesch 
1258d0c12a8SStefan Roesch static inline void io_napi_remove_stale(struct io_ring_ctx *ctx, bool is_stale)
1268d0c12a8SStefan Roesch {
1278d0c12a8SStefan Roesch 	if (is_stale)
1288d0c12a8SStefan Roesch 		__io_napi_remove_stale(ctx);
1298d0c12a8SStefan Roesch }
1308d0c12a8SStefan Roesch 
131342b2e39SPavel Begunkov static inline bool io_napi_busy_loop_timeout(ktime_t start_time,
132342b2e39SPavel Begunkov 					     ktime_t bp)
1338d0c12a8SStefan Roesch {
134342b2e39SPavel Begunkov 	if (bp) {
135342b2e39SPavel Begunkov 		ktime_t end_time = ktime_add(start_time, bp);
136342b2e39SPavel Begunkov 		ktime_t now = net_to_ktime(busy_loop_current_time());
1378d0c12a8SStefan Roesch 
138342b2e39SPavel Begunkov 		return ktime_after(now, end_time);
1398d0c12a8SStefan Roesch 	}
1408d0c12a8SStefan Roesch 
1418d0c12a8SStefan Roesch 	return true;
1428d0c12a8SStefan Roesch }
1438d0c12a8SStefan Roesch 
1448d0c12a8SStefan Roesch static bool io_napi_busy_loop_should_end(void *data,
1458d0c12a8SStefan Roesch 					 unsigned long start_time)
1468d0c12a8SStefan Roesch {
1478d0c12a8SStefan Roesch 	struct io_wait_queue *iowq = data;
1488d0c12a8SStefan Roesch 
1498d0c12a8SStefan Roesch 	if (signal_pending(current))
1508d0c12a8SStefan Roesch 		return true;
151428f1382SJens Axboe 	if (io_should_wake(iowq) || io_has_work(iowq->ctx))
1528d0c12a8SStefan Roesch 		return true;
153342b2e39SPavel Begunkov 	if (io_napi_busy_loop_timeout(net_to_ktime(start_time),
154342b2e39SPavel Begunkov 				      iowq->napi_busy_poll_dt))
1558d0c12a8SStefan Roesch 		return true;
1568d0c12a8SStefan Roesch 
1578d0c12a8SStefan Roesch 	return false;
1588d0c12a8SStefan Roesch }
1598d0c12a8SStefan Roesch 
160*6bf90bd8SOlivier Langlois /*
161*6bf90bd8SOlivier Langlois  * never report stale entries
162*6bf90bd8SOlivier Langlois  */
163*6bf90bd8SOlivier Langlois static bool static_tracking_do_busy_loop(struct io_ring_ctx *ctx,
164*6bf90bd8SOlivier Langlois 					 bool (*loop_end)(void *, unsigned long),
165*6bf90bd8SOlivier Langlois 					 void *loop_end_arg)
166*6bf90bd8SOlivier Langlois {
167*6bf90bd8SOlivier Langlois 	struct io_napi_entry *e;
168*6bf90bd8SOlivier Langlois 
169*6bf90bd8SOlivier Langlois 	list_for_each_entry_rcu(e, &ctx->napi_list, list)
170*6bf90bd8SOlivier Langlois 		napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg,
171*6bf90bd8SOlivier Langlois 				   ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET);
172*6bf90bd8SOlivier Langlois 	return false;
173*6bf90bd8SOlivier Langlois }
174*6bf90bd8SOlivier Langlois 
175*6bf90bd8SOlivier Langlois static bool
176*6bf90bd8SOlivier Langlois dynamic_tracking_do_busy_loop(struct io_ring_ctx *ctx,
17771afd926SOlivier Langlois 			      bool (*loop_end)(void *, unsigned long),
1788d0c12a8SStefan Roesch 			      void *loop_end_arg)
1798d0c12a8SStefan Roesch {
1808d0c12a8SStefan Roesch 	struct io_napi_entry *e;
1818d0c12a8SStefan Roesch 	bool is_stale = false;
1828d0c12a8SStefan Roesch 
1838d0c12a8SStefan Roesch 	list_for_each_entry_rcu(e, &ctx->napi_list, list) {
1848d0c12a8SStefan Roesch 		napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg,
1858d0c12a8SStefan Roesch 				   ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET);
1868d0c12a8SStefan Roesch 
1872f3cc8e4SOlivier Langlois 		if (time_after(jiffies, READ_ONCE(e->timeout)))
1888d0c12a8SStefan Roesch 			is_stale = true;
1898d0c12a8SStefan Roesch 	}
1908d0c12a8SStefan Roesch 
1918d0c12a8SStefan Roesch 	return is_stale;
1928d0c12a8SStefan Roesch }
1938d0c12a8SStefan Roesch 
194*6bf90bd8SOlivier Langlois static inline bool
195*6bf90bd8SOlivier Langlois __io_napi_do_busy_loop(struct io_ring_ctx *ctx,
196*6bf90bd8SOlivier Langlois 		       bool (*loop_end)(void *, unsigned long),
197*6bf90bd8SOlivier Langlois 		       void *loop_end_arg)
198*6bf90bd8SOlivier Langlois {
199*6bf90bd8SOlivier Langlois 	if (READ_ONCE(ctx->napi_track_mode) == IO_URING_NAPI_TRACKING_STATIC)
200*6bf90bd8SOlivier Langlois 		return static_tracking_do_busy_loop(ctx, loop_end, loop_end_arg);
201*6bf90bd8SOlivier Langlois 	return dynamic_tracking_do_busy_loop(ctx, loop_end, loop_end_arg);
202*6bf90bd8SOlivier Langlois }
203*6bf90bd8SOlivier Langlois 
2048d0c12a8SStefan Roesch static void io_napi_blocking_busy_loop(struct io_ring_ctx *ctx,
2058d0c12a8SStefan Roesch 				       struct io_wait_queue *iowq)
2068d0c12a8SStefan Roesch {
2078d0c12a8SStefan Roesch 	unsigned long start_time = busy_loop_current_time();
20871afd926SOlivier Langlois 	bool (*loop_end)(void *, unsigned long) = NULL;
2098d0c12a8SStefan Roesch 	void *loop_end_arg = NULL;
2108d0c12a8SStefan Roesch 	bool is_stale = false;
2118d0c12a8SStefan Roesch 
2128d0c12a8SStefan Roesch 	/* Singular lists use a different napi loop end check function and are
2138d0c12a8SStefan Roesch 	 * only executed once.
2148d0c12a8SStefan Roesch 	 */
21571afd926SOlivier Langlois 	if (list_is_singular(&ctx->napi_list)) {
21671afd926SOlivier Langlois 		loop_end = io_napi_busy_loop_should_end;
2178d0c12a8SStefan Roesch 		loop_end_arg = iowq;
21871afd926SOlivier Langlois 	}
2198d0c12a8SStefan Roesch 
220db1e1adfSOlivier Langlois 	scoped_guard(rcu) {
2218d0c12a8SStefan Roesch 		do {
22271afd926SOlivier Langlois 			is_stale = __io_napi_do_busy_loop(ctx, loop_end,
22371afd926SOlivier Langlois 							  loop_end_arg);
224db1e1adfSOlivier Langlois 		} while (!io_napi_busy_loop_should_end(iowq, start_time) &&
225db1e1adfSOlivier Langlois 			 !loop_end_arg);
226db1e1adfSOlivier Langlois 	}
2278d0c12a8SStefan Roesch 
2288d0c12a8SStefan Roesch 	io_napi_remove_stale(ctx, is_stale);
2298d0c12a8SStefan Roesch }
2308d0c12a8SStefan Roesch 
2318d0c12a8SStefan Roesch /*
2328d0c12a8SStefan Roesch  * io_napi_init() - Init napi settings
2338d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
2348d0c12a8SStefan Roesch  *
2358d0c12a8SStefan Roesch  * Init napi settings in the io-uring context.
2368d0c12a8SStefan Roesch  */
2378d0c12a8SStefan Roesch void io_napi_init(struct io_ring_ctx *ctx)
2388d0c12a8SStefan Roesch {
239342b2e39SPavel Begunkov 	u64 sys_dt = READ_ONCE(sysctl_net_busy_poll) * NSEC_PER_USEC;
240342b2e39SPavel Begunkov 
2418d0c12a8SStefan Roesch 	INIT_LIST_HEAD(&ctx->napi_list);
2428d0c12a8SStefan Roesch 	spin_lock_init(&ctx->napi_lock);
2438d0c12a8SStefan Roesch 	ctx->napi_prefer_busy_poll = false;
244342b2e39SPavel Begunkov 	ctx->napi_busy_poll_dt = ns_to_ktime(sys_dt);
245*6bf90bd8SOlivier Langlois 	ctx->napi_track_mode = IO_URING_NAPI_TRACKING_INACTIVE;
2468d0c12a8SStefan Roesch }
2478d0c12a8SStefan Roesch 
2488d0c12a8SStefan Roesch /*
2498d0c12a8SStefan Roesch  * io_napi_free() - Deallocate napi
2508d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
2518d0c12a8SStefan Roesch  *
2528d0c12a8SStefan Roesch  * Free the napi list and the hash table in the io-uring context.
2538d0c12a8SStefan Roesch  */
2548d0c12a8SStefan Roesch void io_napi_free(struct io_ring_ctx *ctx)
2558d0c12a8SStefan Roesch {
2568d0c12a8SStefan Roesch 	struct io_napi_entry *e;
2578d0c12a8SStefan Roesch 
258db1e1adfSOlivier Langlois 	guard(spinlock)(&ctx->napi_lock);
25945b3941dSOlivier Langlois 	list_for_each_entry(e, &ctx->napi_list, list) {
2608d0c12a8SStefan Roesch 		hash_del_rcu(&e->node);
2618d0c12a8SStefan Roesch 		kfree_rcu(e, rcu);
2628d0c12a8SStefan Roesch 	}
26345b3941dSOlivier Langlois 	INIT_LIST_HEAD_RCU(&ctx->napi_list);
2648d0c12a8SStefan Roesch }
2658d0c12a8SStefan Roesch 
266*6bf90bd8SOlivier Langlois static int io_napi_register_napi(struct io_ring_ctx *ctx,
267*6bf90bd8SOlivier Langlois 				 struct io_uring_napi *napi)
268*6bf90bd8SOlivier Langlois {
269*6bf90bd8SOlivier Langlois 	switch (napi->op_param) {
270*6bf90bd8SOlivier Langlois 	case IO_URING_NAPI_TRACKING_DYNAMIC:
271*6bf90bd8SOlivier Langlois 	case IO_URING_NAPI_TRACKING_STATIC:
272*6bf90bd8SOlivier Langlois 		break;
273*6bf90bd8SOlivier Langlois 	default:
274*6bf90bd8SOlivier Langlois 		return -EINVAL;
275*6bf90bd8SOlivier Langlois 	}
276*6bf90bd8SOlivier Langlois 	/* clean the napi list for new settings */
277*6bf90bd8SOlivier Langlois 	io_napi_free(ctx);
278*6bf90bd8SOlivier Langlois 	WRITE_ONCE(ctx->napi_track_mode, napi->op_param);
279*6bf90bd8SOlivier Langlois 	WRITE_ONCE(ctx->napi_busy_poll_dt, napi->busy_poll_to * NSEC_PER_USEC);
280*6bf90bd8SOlivier Langlois 	WRITE_ONCE(ctx->napi_prefer_busy_poll, !!napi->prefer_busy_poll);
281*6bf90bd8SOlivier Langlois 	return 0;
282*6bf90bd8SOlivier Langlois }
283*6bf90bd8SOlivier Langlois 
2848d0c12a8SStefan Roesch /*
285ef1186c1SStefan Roesch  * io_napi_register() - Register napi with io-uring
286ef1186c1SStefan Roesch  * @ctx: pointer to io-uring context structure
287ef1186c1SStefan Roesch  * @arg: pointer to io_uring_napi structure
288ef1186c1SStefan Roesch  *
289ef1186c1SStefan Roesch  * Register napi in the io-uring context.
290ef1186c1SStefan Roesch  */
291ef1186c1SStefan Roesch int io_register_napi(struct io_ring_ctx *ctx, void __user *arg)
292ef1186c1SStefan Roesch {
293ef1186c1SStefan Roesch 	const struct io_uring_napi curr = {
294342b2e39SPavel Begunkov 		.busy_poll_to 	  = ktime_to_us(ctx->napi_busy_poll_dt),
295*6bf90bd8SOlivier Langlois 		.prefer_busy_poll = ctx->napi_prefer_busy_poll,
296*6bf90bd8SOlivier Langlois 		.op_param	  = ctx->napi_track_mode
297ef1186c1SStefan Roesch 	};
298ef1186c1SStefan Roesch 	struct io_uring_napi napi;
299ef1186c1SStefan Roesch 
300bd44d7e9SPavel Begunkov 	if (ctx->flags & IORING_SETUP_IOPOLL)
301bd44d7e9SPavel Begunkov 		return -EINVAL;
302ef1186c1SStefan Roesch 	if (copy_from_user(&napi, arg, sizeof(napi)))
303ef1186c1SStefan Roesch 		return -EFAULT;
304*6bf90bd8SOlivier Langlois 	if (napi.pad[0] || napi.pad[1] || napi.resv)
305ef1186c1SStefan Roesch 		return -EINVAL;
306ef1186c1SStefan Roesch 
307ef1186c1SStefan Roesch 	if (copy_to_user(arg, &curr, sizeof(curr)))
308ef1186c1SStefan Roesch 		return -EFAULT;
309ef1186c1SStefan Roesch 
310*6bf90bd8SOlivier Langlois 	switch (napi.opcode) {
311*6bf90bd8SOlivier Langlois 	case IO_URING_NAPI_REGISTER_OP:
312*6bf90bd8SOlivier Langlois 		return io_napi_register_napi(ctx, &napi);
313*6bf90bd8SOlivier Langlois 	case IO_URING_NAPI_STATIC_ADD_ID:
314*6bf90bd8SOlivier Langlois 		if (curr.op_param != IO_URING_NAPI_TRACKING_STATIC)
315*6bf90bd8SOlivier Langlois 			return -EINVAL;
316*6bf90bd8SOlivier Langlois 		return __io_napi_add_id(ctx, napi.op_param);
317*6bf90bd8SOlivier Langlois 	case IO_URING_NAPI_STATIC_DEL_ID:
318*6bf90bd8SOlivier Langlois 		if (curr.op_param != IO_URING_NAPI_TRACKING_STATIC)
319*6bf90bd8SOlivier Langlois 			return -EINVAL;
320*6bf90bd8SOlivier Langlois 		return __io_napi_del_id(ctx, napi.op_param);
321*6bf90bd8SOlivier Langlois 	default:
322*6bf90bd8SOlivier Langlois 		return -EINVAL;
323*6bf90bd8SOlivier Langlois 	}
324ef1186c1SStefan Roesch }
325ef1186c1SStefan Roesch 
326ef1186c1SStefan Roesch /*
327ef1186c1SStefan Roesch  * io_napi_unregister() - Unregister napi with io-uring
328ef1186c1SStefan Roesch  * @ctx: pointer to io-uring context structure
329ef1186c1SStefan Roesch  * @arg: pointer to io_uring_napi structure
330ef1186c1SStefan Roesch  *
331ef1186c1SStefan Roesch  * Unregister napi. If arg has been specified copy the busy poll timeout and
332ef1186c1SStefan Roesch  * prefer busy poll setting to the passed in structure.
333ef1186c1SStefan Roesch  */
334ef1186c1SStefan Roesch int io_unregister_napi(struct io_ring_ctx *ctx, void __user *arg)
335ef1186c1SStefan Roesch {
336ef1186c1SStefan Roesch 	const struct io_uring_napi curr = {
337342b2e39SPavel Begunkov 		.busy_poll_to 	  = ktime_to_us(ctx->napi_busy_poll_dt),
338ef1186c1SStefan Roesch 		.prefer_busy_poll = ctx->napi_prefer_busy_poll
339ef1186c1SStefan Roesch 	};
340ef1186c1SStefan Roesch 
341ef1186c1SStefan Roesch 	if (arg && copy_to_user(arg, &curr, sizeof(curr)))
342ef1186c1SStefan Roesch 		return -EFAULT;
343ef1186c1SStefan Roesch 
344342b2e39SPavel Begunkov 	WRITE_ONCE(ctx->napi_busy_poll_dt, 0);
345ef1186c1SStefan Roesch 	WRITE_ONCE(ctx->napi_prefer_busy_poll, false);
346*6bf90bd8SOlivier Langlois 	WRITE_ONCE(ctx->napi_track_mode, IO_URING_NAPI_TRACKING_INACTIVE);
347ef1186c1SStefan Roesch 	return 0;
348ef1186c1SStefan Roesch }
349ef1186c1SStefan Roesch 
350ef1186c1SStefan Roesch /*
3518d0c12a8SStefan Roesch  * __io_napi_busy_loop() - execute busy poll loop
3528d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
3538d0c12a8SStefan Roesch  * @iowq: pointer to io wait queue
3548d0c12a8SStefan Roesch  *
3558d0c12a8SStefan Roesch  * Execute the busy poll loop and merge the spliced off list.
3568d0c12a8SStefan Roesch  */
3578d0c12a8SStefan Roesch void __io_napi_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq)
3588d0c12a8SStefan Roesch {
359489b8006SPavel Begunkov 	if (ctx->flags & IORING_SETUP_SQPOLL)
360489b8006SPavel Begunkov 		return;
3618d0c12a8SStefan Roesch 
362d5cce407SPavel Begunkov 	iowq->napi_busy_poll_dt = READ_ONCE(ctx->napi_busy_poll_dt);
363d5cce407SPavel Begunkov 	if (iowq->timeout != KTIME_MAX) {
3642b8e976bSPavel Begunkov 		ktime_t dt = ktime_sub(iowq->timeout, io_get_time(ctx));
365d5cce407SPavel Begunkov 
366d5cce407SPavel Begunkov 		iowq->napi_busy_poll_dt = min_t(u64, iowq->napi_busy_poll_dt, dt);
367d5cce407SPavel Begunkov 	}
368d5cce407SPavel Begunkov 
369489b8006SPavel Begunkov 	iowq->napi_prefer_busy_poll = READ_ONCE(ctx->napi_prefer_busy_poll);
3708d0c12a8SStefan Roesch 	io_napi_blocking_busy_loop(ctx, iowq);
3718d0c12a8SStefan Roesch }
3728d0c12a8SStefan Roesch 
373ff183d42SStefan Roesch /*
374ff183d42SStefan Roesch  * io_napi_sqpoll_busy_poll() - busy poll loop for sqpoll
375ff183d42SStefan Roesch  * @ctx: pointer to io-uring context structure
376ff183d42SStefan Roesch  *
377ff183d42SStefan Roesch  * Splice of the napi list and execute the napi busy poll loop.
378ff183d42SStefan Roesch  */
379ff183d42SStefan Roesch int io_napi_sqpoll_busy_poll(struct io_ring_ctx *ctx)
380ff183d42SStefan Roesch {
381ff183d42SStefan Roesch 	bool is_stale = false;
382ff183d42SStefan Roesch 
383342b2e39SPavel Begunkov 	if (!READ_ONCE(ctx->napi_busy_poll_dt))
384ff183d42SStefan Roesch 		return 0;
385ff183d42SStefan Roesch 	if (list_empty_careful(&ctx->napi_list))
386ff183d42SStefan Roesch 		return 0;
387ff183d42SStefan Roesch 
388db1e1adfSOlivier Langlois 	scoped_guard(rcu) {
38971afd926SOlivier Langlois 		is_stale = __io_napi_do_busy_loop(ctx, NULL, NULL);
390db1e1adfSOlivier Langlois 	}
391ff183d42SStefan Roesch 
392ff183d42SStefan Roesch 	io_napi_remove_stale(ctx, is_stale);
393ff183d42SStefan Roesch 	return 1;
394ff183d42SStefan Roesch }
395ff183d42SStefan Roesch 
3968d0c12a8SStefan Roesch #endif
397