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
44TAILQ_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) + 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
54struct 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_runningtick;		/* 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_tick;		/* bbr->bw_inuse x */
68	uint32_t maxticks;		/* 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#ifdef _KERNEL
87/* Each hpts has its own p_mtx which is used for locking */
88struct tcp_hpts_entry {
89	/* Cache line 0x00 */
90	struct mtx p_mtx;	/* Mutex for hpts */
91	uint16_t p_hpts_active; /* Flag that says hpts is awake  */
92	uint8_t p_hpts_wake_scheduled;	/* Have we scheduled a wakeup? */
93	uint8_t p_wheel_complete; /* have we completed the wheel arc walk? */
94	uint32_t p_curtick;	/* Tick in 10 us the hpts is going to */
95	uint32_t p_runningtick; /* Current tick we are at if we are running */
96	uint32_t p_prev_slot;	/* Previous slot we were on */
97	uint32_t p_cur_slot;	/* Current slot in wheel hpts is draining */
98	uint32_t p_nxt_slot;	/* The next slot outside the current range of
99				 * slots that the hpts is running on. */
100	int32_t p_on_queue_cnt;	/* Count on queue in this hpts */
101	uint32_t p_lasttick;	/* Last tick before the current one */
102	uint8_t p_direct_wake :1, /* boolean */
103		p_on_min_sleep:1, /* boolean */
104		p_avail:6;
105	uint8_t p_fill[3];	  /* Fill to 32 bits */
106	/* Cache line 0x40 */
107	void *p_inp;
108	struct hptsh p_input;	/* For the tcp-input runner */
109	/* Hptsi wheel */
110	struct hptsh *p_hptss;
111	int32_t p_on_inqueue_cnt; /* Count on input queue in this hpts */
112	uint32_t hit_no_enobuf;
113	uint32_t p_dyn_adjust;
114	uint32_t p_hpts_sleep_time;	/* Current sleep interval having a max
115					 * of 255ms */
116	uint32_t overidden_sleep;	/* what was overrided by min-sleep for logging */
117	uint32_t saved_lasttick;	/* for logging */
118	uint32_t saved_curtick;		/* for logging */
119	uint32_t saved_curslot;		/* for logging */
120	uint32_t saved_prev_slot;       /* for logging */
121	uint32_t p_delayed_by;	/* How much were we delayed by */
122	/* Cache line 0x80 */
123	struct sysctl_ctx_list hpts_ctx;
124	struct sysctl_oid *hpts_root;
125	struct intr_event *ie;
126	void *ie_cookie;
127	uint16_t p_num;		/* The hpts number one per cpu */
128	uint16_t p_cpu;		/* The hpts CPU */
129	/* There is extra space in here */
130	/* Cache line 0x100 */
131	struct callout co __aligned(CACHE_LINE_SIZE);
132}               __aligned(CACHE_LINE_SIZE);
133
134struct tcp_hptsi {
135	struct proc *rp_proc;	/* Process structure for hpts */
136	struct tcp_hpts_entry **rp_ent;	/* Array of hptss */
137	uint32_t rp_num_hptss;	/* Number of hpts threads */
138};
139
140#endif
141
142#define HPTS_REMOVE_INPUT  0x01
143#define HPTS_REMOVE_OUTPUT 0x02
144#define HPTS_REMOVE_ALL    (HPTS_REMOVE_INPUT | HPTS_REMOVE_OUTPUT)
145
146/*
147 * When using the hpts, a TCP stack must make sure
148 * that once a INP_DROPPED flag is applied to a INP
149 * that it does not expect tcp_output() to ever be
150 * called by the hpts. The hpts will *not* call
151 * any output (or input) functions on a TCB that
152 * is in the DROPPED state.
153 *
154 * This implies final ACK's and RST's that might
155 * be sent when a TCB is still around must be
156 * sent from a routine like tcp_respond().
157 */
158#define DEFAULT_MIN_SLEEP 250	/* How many usec's is default for hpts sleep
159				 * this determines min granularity of the
160				 * hpts. If 0, granularity is 10useconds at
161				 * the cost of more CPU (context switching). */
162#ifdef _KERNEL
163#define HPTS_MTX_ASSERT(hpts) mtx_assert(&(hpts)->p_mtx, MA_OWNED)
164struct tcp_hpts_entry *tcp_hpts_lock(struct inpcb *inp);
165struct tcp_hpts_entry *tcp_input_lock(struct inpcb *inp);
166int __tcp_queue_to_hpts_immediate(struct inpcb *inp, int32_t line);
167#define tcp_queue_to_hpts_immediate(a)__tcp_queue_to_hpts_immediate(a, __LINE__)
168
169struct tcp_hpts_entry *tcp_cur_hpts(struct inpcb *inp);
170#define tcp_hpts_remove(a, b) __tcp_hpts_remove(a, b, __LINE__)
171void __tcp_hpts_remove(struct inpcb *inp, int32_t flags, int32_t line);
172
173/*
174 * To insert a TCB on the hpts you *must* be holding the
175 * INP_WLOCK(). The hpts insert code will then acqurire
176 * the hpts's lock and insert the TCB on the requested
177 * slot possibly waking up the hpts if you are requesting
178 * a time earlier than what the hpts is sleeping to (if
179 * the hpts is sleeping). You may check the inp->inp_in_hpts
180 * flag without the hpts lock. The hpts is the only one
181 * that will clear this flag holding only the hpts lock. This
182 * means that in your tcp_output() routine when you test for
183 * it to be 1 (so you wont call output) it may be transitioning
184 * to 0 (by the hpts). That will be fine since that will just
185 * mean an extra call to tcp_output that most likely will find
186 * the call you executed (when the mis-match occured) will have
187 * put the TCB back on the hpts and it will return. If your
188 * call did not add it back to the hpts then you will either
189 * over-send or the cwnd will block you from sending more.
190 *
191 * Note you should also be holding the INP_WLOCK() when you
192 * call the remove from the hpts as well. Thoug usually
193 * you are either doing this from a timer, where you need
194 * that INP_WLOCK() or from destroying your TCB where again
195 * you should already have the INP_WLOCK().
196 */
197uint32_t __tcp_hpts_insert(struct inpcb *inp, uint32_t slot, int32_t line);
198#define tcp_hpts_insert(a, b) __tcp_hpts_insert(a, b, __LINE__)
199
200uint32_t
201tcp_hpts_insert_diag(struct inpcb *inp, uint32_t slot, int32_t line, struct hpts_diag *diag);
202
203int
204    __tcp_queue_to_input_locked(struct inpcb *inp, struct tcp_hpts_entry *hpts, int32_t line);
205#define tcp_queue_to_input_locked(a, b) __tcp_queue_to_input_locked(a, b, __LINE__);
206int
207__tcp_queue_to_input(struct inpcb *inp, int32_t line);
208#define tcp_queue_to_input(a) __tcp_queue_to_input(a, __LINE__)
209
210uint16_t tcp_hpts_delayedby(struct inpcb *inp);
211
212void __tcp_set_hpts(struct inpcb *inp, int32_t line);
213#define tcp_set_hpts(a) __tcp_set_hpts(a, __LINE__)
214
215void __tcp_set_inp_to_drop(struct inpcb *inp, uint16_t reason, int32_t line);
216#define tcp_set_inp_to_drop(a, b) __tcp_set_inp_to_drop(a, b, __LINE__)
217
218extern int32_t tcp_min_hptsi_time;
219
220static __inline uint32_t
221tcp_tv_to_hptstick(struct timeval *sv)
222{
223	return ((sv->tv_sec * 100000) + (sv->tv_usec / 10));
224}
225
226static __inline uint32_t
227tcp_gethptstick(struct timeval *sv)
228{
229	struct timeval tv;
230
231	if (sv == NULL)
232		sv = &tv;
233	microuptime(sv);
234	return (tcp_tv_to_hptstick(sv));
235}
236
237static __inline uint32_t
238tcp_tv_to_usectick(struct timeval *sv)
239{
240	return ((uint32_t) ((sv->tv_sec * HPTS_USEC_IN_SEC) + sv->tv_usec));
241}
242
243static __inline uint32_t
244tcp_tv_to_mssectick(struct timeval *sv)
245{
246	return ((uint32_t) ((sv->tv_sec * HPTS_MSEC_IN_SEC) + (sv->tv_usec/HPTS_USEC_IN_MSEC)));
247}
248
249static __inline void
250tcp_hpts_unlock(struct tcp_hpts_entry *hpts)
251{
252	mtx_unlock(&hpts->p_mtx);
253}
254
255static __inline uint32_t
256tcp_get_usecs(struct timeval *tv)
257{
258	struct timeval tvd;
259
260	if (tv == NULL)
261		tv = &tvd;
262	microuptime(tv);
263	return (tcp_tv_to_usectick(tv));
264}
265
266#endif /* _KERNEL */
267#endif /* __tcp_hpts_h__ */
268