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