xref: /freebsd/sys/netinet/tcp_hpts.h (revision 8b959dd6a3921c35395bef4a6d7ad2426a3bd88e)
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_SLOT 10
48 #define HPTS_MS_TO_SLOTS(x) ((x * 100) + 1)
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 struct hpts_diag {
55 	uint32_t p_hpts_active; 	/* bbr->flex7 x */
56 	uint32_t p_nxt_slot;		/* bbr->flex1 x */
57 	uint32_t p_cur_slot;		/* bbr->flex2 x */
58 	uint32_t p_prev_slot;		/* bbr->delivered */
59 	uint32_t p_runningslot;		/* bbr->inflight */
60 	uint32_t slot_req;		/* bbr->flex3 x */
61 	uint32_t inp_hptsslot;		/* bbr->flex4 x */
62 	uint32_t slot_remaining;	/* bbr->flex5 x */
63 	uint32_t have_slept;		/* bbr->epoch x */
64 	uint32_t hpts_sleep_time;	/* bbr->applimited x */
65 	uint32_t yet_to_sleep;		/* bbr->lt_epoch x */
66 	uint32_t need_new_to;		/* bbr->flex6 x  */
67 	uint32_t wheel_slot;		/* bbr->bw_inuse x */
68 	uint32_t maxslots;		/* bbr->delRate x */
69 	uint32_t wheel_cts;		/* bbr->rttProp x */
70 	int32_t co_ret; 		/* bbr->pkts_out x */
71 	uint32_t p_curtick;		/* upper bbr->cur_del_rate */
72 	uint32_t p_lasttick;		/* lower bbr->cur_del_rate */
73 	uint8_t p_on_min_sleep; 	/* bbr->flex8 x */
74 };
75 
76 /* Magic flags to tell whats cooking on the pacing wheel */
77 #define PACE_TMR_DELACK 0x01	/* Delayed ack timer running */
78 #define PACE_TMR_RACK   0x02	/* RACK timer running */
79 #define PACE_TMR_TLP    0x04	/* TLP timer running */
80 #define PACE_TMR_RXT    0x08	/* Retransmit timer running */
81 #define PACE_TMR_PERSIT 0x10	/* Persists timer running */
82 #define PACE_TMR_KEEP   0x20	/* Keep alive timer running */
83 #define PACE_PKT_OUTPUT 0x40	/* Output Packets being paced */
84 #define PACE_TMR_MASK   (PACE_TMR_KEEP|PACE_TMR_PERSIT|PACE_TMR_RXT|PACE_TMR_TLP|PACE_TMR_RACK|PACE_TMR_DELACK)
85 
86 #define DEFAULT_CONNECTION_THESHOLD 100
87 
88 #ifdef _KERNEL
89 /* Each hpts has its own p_mtx which is used for locking */
90 struct tcp_hpts_entry {
91 	/* Cache line 0x00 */
92 	struct mtx p_mtx;	/* Mutex for hpts */
93 	struct timeval p_mysleep;	/* Our min sleep time */
94 	uint64_t syscall_cnt;
95 	uint64_t sleeping;	/* What the actual sleep was (if sleeping) */
96 	uint16_t p_hpts_active; /* Flag that says hpts is awake  */
97 	uint8_t p_wheel_complete; /* have we completed the wheel arc walk? */
98 	uint32_t p_curtick;	/* Tick in 10 us the hpts is going to */
99 	uint32_t p_runningslot; /* Current tick we are at if we are running */
100 	uint32_t p_prev_slot;	/* Previous slot we were on */
101 	uint32_t p_cur_slot;	/* Current slot in wheel hpts is draining */
102 	uint32_t p_nxt_slot;	/* The next slot outside the current range of
103 				 * slots that the hpts is running on. */
104 	int32_t p_on_queue_cnt;	/* Count on queue in this hpts */
105 	uint32_t p_lasttick;	/* Last tick before the current one */
106 	uint8_t p_direct_wake :1, /* boolean */
107 		p_on_min_sleep:1, /* boolean */
108 		p_hpts_wake_scheduled:1, /* boolean */
109 		p_avail:5;
110 	uint8_t p_fill[3];	  /* Fill to 32 bits */
111 	/* Cache line 0x40 */
112 	void *p_inp;
113 	struct hptsh p_input;	/* For the tcp-input runner */
114 	/* Hptsi wheel */
115 	struct hptsh *p_hptss;
116 	int32_t p_on_inqueue_cnt; /* Count on input queue in this hpts */
117 	uint32_t p_hpts_sleep_time;	/* Current sleep interval having a max
118 					 * of 255ms */
119 	uint32_t overidden_sleep;	/* what was overrided by min-sleep for logging */
120 	uint32_t saved_lasttick;	/* for logging */
121 	uint32_t saved_curtick;		/* for logging */
122 	uint32_t saved_curslot;		/* for logging */
123 	uint32_t saved_prev_slot;       /* for logging */
124 	uint32_t p_delayed_by;	/* How much were we delayed by */
125 	/* Cache line 0x80 */
126 	struct sysctl_ctx_list hpts_ctx;
127 	struct sysctl_oid *hpts_root;
128 	struct intr_event *ie;
129 	void *ie_cookie;
130 	uint16_t p_num;		/* The hpts number one per cpu */
131 	uint16_t p_cpu;		/* The hpts CPU */
132 	/* There is extra space in here */
133 	/* Cache line 0x100 */
134 	struct callout co __aligned(CACHE_LINE_SIZE);
135 }               __aligned(CACHE_LINE_SIZE);
136 
137 struct tcp_hptsi {
138 	struct proc *rp_proc;	/* Process structure for hpts */
139 	struct tcp_hpts_entry **rp_ent;	/* Array of hptss */
140 	uint32_t *cts_last_ran;
141 	uint32_t rp_num_hptss;	/* Number of hpts threads */
142 };
143 
144 #endif
145 
146 #define HPTS_REMOVE_INPUT  0x01
147 #define HPTS_REMOVE_OUTPUT 0x02
148 #define HPTS_REMOVE_ALL    (HPTS_REMOVE_INPUT | HPTS_REMOVE_OUTPUT)
149 
150 /*
151  * When using the hpts, a TCP stack must make sure
152  * that once a INP_DROPPED flag is applied to a INP
153  * that it does not expect tcp_output() to ever be
154  * called by the hpts. The hpts will *not* call
155  * any output (or input) functions on a TCB that
156  * is in the DROPPED state.
157  *
158  * This implies final ACK's and RST's that might
159  * be sent when a TCB is still around must be
160  * sent from a routine like tcp_respond().
161  */
162 #define LOWEST_SLEEP_ALLOWED 50
163 #define DEFAULT_MIN_SLEEP 250	/* How many usec's is default for hpts sleep
164 				 * this determines min granularity of the
165 				 * hpts. If 1, granularity is 10useconds at
166 				 * the cost of more CPU (context switching).
167 				 * Note do not set this to 0.
168 				 */
169 #define DYNAMIC_MIN_SLEEP DEFAULT_MIN_SLEEP
170 #define DYNAMIC_MAX_SLEEP 100000	/* 100ms */
171 /* No of connections when wee start aligning to the cpu from syscalls */
172 #define OLDEST_THRESHOLD 1200
173 /* Thresholds for raising/lowering sleep */
174 #define TICKS_INDICATE_MORE_SLEEP 100		/* This would be 1ms */
175 #define TICKS_INDICATE_LESS_SLEEP 1000		/* This would indicate 10ms */
176 /**
177  *
178  * Dynamic adjustment of sleeping times is done in "new" mode
179  * where we are depending on syscall returns and lro returns
180  * to push hpts forward mainly and the timer is only a backstop.
181  *
182  * When we are in the "new" mode i.e. conn_cnt > conn_cnt_thresh
183  * then we do a dynamic adjustment on the time we sleep.
184  * Our threshold is if the lateness of the first client served (in ticks) is
185  * greater than or equal too ticks_indicate_more_sleep (10ms
186  * or 10000 ticks). If we were that late, the actual sleep time
187  * is adjusted down by 50%. If the ticks_ran is less than
188  * ticks_indicate_more_sleep (100 ticks or 1000usecs).
189  *
190  */
191 
192 
193 #ifdef _KERNEL
194 #define HPTS_MTX_ASSERT(hpts) mtx_assert(&(hpts)->p_mtx, MA_OWNED)
195 struct tcp_hpts_entry *tcp_hpts_lock(struct inpcb *inp);
196 struct tcp_hpts_entry *tcp_input_lock(struct inpcb *inp);
197 int __tcp_queue_to_hpts_immediate(struct inpcb *inp, int32_t line);
198 #define tcp_queue_to_hpts_immediate(a)__tcp_queue_to_hpts_immediate(a, __LINE__)
199 
200 struct tcp_hpts_entry *tcp_cur_hpts(struct inpcb *inp);
201 #define tcp_hpts_remove(a, b) __tcp_hpts_remove(a, b, __LINE__)
202 void __tcp_hpts_remove(struct inpcb *inp, int32_t flags, int32_t line);
203 
204 /*
205  * To insert a TCB on the hpts you *must* be holding the
206  * INP_WLOCK(). The hpts insert code will then acqurire
207  * the hpts's lock and insert the TCB on the requested
208  * slot possibly waking up the hpts if you are requesting
209  * a time earlier than what the hpts is sleeping to (if
210  * the hpts is sleeping). You may check the inp->inp_in_hpts
211  * flag without the hpts lock. The hpts is the only one
212  * that will clear this flag holding only the hpts lock. This
213  * means that in your tcp_output() routine when you test for
214  * it to be 1 (so you wont call output) it may be transitioning
215  * to 0 (by the hpts). That will be fine since that will just
216  * mean an extra call to tcp_output that most likely will find
217  * the call you executed (when the mis-match occured) will have
218  * put the TCB back on the hpts and it will return. If your
219  * call did not add it back to the hpts then you will either
220  * over-send or the cwnd will block you from sending more.
221  *
222  * Note you should also be holding the INP_WLOCK() when you
223  * call the remove from the hpts as well. Thoug usually
224  * you are either doing this from a timer, where you need
225  * that INP_WLOCK() or from destroying your TCB where again
226  * you should already have the INP_WLOCK().
227  */
228 uint32_t __tcp_hpts_insert(struct inpcb *inp, uint32_t slot, int32_t line);
229 #define tcp_hpts_insert(a, b) __tcp_hpts_insert(a, b, __LINE__)
230 
231 uint32_t
232 tcp_hpts_insert_diag(struct inpcb *inp, uint32_t slot, int32_t line, struct hpts_diag *diag);
233 
234 int
235     __tcp_queue_to_input_locked(struct inpcb *inp, struct tcp_hpts_entry *hpts, int32_t line);
236 #define tcp_queue_to_input_locked(a, b) __tcp_queue_to_input_locked(a, b, __LINE__);
237 int
238 __tcp_queue_to_input(struct inpcb *inp, int32_t line);
239 #define tcp_queue_to_input(a) __tcp_queue_to_input(a, __LINE__)
240 
241 uint16_t tcp_hpts_delayedby(struct inpcb *inp);
242 
243 void __tcp_set_hpts(struct inpcb *inp, int32_t line);
244 #define tcp_set_hpts(a) __tcp_set_hpts(a, __LINE__)
245 
246 void __tcp_set_inp_to_drop(struct inpcb *inp, uint16_t reason, int32_t line);
247 #define tcp_set_inp_to_drop(a, b) __tcp_set_inp_to_drop(a, b, __LINE__)
248 
249 void tcp_run_hpts(void);
250 
251 uint16_t hpts_random_cpu(struct inpcb *inp);
252 
253 extern int32_t tcp_min_hptsi_time;
254 
255 #endif /* _KERNEL */
256 
257 /*
258  * The following functions should also be available
259  * to userspace as well.
260  */
261 static __inline uint32_t
262 tcp_tv_to_hptstick(const struct timeval *sv)
263 {
264 	return ((sv->tv_sec * 100000) + (sv->tv_usec / HPTS_TICKS_PER_SLOT));
265 }
266 
267 static __inline uint32_t
268 tcp_tv_to_usectick(const struct timeval *sv)
269 {
270 	return ((uint32_t) ((sv->tv_sec * HPTS_USEC_IN_SEC) + sv->tv_usec));
271 }
272 
273 static __inline uint32_t
274 tcp_tv_to_mssectick(const struct timeval *sv)
275 {
276 	return ((uint32_t) ((sv->tv_sec * HPTS_MSEC_IN_SEC) + (sv->tv_usec/HPTS_USEC_IN_MSEC)));
277 }
278 
279 static __inline uint64_t
280 tcp_tv_to_lusectick(const struct timeval *sv)
281 {
282 	return ((uint64_t)((sv->tv_sec * HPTS_USEC_IN_SEC) + sv->tv_usec));
283 }
284 
285 #ifdef _KERNEL
286 
287 static __inline void
288 tcp_hpts_unlock(struct tcp_hpts_entry *hpts)
289 {
290 	mtx_unlock(&hpts->p_mtx);
291 }
292 
293 static __inline uint32_t
294 tcp_gethptstick(struct timeval *sv)
295 {
296 	struct timeval tv;
297 
298 	if (sv == NULL)
299 		sv = &tv;
300 	microuptime(sv);
301 	return (tcp_tv_to_hptstick(sv));
302 }
303 
304 static __inline uint32_t
305 tcp_get_usecs(struct timeval *tv)
306 {
307 	struct timeval tvd;
308 
309 	if (tv == NULL)
310 		tv = &tvd;
311 	microuptime(tv);
312 	return (tcp_tv_to_usectick(tv));
313 }
314 
315 #endif /* _KERNEL */
316 #endif /* __tcp_hpts_h__ */
317