xref: /linux-6.15/io_uring/napi.c (revision 8d0c12a8)
1*8d0c12a8SStefan Roesch // SPDX-License-Identifier: GPL-2.0
2*8d0c12a8SStefan Roesch 
3*8d0c12a8SStefan Roesch #include "io_uring.h"
4*8d0c12a8SStefan Roesch #include "napi.h"
5*8d0c12a8SStefan Roesch 
6*8d0c12a8SStefan Roesch #ifdef CONFIG_NET_RX_BUSY_POLL
7*8d0c12a8SStefan Roesch 
8*8d0c12a8SStefan Roesch /* Timeout for cleanout of stale entries. */
9*8d0c12a8SStefan Roesch #define NAPI_TIMEOUT		(60 * SEC_CONVERSION)
10*8d0c12a8SStefan Roesch 
11*8d0c12a8SStefan Roesch struct io_napi_entry {
12*8d0c12a8SStefan Roesch 	unsigned int		napi_id;
13*8d0c12a8SStefan Roesch 	struct list_head	list;
14*8d0c12a8SStefan Roesch 
15*8d0c12a8SStefan Roesch 	unsigned long		timeout;
16*8d0c12a8SStefan Roesch 	struct hlist_node	node;
17*8d0c12a8SStefan Roesch 
18*8d0c12a8SStefan Roesch 	struct rcu_head		rcu;
19*8d0c12a8SStefan Roesch };
20*8d0c12a8SStefan Roesch 
21*8d0c12a8SStefan Roesch static struct io_napi_entry *io_napi_hash_find(struct hlist_head *hash_list,
22*8d0c12a8SStefan Roesch 					       unsigned int napi_id)
23*8d0c12a8SStefan Roesch {
24*8d0c12a8SStefan Roesch 	struct io_napi_entry *e;
25*8d0c12a8SStefan Roesch 
26*8d0c12a8SStefan Roesch 	hlist_for_each_entry_rcu(e, hash_list, node) {
27*8d0c12a8SStefan Roesch 		if (e->napi_id != napi_id)
28*8d0c12a8SStefan Roesch 			continue;
29*8d0c12a8SStefan Roesch 		e->timeout = jiffies + NAPI_TIMEOUT;
30*8d0c12a8SStefan Roesch 		return e;
31*8d0c12a8SStefan Roesch 	}
32*8d0c12a8SStefan Roesch 
33*8d0c12a8SStefan Roesch 	return NULL;
34*8d0c12a8SStefan Roesch }
35*8d0c12a8SStefan Roesch 
36*8d0c12a8SStefan Roesch void __io_napi_add(struct io_ring_ctx *ctx, struct socket *sock)
37*8d0c12a8SStefan Roesch {
38*8d0c12a8SStefan Roesch 	struct hlist_head *hash_list;
39*8d0c12a8SStefan Roesch 	unsigned int napi_id;
40*8d0c12a8SStefan Roesch 	struct sock *sk;
41*8d0c12a8SStefan Roesch 	struct io_napi_entry *e;
42*8d0c12a8SStefan Roesch 
43*8d0c12a8SStefan Roesch 	sk = sock->sk;
44*8d0c12a8SStefan Roesch 	if (!sk)
45*8d0c12a8SStefan Roesch 		return;
46*8d0c12a8SStefan Roesch 
47*8d0c12a8SStefan Roesch 	napi_id = READ_ONCE(sk->sk_napi_id);
48*8d0c12a8SStefan Roesch 
49*8d0c12a8SStefan Roesch 	/* Non-NAPI IDs can be rejected. */
50*8d0c12a8SStefan Roesch 	if (napi_id < MIN_NAPI_ID)
51*8d0c12a8SStefan Roesch 		return;
52*8d0c12a8SStefan Roesch 
53*8d0c12a8SStefan Roesch 	hash_list = &ctx->napi_ht[hash_min(napi_id, HASH_BITS(ctx->napi_ht))];
54*8d0c12a8SStefan Roesch 
55*8d0c12a8SStefan Roesch 	rcu_read_lock();
56*8d0c12a8SStefan Roesch 	e = io_napi_hash_find(hash_list, napi_id);
57*8d0c12a8SStefan Roesch 	if (e) {
58*8d0c12a8SStefan Roesch 		e->timeout = jiffies + NAPI_TIMEOUT;
59*8d0c12a8SStefan Roesch 		rcu_read_unlock();
60*8d0c12a8SStefan Roesch 		return;
61*8d0c12a8SStefan Roesch 	}
62*8d0c12a8SStefan Roesch 	rcu_read_unlock();
63*8d0c12a8SStefan Roesch 
64*8d0c12a8SStefan Roesch 	e = kmalloc(sizeof(*e), GFP_NOWAIT);
65*8d0c12a8SStefan Roesch 	if (!e)
66*8d0c12a8SStefan Roesch 		return;
67*8d0c12a8SStefan Roesch 
68*8d0c12a8SStefan Roesch 	e->napi_id = napi_id;
69*8d0c12a8SStefan Roesch 	e->timeout = jiffies + NAPI_TIMEOUT;
70*8d0c12a8SStefan Roesch 
71*8d0c12a8SStefan Roesch 	spin_lock(&ctx->napi_lock);
72*8d0c12a8SStefan Roesch 	if (unlikely(io_napi_hash_find(hash_list, napi_id))) {
73*8d0c12a8SStefan Roesch 		spin_unlock(&ctx->napi_lock);
74*8d0c12a8SStefan Roesch 		kfree(e);
75*8d0c12a8SStefan Roesch 		return;
76*8d0c12a8SStefan Roesch 	}
77*8d0c12a8SStefan Roesch 
78*8d0c12a8SStefan Roesch 	hlist_add_tail_rcu(&e->node, hash_list);
79*8d0c12a8SStefan Roesch 	list_add_tail(&e->list, &ctx->napi_list);
80*8d0c12a8SStefan Roesch 	spin_unlock(&ctx->napi_lock);
81*8d0c12a8SStefan Roesch }
82*8d0c12a8SStefan Roesch 
83*8d0c12a8SStefan Roesch static void __io_napi_remove_stale(struct io_ring_ctx *ctx)
84*8d0c12a8SStefan Roesch {
85*8d0c12a8SStefan Roesch 	struct io_napi_entry *e;
86*8d0c12a8SStefan Roesch 	unsigned int i;
87*8d0c12a8SStefan Roesch 
88*8d0c12a8SStefan Roesch 	spin_lock(&ctx->napi_lock);
89*8d0c12a8SStefan Roesch 	hash_for_each(ctx->napi_ht, i, e, node) {
90*8d0c12a8SStefan Roesch 		if (time_after(jiffies, e->timeout)) {
91*8d0c12a8SStefan Roesch 			list_del(&e->list);
92*8d0c12a8SStefan Roesch 			hash_del_rcu(&e->node);
93*8d0c12a8SStefan Roesch 			kfree_rcu(e, rcu);
94*8d0c12a8SStefan Roesch 		}
95*8d0c12a8SStefan Roesch 	}
96*8d0c12a8SStefan Roesch 	spin_unlock(&ctx->napi_lock);
97*8d0c12a8SStefan Roesch }
98*8d0c12a8SStefan Roesch 
99*8d0c12a8SStefan Roesch static inline void io_napi_remove_stale(struct io_ring_ctx *ctx, bool is_stale)
100*8d0c12a8SStefan Roesch {
101*8d0c12a8SStefan Roesch 	if (is_stale)
102*8d0c12a8SStefan Roesch 		__io_napi_remove_stale(ctx);
103*8d0c12a8SStefan Roesch }
104*8d0c12a8SStefan Roesch 
105*8d0c12a8SStefan Roesch static inline bool io_napi_busy_loop_timeout(unsigned long start_time,
106*8d0c12a8SStefan Roesch 					     unsigned long bp_usec)
107*8d0c12a8SStefan Roesch {
108*8d0c12a8SStefan Roesch 	if (bp_usec) {
109*8d0c12a8SStefan Roesch 		unsigned long end_time = start_time + bp_usec;
110*8d0c12a8SStefan Roesch 		unsigned long now = busy_loop_current_time();
111*8d0c12a8SStefan Roesch 
112*8d0c12a8SStefan Roesch 		return time_after(now, end_time);
113*8d0c12a8SStefan Roesch 	}
114*8d0c12a8SStefan Roesch 
115*8d0c12a8SStefan Roesch 	return true;
116*8d0c12a8SStefan Roesch }
117*8d0c12a8SStefan Roesch 
118*8d0c12a8SStefan Roesch static bool io_napi_busy_loop_should_end(void *data,
119*8d0c12a8SStefan Roesch 					 unsigned long start_time)
120*8d0c12a8SStefan Roesch {
121*8d0c12a8SStefan Roesch 	struct io_wait_queue *iowq = data;
122*8d0c12a8SStefan Roesch 
123*8d0c12a8SStefan Roesch 	if (signal_pending(current))
124*8d0c12a8SStefan Roesch 		return true;
125*8d0c12a8SStefan Roesch 	if (io_should_wake(iowq))
126*8d0c12a8SStefan Roesch 		return true;
127*8d0c12a8SStefan Roesch 	if (io_napi_busy_loop_timeout(start_time, iowq->napi_busy_poll_to))
128*8d0c12a8SStefan Roesch 		return true;
129*8d0c12a8SStefan Roesch 
130*8d0c12a8SStefan Roesch 	return false;
131*8d0c12a8SStefan Roesch }
132*8d0c12a8SStefan Roesch 
133*8d0c12a8SStefan Roesch static bool __io_napi_do_busy_loop(struct io_ring_ctx *ctx,
134*8d0c12a8SStefan Roesch 				   void *loop_end_arg)
135*8d0c12a8SStefan Roesch {
136*8d0c12a8SStefan Roesch 	struct io_napi_entry *e;
137*8d0c12a8SStefan Roesch 	bool (*loop_end)(void *, unsigned long) = NULL;
138*8d0c12a8SStefan Roesch 	bool is_stale = false;
139*8d0c12a8SStefan Roesch 
140*8d0c12a8SStefan Roesch 	if (loop_end_arg)
141*8d0c12a8SStefan Roesch 		loop_end = io_napi_busy_loop_should_end;
142*8d0c12a8SStefan Roesch 
143*8d0c12a8SStefan Roesch 	list_for_each_entry_rcu(e, &ctx->napi_list, list) {
144*8d0c12a8SStefan Roesch 		napi_busy_loop_rcu(e->napi_id, loop_end, loop_end_arg,
145*8d0c12a8SStefan Roesch 				   ctx->napi_prefer_busy_poll, BUSY_POLL_BUDGET);
146*8d0c12a8SStefan Roesch 
147*8d0c12a8SStefan Roesch 		if (time_after(jiffies, e->timeout))
148*8d0c12a8SStefan Roesch 			is_stale = true;
149*8d0c12a8SStefan Roesch 	}
150*8d0c12a8SStefan Roesch 
151*8d0c12a8SStefan Roesch 	return is_stale;
152*8d0c12a8SStefan Roesch }
153*8d0c12a8SStefan Roesch 
154*8d0c12a8SStefan Roesch static void io_napi_blocking_busy_loop(struct io_ring_ctx *ctx,
155*8d0c12a8SStefan Roesch 				       struct io_wait_queue *iowq)
156*8d0c12a8SStefan Roesch {
157*8d0c12a8SStefan Roesch 	unsigned long start_time = busy_loop_current_time();
158*8d0c12a8SStefan Roesch 	void *loop_end_arg = NULL;
159*8d0c12a8SStefan Roesch 	bool is_stale = false;
160*8d0c12a8SStefan Roesch 
161*8d0c12a8SStefan Roesch 	/* Singular lists use a different napi loop end check function and are
162*8d0c12a8SStefan Roesch 	 * only executed once.
163*8d0c12a8SStefan Roesch 	 */
164*8d0c12a8SStefan Roesch 	if (list_is_singular(&ctx->napi_list))
165*8d0c12a8SStefan Roesch 		loop_end_arg = iowq;
166*8d0c12a8SStefan Roesch 
167*8d0c12a8SStefan Roesch 	rcu_read_lock();
168*8d0c12a8SStefan Roesch 	do {
169*8d0c12a8SStefan Roesch 		is_stale = __io_napi_do_busy_loop(ctx, loop_end_arg);
170*8d0c12a8SStefan Roesch 	} while (!io_napi_busy_loop_should_end(iowq, start_time) && !loop_end_arg);
171*8d0c12a8SStefan Roesch 	rcu_read_unlock();
172*8d0c12a8SStefan Roesch 
173*8d0c12a8SStefan Roesch 	io_napi_remove_stale(ctx, is_stale);
174*8d0c12a8SStefan Roesch }
175*8d0c12a8SStefan Roesch 
176*8d0c12a8SStefan Roesch /*
177*8d0c12a8SStefan Roesch  * io_napi_init() - Init napi settings
178*8d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
179*8d0c12a8SStefan Roesch  *
180*8d0c12a8SStefan Roesch  * Init napi settings in the io-uring context.
181*8d0c12a8SStefan Roesch  */
182*8d0c12a8SStefan Roesch void io_napi_init(struct io_ring_ctx *ctx)
183*8d0c12a8SStefan Roesch {
184*8d0c12a8SStefan Roesch 	INIT_LIST_HEAD(&ctx->napi_list);
185*8d0c12a8SStefan Roesch 	spin_lock_init(&ctx->napi_lock);
186*8d0c12a8SStefan Roesch 	ctx->napi_prefer_busy_poll = false;
187*8d0c12a8SStefan Roesch 	ctx->napi_busy_poll_to = READ_ONCE(sysctl_net_busy_poll);
188*8d0c12a8SStefan Roesch }
189*8d0c12a8SStefan Roesch 
190*8d0c12a8SStefan Roesch /*
191*8d0c12a8SStefan Roesch  * io_napi_free() - Deallocate napi
192*8d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
193*8d0c12a8SStefan Roesch  *
194*8d0c12a8SStefan Roesch  * Free the napi list and the hash table in the io-uring context.
195*8d0c12a8SStefan Roesch  */
196*8d0c12a8SStefan Roesch void io_napi_free(struct io_ring_ctx *ctx)
197*8d0c12a8SStefan Roesch {
198*8d0c12a8SStefan Roesch 	struct io_napi_entry *e;
199*8d0c12a8SStefan Roesch 	LIST_HEAD(napi_list);
200*8d0c12a8SStefan Roesch 	unsigned int i;
201*8d0c12a8SStefan Roesch 
202*8d0c12a8SStefan Roesch 	spin_lock(&ctx->napi_lock);
203*8d0c12a8SStefan Roesch 	hash_for_each(ctx->napi_ht, i, e, node) {
204*8d0c12a8SStefan Roesch 		hash_del_rcu(&e->node);
205*8d0c12a8SStefan Roesch 		kfree_rcu(e, rcu);
206*8d0c12a8SStefan Roesch 	}
207*8d0c12a8SStefan Roesch 	spin_unlock(&ctx->napi_lock);
208*8d0c12a8SStefan Roesch }
209*8d0c12a8SStefan Roesch 
210*8d0c12a8SStefan Roesch /*
211*8d0c12a8SStefan Roesch  * __io_napi_adjust_timeout() - Add napi id to the busy poll list
212*8d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
213*8d0c12a8SStefan Roesch  * @iowq: pointer to io wait queue
214*8d0c12a8SStefan Roesch  * @ts: pointer to timespec or NULL
215*8d0c12a8SStefan Roesch  *
216*8d0c12a8SStefan Roesch  * Adjust the busy loop timeout according to timespec and busy poll timeout.
217*8d0c12a8SStefan Roesch  */
218*8d0c12a8SStefan Roesch void __io_napi_adjust_timeout(struct io_ring_ctx *ctx, struct io_wait_queue *iowq,
219*8d0c12a8SStefan Roesch 			      struct timespec64 *ts)
220*8d0c12a8SStefan Roesch {
221*8d0c12a8SStefan Roesch 	unsigned int poll_to = READ_ONCE(ctx->napi_busy_poll_to);
222*8d0c12a8SStefan Roesch 
223*8d0c12a8SStefan Roesch 	if (ts) {
224*8d0c12a8SStefan Roesch 		struct timespec64 poll_to_ts = ns_to_timespec64(1000 * (s64)poll_to);
225*8d0c12a8SStefan Roesch 
226*8d0c12a8SStefan Roesch 		if (timespec64_compare(ts, &poll_to_ts) > 0) {
227*8d0c12a8SStefan Roesch 			*ts = timespec64_sub(*ts, poll_to_ts);
228*8d0c12a8SStefan Roesch 		} else {
229*8d0c12a8SStefan Roesch 			u64 to = timespec64_to_ns(ts);
230*8d0c12a8SStefan Roesch 
231*8d0c12a8SStefan Roesch 			do_div(to, 1000);
232*8d0c12a8SStefan Roesch 			ts->tv_sec = 0;
233*8d0c12a8SStefan Roesch 			ts->tv_nsec = 0;
234*8d0c12a8SStefan Roesch 		}
235*8d0c12a8SStefan Roesch 	}
236*8d0c12a8SStefan Roesch 
237*8d0c12a8SStefan Roesch 	iowq->napi_busy_poll_to = poll_to;
238*8d0c12a8SStefan Roesch }
239*8d0c12a8SStefan Roesch 
240*8d0c12a8SStefan Roesch /*
241*8d0c12a8SStefan Roesch  * __io_napi_busy_loop() - execute busy poll loop
242*8d0c12a8SStefan Roesch  * @ctx: pointer to io-uring context structure
243*8d0c12a8SStefan Roesch  * @iowq: pointer to io wait queue
244*8d0c12a8SStefan Roesch  *
245*8d0c12a8SStefan Roesch  * Execute the busy poll loop and merge the spliced off list.
246*8d0c12a8SStefan Roesch  */
247*8d0c12a8SStefan Roesch void __io_napi_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq)
248*8d0c12a8SStefan Roesch {
249*8d0c12a8SStefan Roesch 	iowq->napi_prefer_busy_poll = READ_ONCE(ctx->napi_prefer_busy_poll);
250*8d0c12a8SStefan Roesch 
251*8d0c12a8SStefan Roesch 	if (!(ctx->flags & IORING_SETUP_SQPOLL) && iowq->napi_busy_poll_to)
252*8d0c12a8SStefan Roesch 		io_napi_blocking_busy_loop(ctx, iowq);
253*8d0c12a8SStefan Roesch }
254*8d0c12a8SStefan Roesch 
255*8d0c12a8SStefan Roesch #endif
256