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 } 120 121 /* 122 * To insert a TCB on the hpts you *must* be holding the 123 * INP_WLOCK(). The hpts insert code will then acqurire 124 * the hpts's lock and insert the TCB on the requested 125 * slot possibly waking up the hpts if you are requesting 126 * a time earlier than what the hpts is sleeping to (if 127 * the hpts is sleeping). You may check the inp->inp_in_hpts 128 * flag without the hpts lock. The hpts is the only one 129 * that will clear this flag holding only the hpts lock. This 130 * means that in your tcp_output() routine when you test for 131 * it to be 1 (so you wont call output) it may be transitioning 132 * to 0 (by the hpts). That will be fine since that will just 133 * mean an extra call to tcp_output that most likely will find 134 * the call you executed (when the mis-match occurred) will have 135 * put the TCB back on the hpts and it will return. If your 136 * call did not add it back to the hpts then you will either 137 * over-send or the cwnd will block you from sending more. 138 * 139 * Note you should also be holding the INP_WLOCK() when you 140 * call the remove from the hpts as well. Thoug usually 141 * you are either doing this from a timer, where you need 142 * that INP_WLOCK() or from destroying your TCB where again 143 * you should already have the INP_WLOCK(). 144 */ 145 uint32_t tcp_hpts_insert_diag(struct tcpcb *tp, uint32_t slot, int32_t line, 146 struct hpts_diag *diag); 147 #define tcp_hpts_insert(inp, slot) \ 148 tcp_hpts_insert_diag((inp), (slot), __LINE__, NULL) 149 150 void __tcp_set_hpts(struct tcpcb *tp, int32_t line); 151 #define tcp_set_hpts(a) __tcp_set_hpts(a, __LINE__) 152 153 void tcp_set_inp_to_drop(struct inpcb *inp, uint16_t reason); 154 155 void tcp_lro_hpts_init(void); 156 157 extern int32_t tcp_min_hptsi_time; 158 159 #endif /* _KERNEL */ 160 161 /* 162 * The following functions should also be available 163 * to userspace as well. 164 */ 165 static __inline uint32_t 166 tcp_tv_to_hptstick(const struct timeval *sv) 167 { 168 return ((sv->tv_sec * 100000) + (sv->tv_usec / HPTS_TICKS_PER_SLOT)); 169 } 170 171 static __inline uint32_t 172 tcp_tv_to_usectick(const struct timeval *sv) 173 { 174 return ((uint32_t) ((sv->tv_sec * HPTS_USEC_IN_SEC) + sv->tv_usec)); 175 } 176 177 static __inline uint32_t 178 tcp_tv_to_mssectick(const struct timeval *sv) 179 { 180 return ((uint32_t) ((sv->tv_sec * HPTS_MSEC_IN_SEC) + (sv->tv_usec/HPTS_USEC_IN_MSEC))); 181 } 182 183 static __inline uint64_t 184 tcp_tv_to_lusectick(const struct timeval *sv) 185 { 186 return ((uint64_t)((sv->tv_sec * HPTS_USEC_IN_SEC) + sv->tv_usec)); 187 } 188 189 #ifdef _KERNEL 190 191 extern int32_t tcp_min_hptsi_time; 192 193 static inline int32_t 194 get_hpts_min_sleep_time(void) 195 { 196 return (tcp_min_hptsi_time + HPTS_TICKS_PER_SLOT); 197 } 198 199 static __inline uint32_t 200 tcp_gethptstick(struct timeval *sv) 201 { 202 struct timeval tv; 203 204 if (sv == NULL) 205 sv = &tv; 206 microuptime(sv); 207 return (tcp_tv_to_hptstick(sv)); 208 } 209 210 static __inline uint32_t 211 tcp_get_usecs(struct timeval *tv) 212 { 213 struct timeval tvd; 214 215 if (tv == NULL) 216 tv = &tvd; 217 microuptime(tv); 218 return (tcp_tv_to_usectick(tv)); 219 } 220 221 #endif /* _KERNEL */ 222 #endif /* __tcp_hpts_h__ */ 223