xref: /freebsd/sys/netinet/tcp_hpts.h (revision b9f654b163bce26de79705e77b872427c9f2afa1)
1 /*-
2  * Copyright (c) 2016-2018 Netflix, Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 
28 #ifndef __tcp_hpts_h__
29 #define __tcp_hpts_h__
30 
31 /*
32  * The hpts uses a 102400 wheel. The wheel
33  * defines the time in 10 usec increments (102400 x 10).
34  * This gives a range of 10usec - 1024ms to place
35  * an entry within. If the user requests more than
36  * 1.024 second, a remaineder is attached and the hpts
37  * when seeing the remainder will re-insert the
38  * inpcb forward in time from where it is until
39  * the remainder is zero.
40  */
41 
42 #define NUM_OF_HPTSI_SLOTS 102400
43 
44 TAILQ_HEAD(hptsh, inpcb);
45 
46 /* Number of useconds in a hpts tick */
47 #define HPTS_TICKS_PER_USEC 10
48 #define HPTS_MS_TO_SLOTS(x) (x * 100)
49 #define HPTS_USEC_TO_SLOTS(x) ((x+9) /10)
50 #define HPTS_USEC_IN_SEC 1000000
51 #define HPTS_MSEC_IN_SEC 1000
52 #define HPTS_USEC_IN_MSEC 1000
53 
54 #define DEFAULT_HPTS_LOG 3072
55 
56 /*
57  * Log flags consist of
58  *  7f      7f         1            1 bits
59  * p_cpu | p_num | INPUT_ACTIVE | HPTS_ACTIVE
60  *
61  * So for example cpu 10, number 10 would with
62  * input active would show up as:
63  * p_flags = 0001010 0001010 1 0
64  *  <or>
65  * p_flags = 0x142a
66  */
67 #define HPTS_HPTS_ACTIVE 0x01
68 #define HPTS_INPUT_ACTIVE 0x02
69 
70 #define HPTSLOG_IMMEDIATE	1
71 #define HPTSLOG_INSERT_NORMAL	2
72 #define HPTSLOG_INSERT_SLEEPER	3
73 #define HPTSLOG_SLEEP_AFTER	4
74 #define HPTSLOG_SLEEP_BEFORE	5
75 #define HPTSLOG_INSERTED	6
76 #define HPTSLOG_WAKEUP_HPTS	7
77 #define HPTSLOG_SETTORUN	8
78 #define HPTSLOG_HPTSI		9
79 #define HPTSLOG_TOLONG		10
80 #define HPTSLOG_AWAKENS	11
81 #define HPTSLOG_TIMESOUT	12
82 #define HPTSLOG_SLEEPSET	13
83 #define HPTSLOG_WAKEUP_INPUT	14
84 #define HPTSLOG_RESCHEDULE     15
85 #define HPTSLOG_AWAKE		16
86 #define HPTSLOG_INP_DONE	17
87 
88 struct hpts_log {
89 	struct inpcb *inp;
90 	int32_t event;
91 	uint32_t cts;
92 	int32_t line;
93 	uint32_t ticknow;
94 	uint32_t t_paceslot;
95 	uint32_t t_hptsreq;
96 	uint32_t p_curtick;
97 	uint32_t p_prevtick;
98 	uint32_t slot_req;
99 	uint32_t p_on_queue_cnt;
100 	uint32_t p_nxt_slot;
101 	uint32_t p_cur_slot;
102 	uint32_t p_hpts_sleep_time;
103 	uint16_t p_flags;
104 	uint8_t p_onhpts;
105 	uint8_t p_oninput;
106 	uint8_t is_notempty;
107 };
108 
109 struct hpts_diag {
110 	uint32_t p_hpts_active;
111 	uint32_t p_nxt_slot;
112 	uint32_t p_cur_slot;
113 	uint32_t slot_req;
114 	uint32_t inp_hptsslot;
115 	uint32_t slot_now;
116 	uint32_t have_slept;
117 	uint32_t hpts_sleep_time;
118 	uint32_t yet_to_sleep;
119 	uint32_t need_new_to;
120 	int32_t co_ret;
121 	uint8_t p_on_min_sleep;
122 };
123 
124 #ifdef _KERNEL
125 /* Each hpts has its own p_mtx which is used for locking */
126 struct tcp_hpts_entry {
127 	/* Cache line 0x00 */
128 	struct mtx p_mtx;	/* Mutex for hpts */
129 	uint32_t p_hpts_active; /* Flag that says hpts is awake  */
130 	uint32_t p_curtick;	/* Current tick in 10 us the hpts is at */
131 	uint32_t p_prevtick;	/* Previous tick in 10 us the hpts ran */
132 	uint32_t p_cur_slot;	/* Current slot in wheel hpts is draining */
133 	uint32_t p_nxt_slot;	/* The next slot outside the current range of
134 				 * slots that the hpts is running on. */
135 	int32_t p_on_queue_cnt;	/* Count on queue in this hpts */
136 	uint32_t enobuf_cnt;
137 	uint16_t p_log_at;
138 	uint8_t p_direct_wake :1, /* boolean */
139 		p_log_wrapped :1, /* boolean */
140 		p_on_min_sleep:1; /* boolean */
141 	uint8_t p_fill;
142 	/* Cache line 0x40 */
143 	void *p_inp;
144 	struct hptsh p_input;	/* For the tcp-input runner */
145 	/* Hptsi wheel */
146 	struct hptsh *p_hptss;
147 	struct hpts_log *p_log;
148 	uint32_t p_logsize;
149 	int32_t p_on_inqueue_cnt; /* Count on input queue in this hpts */
150 	uint32_t hit_no_enobuf;
151 	uint32_t p_dyn_adjust;
152 	uint32_t p_hpts_sleep_time;	/* Current sleep interval having a max
153 					 * of 255ms */
154 	uint32_t p_delayed_by;	/* How much were we delayed by */
155 	/* Cache line 0x80 */
156 	struct sysctl_ctx_list hpts_ctx;
157 	struct sysctl_oid *hpts_root;
158 	struct intr_event *ie;
159 	void *ie_cookie;
160 	uint16_t p_num;		/* The hpts number one per cpu */
161 	uint16_t p_cpu;		/* The hpts CPU */
162 	/* There is extra space in here */
163 	/* Cache line 0x100 */
164 	struct callout co __aligned(CACHE_LINE_SIZE);
165 }               __aligned(CACHE_LINE_SIZE);
166 
167 struct tcp_hptsi {
168 	struct proc *rp_proc;	/* Process structure for hpts */
169 	struct tcp_hpts_entry **rp_ent;	/* Array of hptss */
170 	uint32_t rp_num_hptss;	/* Number of hpts threads */
171 };
172 
173 #endif
174 
175 #define HPTS_REMOVE_INPUT  0x01
176 #define HPTS_REMOVE_OUTPUT 0x02
177 #define HPTS_REMOVE_ALL    (HPTS_REMOVE_INPUT | HPTS_REMOVE_OUTPUT)
178 
179 /*
180  * When using the hpts, a TCP stack must make sure
181  * that once a INP_DROPPED flag is applied to a INP
182  * that it does not expect tcp_output() to ever be
183  * called by the hpts. The hpts will *not* call
184  * any output (or input) functions on a TCB that
185  * is in the DROPPED state.
186  *
187  * This implies final ACK's and RST's that might
188  * be sent when a TCB is still around must be
189  * sent from a routine like tcp_respond().
190  */
191 #define DEFAULT_MIN_SLEEP 250	/* How many usec's is default for hpts sleep
192 				 * this determines min granularity of the
193 				 * hpts. If 0, granularity is 10useconds at
194 				 * the cost of more CPU (context switching). */
195 #ifdef _KERNEL
196 #define HPTS_MTX_ASSERT(hpts) mtx_assert(&(hpts)->p_mtx, MA_OWNED)
197 struct tcp_hpts_entry *tcp_hpts_lock(struct inpcb *inp);
198 struct tcp_hpts_entry *tcp_input_lock(struct inpcb *inp);
199 int __tcp_queue_to_hpts_immediate(struct inpcb *inp, int32_t line);
200 #define tcp_queue_to_hpts_immediate(a)__tcp_queue_to_hpts_immediate(a, __LINE__)
201 
202 struct tcp_hpts_entry *tcp_cur_hpts(struct inpcb *inp);
203 #define tcp_hpts_remove(a, b) __tcp_hpts_remove(a, b, __LINE__)
204 void __tcp_hpts_remove(struct inpcb *inp, int32_t flags, int32_t line);
205 
206 /*
207  * To insert a TCB on the hpts you *must* be holding the
208  * INP_WLOCK(). The hpts insert code will then acqurire
209  * the hpts's lock and insert the TCB on the requested
210  * slot possibly waking up the hpts if you are requesting
211  * a time earlier than what the hpts is sleeping to (if
212  * the hpts is sleeping). You may check the inp->inp_in_hpts
213  * flag without the hpts lock. The hpts is the only one
214  * that will clear this flag holding only the hpts lock. This
215  * means that in your tcp_output() routine when you test for
216  * it to be 1 (so you wont call output) it may be transitioning
217  * to 0 (by the hpts). That will be fine since that will just
218  * mean an extra call to tcp_output that most likely will find
219  * the call you executed (when the mis-match occured) will have
220  * put the TCB back on the hpts and it will return. If your
221  * call did not add it back to the hpts then you will either
222  * over-send or the cwnd will block you from sending more.
223  *
224  * Note you should also be holding the INP_WLOCK() when you
225  * call the remove from the hpts as well. Thoug usually
226  * you are either doing this from a timer, where you need
227  * that INP_WLOCK() or from destroying your TCB where again
228  * you should already have the INP_WLOCK().
229  */
230 uint32_t __tcp_hpts_insert(struct inpcb *inp, uint32_t slot, int32_t line);
231 #define tcp_hpts_insert(a, b) __tcp_hpts_insert(a, b, __LINE__)
232 
233 uint32_t
234 tcp_hpts_insert_diag(struct inpcb *inp, uint32_t slot, int32_t line, struct hpts_diag *diag);
235 
236 int
237     __tcp_queue_to_input_locked(struct inpcb *inp, struct tcp_hpts_entry *hpts, int32_t line);
238 #define tcp_queue_to_input_locked(a, b) __tcp_queue_to_input_locked(a, b, __LINE__);
239 void
240 tcp_queue_pkt_to_input(struct tcpcb *tp, struct mbuf *m, struct tcphdr *th,
241     int32_t tlen, int32_t drop_hdrlen, uint8_t iptos);
242 int
243 __tcp_queue_to_input(struct tcpcb *tp, struct mbuf *m, struct tcphdr *th,
244     int32_t tlen, int32_t drop_hdrlen, uint8_t iptos, int32_t line);
245 #define tcp_queue_to_input(a, b, c, d, e, f, g) __tcp_queue_to_input(a, b, c, d, e, f, g, __LINE__)
246 
247 uint16_t tcp_hpts_delayedby(struct inpcb *inp);
248 
249 void __tcp_set_hpts(struct inpcb *inp, int32_t line);
250 #define tcp_set_hpts(a) __tcp_set_hpts(a, __LINE__)
251 
252 void __tcp_set_inp_to_drop(struct inpcb *inp, uint16_t reason, int32_t line);
253 #define tcp_set_inp_to_drop(a, b) __tcp_set_inp_to_drop(a, b, __LINE__)
254 
255 extern int32_t tcp_min_hptsi_time;
256 
257 static __inline uint32_t
258 tcp_tv_to_hptstick(struct timeval *sv)
259 {
260 	return ((sv->tv_sec * 100000) + (sv->tv_usec / 10));
261 }
262 
263 static __inline uint32_t
264 tcp_gethptstick(struct timeval *sv)
265 {
266 	struct timeval tv;
267 
268 	if (sv == NULL)
269 		sv = &tv;
270 	microuptime(sv);
271 	return (tcp_tv_to_hptstick(sv));
272 }
273 
274 static __inline uint32_t
275 tcp_tv_to_usectick(struct timeval *sv)
276 {
277 	return ((uint32_t) ((sv->tv_sec * HPTS_USEC_IN_SEC) + sv->tv_usec));
278 }
279 
280 static __inline uint32_t
281 tcp_tv_to_mssectick(struct timeval *sv)
282 {
283 	return ((uint32_t) ((sv->tv_sec * HPTS_MSEC_IN_SEC) + (sv->tv_usec/HPTS_USEC_IN_MSEC)));
284 }
285 
286 static __inline void
287 tcp_hpts_unlock(struct tcp_hpts_entry *hpts)
288 {
289 	mtx_unlock(&hpts->p_mtx);
290 }
291 
292 static __inline uint32_t
293 tcp_get_usecs(struct timeval *tv)
294 {
295 	struct timeval tvd;
296 
297 	if (tv == NULL)
298 		tv = &tvd;
299 	microuptime(tv);
300 	return (tcp_tv_to_usectick(tv));
301 }
302 
303 #endif /* _KERNEL */
304 #endif /* __tcp_hpts_h__ */
305