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 26 #ifndef __tcp_hpts_h__ 27 #define __tcp_hpts_h__ 28 29 /* Number of useconds in a hpts tick */ 30 #define HPTS_TICKS_PER_SLOT 10 31 #define HPTS_MS_TO_SLOTS(x) ((x * 100) + 1) 32 #define HPTS_USEC_TO_SLOTS(x) ((x+9) /10) 33 #define HPTS_USEC_IN_SEC 1000000 34 #define HPTS_MSEC_IN_SEC 1000 35 #define HPTS_USEC_IN_MSEC 1000 36 37 struct hpts_diag { 38 uint32_t p_hpts_active; /* bbr->flex7 x */ 39 uint32_t p_nxt_slot; /* bbr->flex1 x */ 40 uint32_t p_cur_slot; /* bbr->flex2 x */ 41 uint32_t p_prev_slot; /* bbr->delivered */ 42 uint32_t p_runningslot; /* bbr->inflight */ 43 uint32_t slot_req; /* bbr->flex3 x */ 44 uint32_t inp_hptsslot; /* bbr->flex4 x */ 45 uint32_t slot_remaining; /* bbr->flex5 x */ 46 uint32_t have_slept; /* bbr->epoch x */ 47 uint32_t hpts_sleep_time; /* bbr->applimited x */ 48 uint32_t yet_to_sleep; /* bbr->lt_epoch x */ 49 uint32_t need_new_to; /* bbr->flex6 x */ 50 uint32_t wheel_slot; /* bbr->bw_inuse x */ 51 uint32_t maxslots; /* bbr->delRate x */ 52 uint32_t wheel_cts; /* bbr->rttProp x */ 53 int32_t co_ret; /* bbr->pkts_out x */ 54 uint32_t p_curtick; /* upper bbr->cur_del_rate */ 55 uint32_t p_lasttick; /* lower bbr->cur_del_rate */ 56 uint8_t p_on_min_sleep; /* bbr->flex8 x */ 57 }; 58 59 /* Magic flags to tell whats cooking on the pacing wheel */ 60 #define PACE_TMR_DELACK 0x01 /* Delayed ack timer running */ 61 #define PACE_TMR_RACK 0x02 /* RACK timer running */ 62 #define PACE_TMR_TLP 0x04 /* TLP timer running */ 63 #define PACE_TMR_RXT 0x08 /* Retransmit timer running */ 64 #define PACE_TMR_PERSIT 0x10 /* Persists timer running */ 65 #define PACE_TMR_KEEP 0x20 /* Keep alive timer running */ 66 #define PACE_PKT_OUTPUT 0x40 /* Output Packets being paced */ 67 #define PACE_TMR_MASK (PACE_TMR_KEEP|PACE_TMR_PERSIT|PACE_TMR_RXT|PACE_TMR_TLP|PACE_TMR_RACK|PACE_TMR_DELACK) 68 69 #define DEFAULT_CONNECTION_THESHOLD 100 70 71 /* 72 * When using the hpts, a TCP stack must make sure 73 * that once a INP_DROPPED flag is applied to a INP 74 * that it does not expect tcp_output() to ever be 75 * called by the hpts. The hpts will *not* call 76 * any output (or input) functions on a TCB that 77 * is in the DROPPED state. 78 * 79 * This implies final ACK's and RST's that might 80 * be sent when a TCB is still around must be 81 * sent from a routine like tcp_respond(). 82 */ 83 #define LOWEST_SLEEP_ALLOWED 50 84 #define DEFAULT_MIN_SLEEP 250 /* How many usec's is default for hpts sleep 85 * this determines min granularity of the 86 * hpts. If 1, granularity is 10useconds at 87 * the cost of more CPU (context switching). 88 * Note do not set this to 0. 89 */ 90 #define DYNAMIC_MIN_SLEEP DEFAULT_MIN_SLEEP 91 #define DYNAMIC_MAX_SLEEP 5000 /* 5ms */ 92 93 /* Thresholds for raising/lowering sleep */ 94 #define TICKS_INDICATE_MORE_SLEEP 100 /* This would be 1ms */ 95 #define TICKS_INDICATE_LESS_SLEEP 1000 /* This would indicate 10ms */ 96 /** 97 * 98 * Dynamic adjustment of sleeping times is done in "new" mode 99 * where we are depending on syscall returns and lro returns 100 * to push hpts forward mainly and the timer is only a backstop. 101 * 102 * When we are in the "new" mode i.e. conn_cnt > conn_cnt_thresh 103 * then we do a dynamic adjustment on the time we sleep. 104 * Our threshold is if the lateness of the first client served (in ticks) is 105 * greater than or equal too ticks_indicate_more_sleep (10ms 106 * or 10000 ticks). If we were that late, the actual sleep time 107 * is adjusted down by 50%. If the ticks_ran is less than 108 * ticks_indicate_more_sleep (100 ticks or 1000usecs). 109 * 110 */ 111 112 #ifdef _KERNEL 113 void tcp_hpts_init(struct tcpcb *); 114 void tcp_hpts_remove(struct tcpcb *); 115 static inline bool 116 tcp_in_hpts(struct tcpcb *tp) 117 { 118 return ((tp->t_in_hpts == IHPTS_ONQUEUE) || 119 ((tp->t_in_hpts == IHPTS_MOVING) && 120 (tp->t_hpts_slot != -1))); 121 } 122 123 /* 124 * To insert a TCB on the hpts you *must* be holding the 125 * INP_WLOCK(). The hpts insert code will then acqurire 126 * the hpts's lock and insert the TCB on the requested 127 * slot possibly waking up the hpts if you are requesting 128 * a time earlier than what the hpts is sleeping to (if 129 * the hpts is sleeping). You may check the inp->inp_in_hpts 130 * flag without the hpts lock. The hpts is the only one 131 * that will clear this flag holding only the hpts lock. This 132 * means that in your tcp_output() routine when you test for 133 * it to be 1 (so you wont call output) it may be transitioning 134 * to 0 (by the hpts). That will be fine since that will just 135 * mean an extra call to tcp_output that most likely will find 136 * the call you executed (when the mis-match occurred) will have 137 * put the TCB back on the hpts and it will return. If your 138 * call did not add it back to the hpts then you will either 139 * over-send or the cwnd will block you from sending more. 140 * 141 * Note you should also be holding the INP_WLOCK() when you 142 * call the remove from the hpts as well. Thoug usually 143 * you are either doing this from a timer, where you need 144 * that INP_WLOCK() or from destroying your TCB where again 145 * you should already have the INP_WLOCK(). 146 */ 147 uint32_t tcp_hpts_insert_diag(struct tcpcb *tp, uint32_t slot, int32_t line, 148 struct hpts_diag *diag); 149 #define tcp_hpts_insert(inp, slot) \ 150 tcp_hpts_insert_diag((inp), (slot), __LINE__, NULL) 151 152 void __tcp_set_hpts(struct tcpcb *tp, int32_t line); 153 #define tcp_set_hpts(a) __tcp_set_hpts(a, __LINE__) 154 155 void tcp_set_inp_to_drop(struct inpcb *inp, uint16_t reason); 156 157 void tcp_lro_hpts_init(void); 158 void tcp_lro_hpts_uninit(void); 159 160 extern int32_t tcp_min_hptsi_time; 161 162 #endif /* _KERNEL */ 163 164 /* 165 * The following functions should also be available 166 * to userspace as well. 167 */ 168 static __inline uint32_t 169 tcp_tv_to_hptstick(const struct timeval *sv) 170 { 171 return ((sv->tv_sec * 100000) + (sv->tv_usec / HPTS_TICKS_PER_SLOT)); 172 } 173 174 static __inline uint32_t 175 tcp_tv_to_usectick(const struct timeval *sv) 176 { 177 return ((uint32_t) ((sv->tv_sec * HPTS_USEC_IN_SEC) + sv->tv_usec)); 178 } 179 180 static __inline uint32_t 181 tcp_tv_to_mssectick(const struct timeval *sv) 182 { 183 return ((uint32_t) ((sv->tv_sec * HPTS_MSEC_IN_SEC) + (sv->tv_usec/HPTS_USEC_IN_MSEC))); 184 } 185 186 static __inline uint64_t 187 tcp_tv_to_lusectick(const struct timeval *sv) 188 { 189 return ((uint64_t)((sv->tv_sec * HPTS_USEC_IN_SEC) + sv->tv_usec)); 190 } 191 192 #ifdef _KERNEL 193 194 extern int32_t tcp_min_hptsi_time; 195 196 static inline int32_t 197 get_hpts_min_sleep_time(void) 198 { 199 return (tcp_min_hptsi_time + HPTS_TICKS_PER_SLOT); 200 } 201 202 static __inline uint32_t 203 tcp_gethptstick(struct timeval *sv) 204 { 205 struct timeval tv; 206 207 if (sv == NULL) 208 sv = &tv; 209 microuptime(sv); 210 return (tcp_tv_to_hptstick(sv)); 211 } 212 213 static __inline uint64_t 214 tcp_get_u64_usecs(struct timeval *tv) 215 { 216 struct timeval tvd; 217 218 if (tv == NULL) 219 tv = &tvd; 220 microuptime(tv); 221 return (tcp_tv_to_lusectick(tv)); 222 } 223 224 static __inline uint32_t 225 tcp_get_usecs(struct timeval *tv) 226 { 227 struct timeval tvd; 228 229 if (tv == NULL) 230 tv = &tvd; 231 microuptime(tv); 232 return (tcp_tv_to_usectick(tv)); 233 } 234 235 #endif /* _KERNEL */ 236 #endif /* __tcp_hpts_h__ */ 237