1*76404edcSAsim Jamshed #include "timer.h" 2*76404edcSAsim Jamshed #include "tcp_in.h" 3*76404edcSAsim Jamshed #include "tcp_out.h" 4*76404edcSAsim Jamshed #include "stat.h" 5*76404edcSAsim Jamshed #include "debug.h" 6*76404edcSAsim Jamshed #include "memory_mgt.h" 7*76404edcSAsim Jamshed #include "config.h" 8*76404edcSAsim Jamshed 9*76404edcSAsim Jamshed #define MAX(a, b) ((a)>(b)?(a):(b)) 10*76404edcSAsim Jamshed #define MIN(a, b) ((a)<(b)?(a):(b)) 11*76404edcSAsim Jamshed 12*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 13*76404edcSAsim Jamshed struct rto_hashstore* 14*76404edcSAsim Jamshed InitRTOHashstore() 15*76404edcSAsim Jamshed { 16*76404edcSAsim Jamshed int i; 17*76404edcSAsim Jamshed struct rto_hashstore* hs = calloc(1, sizeof(struct rto_hashstore)); 18*76404edcSAsim Jamshed if (!hs) { 19*76404edcSAsim Jamshed TRACE_ERROR("calloc: InitHashStore"); 20*76404edcSAsim Jamshed return 0; 21*76404edcSAsim Jamshed } 22*76404edcSAsim Jamshed 23*76404edcSAsim Jamshed for (i = 0; i < RTO_HASH; i++) 24*76404edcSAsim Jamshed TAILQ_INIT(&hs->rto_list[i]); 25*76404edcSAsim Jamshed 26*76404edcSAsim Jamshed TAILQ_INIT(&hs->rto_list[RTO_HASH]); 27*76404edcSAsim Jamshed 28*76404edcSAsim Jamshed return hs; 29*76404edcSAsim Jamshed } 30*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 31*76404edcSAsim Jamshed inline void 32*76404edcSAsim Jamshed AddtoRTOList(mtcp_manager_t mtcp, tcp_stream *cur_stream) 33*76404edcSAsim Jamshed { 34*76404edcSAsim Jamshed if (!mtcp->rto_list_cnt) { 35*76404edcSAsim Jamshed mtcp->rto_store->rto_now_idx = 0; 36*76404edcSAsim Jamshed mtcp->rto_store->rto_now_ts = cur_stream->sndvar->ts_rto; 37*76404edcSAsim Jamshed } 38*76404edcSAsim Jamshed 39*76404edcSAsim Jamshed if (cur_stream->on_rto_idx < 0 ) { 40*76404edcSAsim Jamshed if (cur_stream->on_timewait_list) { 41*76404edcSAsim Jamshed TRACE_ERROR("Stream %u: cannot be in both " 42*76404edcSAsim Jamshed "rto and timewait list.\n", cur_stream->id); 43*76404edcSAsim Jamshed #ifdef DUMP_STREAM 44*76404edcSAsim Jamshed DumpStream(mtcp, cur_stream); 45*76404edcSAsim Jamshed #endif 46*76404edcSAsim Jamshed return; 47*76404edcSAsim Jamshed } 48*76404edcSAsim Jamshed 49*76404edcSAsim Jamshed int diff = (int32_t)(cur_stream->sndvar->ts_rto - mtcp->rto_store->rto_now_ts); 50*76404edcSAsim Jamshed #if 0 51*76404edcSAsim Jamshed if (diff < RTO_HASH) { 52*76404edcSAsim Jamshed #else 53*76404edcSAsim Jamshed int offset= ((diff + mtcp->rto_store->rto_now_idx) & (RTO_HASH - 1)); 54*76404edcSAsim Jamshed cur_stream->on_rto_idx = offset; 55*76404edcSAsim Jamshed TAILQ_INSERT_TAIL(&(mtcp->rto_store->rto_list[offset]), 56*76404edcSAsim Jamshed cur_stream, sndvar->timer_link); 57*76404edcSAsim Jamshed #endif 58*76404edcSAsim Jamshed #if 0 59*76404edcSAsim Jamshed } else { 60*76404edcSAsim Jamshed cur_stream->on_rto_idx = RTO_HASH; 61*76404edcSAsim Jamshed TAILQ_INSERT_TAIL(&(mtcp->rto_store->rto_list[RTO_HASH]), 62*76404edcSAsim Jamshed cur_stream, sndvar->timer_link); 63*76404edcSAsim Jamshed } 64*76404edcSAsim Jamshed #endif 65*76404edcSAsim Jamshed mtcp->rto_list_cnt++; 66*76404edcSAsim Jamshed } 67*76404edcSAsim Jamshed } 68*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 69*76404edcSAsim Jamshed inline void 70*76404edcSAsim Jamshed RemoveFromRTOList(mtcp_manager_t mtcp, tcp_stream *cur_stream) 71*76404edcSAsim Jamshed { 72*76404edcSAsim Jamshed if (cur_stream->on_rto_idx < 0) { 73*76404edcSAsim Jamshed // assert(0); 74*76404edcSAsim Jamshed return; 75*76404edcSAsim Jamshed } 76*76404edcSAsim Jamshed 77*76404edcSAsim Jamshed TAILQ_REMOVE(&mtcp->rto_store->rto_list[cur_stream->on_rto_idx], 78*76404edcSAsim Jamshed cur_stream, sndvar->timer_link); 79*76404edcSAsim Jamshed cur_stream->on_rto_idx = -1; 80*76404edcSAsim Jamshed 81*76404edcSAsim Jamshed mtcp->rto_list_cnt--; 82*76404edcSAsim Jamshed } 83*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 84*76404edcSAsim Jamshed inline void 85*76404edcSAsim Jamshed AddtoTimewaitList(mtcp_manager_t mtcp, tcp_stream *cur_stream, uint32_t cur_ts) 86*76404edcSAsim Jamshed { 87*76404edcSAsim Jamshed cur_stream->rcvvar->ts_tw_expire = cur_ts + g_config.mos->tcp_tw_interval; 88*76404edcSAsim Jamshed 89*76404edcSAsim Jamshed if (cur_stream->on_timewait_list) { 90*76404edcSAsim Jamshed // Update list in sorted way by ts_tw_expire 91*76404edcSAsim Jamshed TAILQ_REMOVE(&mtcp->timewait_list, cur_stream, sndvar->timer_link); 92*76404edcSAsim Jamshed TAILQ_INSERT_TAIL(&mtcp->timewait_list, cur_stream, sndvar->timer_link); 93*76404edcSAsim Jamshed } else { 94*76404edcSAsim Jamshed if (cur_stream->on_rto_idx >= 0) { 95*76404edcSAsim Jamshed TRACE_DBG("Stream %u: cannot be in both " 96*76404edcSAsim Jamshed "timewait and rto list.\n", cur_stream->id); 97*76404edcSAsim Jamshed //assert(0); 98*76404edcSAsim Jamshed #ifdef DUMP_STREAM 99*76404edcSAsim Jamshed DumpStream(mtcp, cur_stream); 100*76404edcSAsim Jamshed #endif 101*76404edcSAsim Jamshed RemoveFromRTOList(mtcp, cur_stream); 102*76404edcSAsim Jamshed } 103*76404edcSAsim Jamshed 104*76404edcSAsim Jamshed cur_stream->on_timewait_list = TRUE; 105*76404edcSAsim Jamshed TAILQ_INSERT_TAIL(&mtcp->timewait_list, cur_stream, sndvar->timer_link); 106*76404edcSAsim Jamshed mtcp->timewait_list_cnt++; 107*76404edcSAsim Jamshed } 108*76404edcSAsim Jamshed } 109*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 110*76404edcSAsim Jamshed inline void 111*76404edcSAsim Jamshed RemoveFromTimewaitList(mtcp_manager_t mtcp, tcp_stream *cur_stream) 112*76404edcSAsim Jamshed { 113*76404edcSAsim Jamshed if (!cur_stream->on_timewait_list) { 114*76404edcSAsim Jamshed //assert(0); 115*76404edcSAsim Jamshed return; 116*76404edcSAsim Jamshed } 117*76404edcSAsim Jamshed 118*76404edcSAsim Jamshed TAILQ_REMOVE(&mtcp->timewait_list, cur_stream, sndvar->timer_link); 119*76404edcSAsim Jamshed cur_stream->on_timewait_list = FALSE; 120*76404edcSAsim Jamshed mtcp->timewait_list_cnt--; 121*76404edcSAsim Jamshed } 122*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 123*76404edcSAsim Jamshed inline void 124*76404edcSAsim Jamshed AddtoTimeoutList(mtcp_manager_t mtcp, tcp_stream *cur_stream) 125*76404edcSAsim Jamshed { 126*76404edcSAsim Jamshed if (cur_stream->on_timeout_list) { 127*76404edcSAsim Jamshed //assert(0); 128*76404edcSAsim Jamshed return; 129*76404edcSAsim Jamshed } 130*76404edcSAsim Jamshed 131*76404edcSAsim Jamshed cur_stream->on_timeout_list = TRUE; 132*76404edcSAsim Jamshed TAILQ_INSERT_TAIL(&mtcp->timeout_list, cur_stream, sndvar->timeout_link); 133*76404edcSAsim Jamshed mtcp->timeout_list_cnt++; 134*76404edcSAsim Jamshed } 135*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 136*76404edcSAsim Jamshed inline void 137*76404edcSAsim Jamshed RemoveFromTimeoutList(mtcp_manager_t mtcp, tcp_stream *cur_stream) 138*76404edcSAsim Jamshed { 139*76404edcSAsim Jamshed if (cur_stream->on_timeout_list) { 140*76404edcSAsim Jamshed cur_stream->on_timeout_list = FALSE; 141*76404edcSAsim Jamshed TAILQ_REMOVE(&mtcp->timeout_list, cur_stream, sndvar->timeout_link); 142*76404edcSAsim Jamshed mtcp->timeout_list_cnt--; 143*76404edcSAsim Jamshed } 144*76404edcSAsim Jamshed } 145*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 146*76404edcSAsim Jamshed inline void 147*76404edcSAsim Jamshed UpdateTimeoutList(mtcp_manager_t mtcp, tcp_stream *cur_stream) 148*76404edcSAsim Jamshed { 149*76404edcSAsim Jamshed if (cur_stream->on_timeout_list) { 150*76404edcSAsim Jamshed TAILQ_REMOVE(&mtcp->timeout_list, cur_stream, sndvar->timeout_link); 151*76404edcSAsim Jamshed TAILQ_INSERT_TAIL(&mtcp->timeout_list, cur_stream, sndvar->timeout_link); 152*76404edcSAsim Jamshed } 153*76404edcSAsim Jamshed } 154*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 155*76404edcSAsim Jamshed inline void 156*76404edcSAsim Jamshed UpdateRetransmissionTimer(mtcp_manager_t mtcp, 157*76404edcSAsim Jamshed tcp_stream *cur_stream, uint32_t cur_ts) 158*76404edcSAsim Jamshed { 159*76404edcSAsim Jamshed /* Update the retransmission timer */ 160*76404edcSAsim Jamshed assert(cur_stream->sndvar->rto > 0); 161*76404edcSAsim Jamshed cur_stream->sndvar->nrtx = 0; 162*76404edcSAsim Jamshed 163*76404edcSAsim Jamshed /* if in rto list, remove it */ 164*76404edcSAsim Jamshed if (cur_stream->on_rto_idx >= 0) { 165*76404edcSAsim Jamshed RemoveFromRTOList(mtcp, cur_stream); 166*76404edcSAsim Jamshed } 167*76404edcSAsim Jamshed 168*76404edcSAsim Jamshed /* Reset retransmission timeout */ 169*76404edcSAsim Jamshed if (TCP_SEQ_GT(cur_stream->snd_nxt, cur_stream->sndvar->snd_una)) { 170*76404edcSAsim Jamshed /* there are packets sent but not acked */ 171*76404edcSAsim Jamshed /* update rto timestamp */ 172*76404edcSAsim Jamshed cur_stream->sndvar->ts_rto = cur_ts + cur_stream->sndvar->rto; 173*76404edcSAsim Jamshed AddtoRTOList(mtcp, cur_stream); 174*76404edcSAsim Jamshed 175*76404edcSAsim Jamshed } else { 176*76404edcSAsim Jamshed /* all packets are acked */ 177*76404edcSAsim Jamshed TRACE_RTO("All packets are acked. snd_una: %u, snd_nxt: %u\n", 178*76404edcSAsim Jamshed cur_stream->sndvar->snd_una, cur_stream->snd_nxt); 179*76404edcSAsim Jamshed } 180*76404edcSAsim Jamshed } 181*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 182*76404edcSAsim Jamshed static inline int 183*76404edcSAsim Jamshed HandleRTO(mtcp_manager_t mtcp, uint32_t cur_ts, tcp_stream *cur_stream) 184*76404edcSAsim Jamshed { 185*76404edcSAsim Jamshed uint8_t backoff; 186*76404edcSAsim Jamshed 187*76404edcSAsim Jamshed TRACE_RTO("Stream %d Timeout! rto: %u (%ums), snd_una: %u, snd_nxt: %u\n", 188*76404edcSAsim Jamshed cur_stream->id, cur_stream->sndvar->rto, TS_TO_MSEC(cur_stream->sndvar->rto), 189*76404edcSAsim Jamshed cur_stream->sndvar->snd_una, cur_stream->snd_nxt); 190*76404edcSAsim Jamshed assert(cur_stream->sndvar->rto > 0); 191*76404edcSAsim Jamshed 192*76404edcSAsim Jamshed /* count number of retransmissions */ 193*76404edcSAsim Jamshed if (cur_stream->sndvar->nrtx < TCP_MAX_RTX) { 194*76404edcSAsim Jamshed cur_stream->sndvar->nrtx++; 195*76404edcSAsim Jamshed } else { 196*76404edcSAsim Jamshed /* if it exceeds the threshold, destroy and notify to application */ 197*76404edcSAsim Jamshed TRACE_RTO("Stream %d: Exceed MAX_RTX\n", cur_stream->id); 198*76404edcSAsim Jamshed if (cur_stream->state < TCP_ST_ESTABLISHED) { 199*76404edcSAsim Jamshed cur_stream->state = TCP_ST_CLOSED_RSVD; 200*76404edcSAsim Jamshed cur_stream->close_reason = TCP_CONN_FAIL; 201*76404edcSAsim Jamshed cur_stream->cb_events |= MOS_ON_TCP_STATE_CHANGE; 202*76404edcSAsim Jamshed DestroyTCPStream(mtcp, cur_stream); 203*76404edcSAsim Jamshed } else { 204*76404edcSAsim Jamshed cur_stream->state = TCP_ST_CLOSED_RSVD; 205*76404edcSAsim Jamshed cur_stream->close_reason = TCP_CONN_LOST; 206*76404edcSAsim Jamshed cur_stream->cb_events |= MOS_ON_TCP_STATE_CHANGE; 207*76404edcSAsim Jamshed if (cur_stream->socket) { 208*76404edcSAsim Jamshed RaiseErrorEvent(mtcp, cur_stream); 209*76404edcSAsim Jamshed } else { 210*76404edcSAsim Jamshed DestroyTCPStream(mtcp, cur_stream); 211*76404edcSAsim Jamshed } 212*76404edcSAsim Jamshed } 213*76404edcSAsim Jamshed return 0; 214*76404edcSAsim Jamshed } 215*76404edcSAsim Jamshed if (cur_stream->sndvar->nrtx > cur_stream->sndvar->max_nrtx) { 216*76404edcSAsim Jamshed cur_stream->sndvar->max_nrtx = cur_stream->sndvar->nrtx; 217*76404edcSAsim Jamshed } 218*76404edcSAsim Jamshed 219*76404edcSAsim Jamshed /* update rto timestamp */ 220*76404edcSAsim Jamshed if (cur_stream->state >= TCP_ST_ESTABLISHED) { 221*76404edcSAsim Jamshed uint32_t rto_prev; 222*76404edcSAsim Jamshed backoff = MIN(cur_stream->sndvar->nrtx, TCP_MAX_BACKOFF); 223*76404edcSAsim Jamshed 224*76404edcSAsim Jamshed rto_prev = cur_stream->sndvar->rto; 225*76404edcSAsim Jamshed cur_stream->sndvar->rto = 226*76404edcSAsim Jamshed ((cur_stream->rcvvar->srtt >> 3) + cur_stream->rcvvar->rttvar) << backoff; 227*76404edcSAsim Jamshed if (cur_stream->sndvar->rto <= 0) { 228*76404edcSAsim Jamshed TRACE_RTO("Stream %d current rto: %u, prev: %u, state: %s\n", 229*76404edcSAsim Jamshed cur_stream->id, cur_stream->sndvar->rto, rto_prev, 230*76404edcSAsim Jamshed TCPStateToString(cur_stream)); 231*76404edcSAsim Jamshed cur_stream->sndvar->rto = rto_prev; 232*76404edcSAsim Jamshed } 233*76404edcSAsim Jamshed } else if (cur_stream->state >= TCP_ST_SYN_SENT) { 234*76404edcSAsim Jamshed /* if there is no rtt measured, update rto based on the previous one */ 235*76404edcSAsim Jamshed if (cur_stream->sndvar->nrtx < TCP_MAX_BACKOFF) { 236*76404edcSAsim Jamshed cur_stream->sndvar->rto <<= 1; 237*76404edcSAsim Jamshed } 238*76404edcSAsim Jamshed } 239*76404edcSAsim Jamshed //cur_stream->sndvar->ts_rto = cur_ts + cur_stream->sndvar->rto; 240*76404edcSAsim Jamshed 241*76404edcSAsim Jamshed /* reduce congestion window and ssthresh */ 242*76404edcSAsim Jamshed cur_stream->sndvar->ssthresh = MIN(cur_stream->sndvar->cwnd, cur_stream->sndvar->peer_wnd) / 2; 243*76404edcSAsim Jamshed if (cur_stream->sndvar->ssthresh < (2 * cur_stream->sndvar->mss)) { 244*76404edcSAsim Jamshed cur_stream->sndvar->ssthresh = cur_stream->sndvar->mss * 2; 245*76404edcSAsim Jamshed } 246*76404edcSAsim Jamshed cur_stream->sndvar->cwnd = cur_stream->sndvar->mss; 247*76404edcSAsim Jamshed TRACE_CONG("Stream %d Timeout. cwnd: %u, ssthresh: %u\n", 248*76404edcSAsim Jamshed cur_stream->id, cur_stream->sndvar->cwnd, cur_stream->sndvar->ssthresh); 249*76404edcSAsim Jamshed 250*76404edcSAsim Jamshed #if RTM_STAT 251*76404edcSAsim Jamshed /* update retransmission stats */ 252*76404edcSAsim Jamshed cur_stream->sndvar->rstat.rto_cnt++; 253*76404edcSAsim Jamshed cur_stream->sndvar->rstat.rto_bytes += (cur_stream->snd_nxt - cur_stream->sndvar->snd_una); 254*76404edcSAsim Jamshed #endif 255*76404edcSAsim Jamshed 256*76404edcSAsim Jamshed if (cur_stream->on_rto_idx >= 0) 257*76404edcSAsim Jamshed RemoveFromRTOList(mtcp, cur_stream); 258*76404edcSAsim Jamshed 259*76404edcSAsim Jamshed /* Retransmission */ 260*76404edcSAsim Jamshed if (cur_stream->state == TCP_ST_SYN_SENT) { 261*76404edcSAsim Jamshed /* SYN lost */ 262*76404edcSAsim Jamshed if (cur_stream->sndvar->nrtx > TCP_MAX_SYN_RETRY) { 263*76404edcSAsim Jamshed cur_stream->state = TCP_ST_CLOSED_RSVD; 264*76404edcSAsim Jamshed cur_stream->close_reason = TCP_CONN_FAIL; 265*76404edcSAsim Jamshed cur_stream->cb_events |= MOS_ON_TCP_STATE_CHANGE; 266*76404edcSAsim Jamshed TRACE_RTO("Stream %d: SYN retries exceed maximum retries.\n", 267*76404edcSAsim Jamshed cur_stream->id); 268*76404edcSAsim Jamshed if (cur_stream->socket) { 269*76404edcSAsim Jamshed RaiseErrorEvent(mtcp, cur_stream); 270*76404edcSAsim Jamshed } else { 271*76404edcSAsim Jamshed DestroyTCPStream(mtcp, cur_stream); 272*76404edcSAsim Jamshed } 273*76404edcSAsim Jamshed 274*76404edcSAsim Jamshed return 0; 275*76404edcSAsim Jamshed } 276*76404edcSAsim Jamshed TRACE_RTO("Stream %d Retransmit SYN. snd_nxt: %u, snd_una: %u\n", 277*76404edcSAsim Jamshed cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una); 278*76404edcSAsim Jamshed 279*76404edcSAsim Jamshed } else if (cur_stream->state == TCP_ST_SYN_RCVD) { 280*76404edcSAsim Jamshed /* SYN/ACK lost */ 281*76404edcSAsim Jamshed TRACE_RTO("Stream %d: Retransmit SYN/ACK. snd_nxt: %u, snd_una: %u\n", 282*76404edcSAsim Jamshed cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una); 283*76404edcSAsim Jamshed 284*76404edcSAsim Jamshed } else if (cur_stream->state == TCP_ST_ESTABLISHED) { 285*76404edcSAsim Jamshed /* Data lost */ 286*76404edcSAsim Jamshed TRACE_RTO("Stream %d: Retransmit data. snd_nxt: %u, snd_una: %u\n", 287*76404edcSAsim Jamshed cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una); 288*76404edcSAsim Jamshed 289*76404edcSAsim Jamshed } else if (cur_stream->state == TCP_ST_CLOSE_WAIT) { 290*76404edcSAsim Jamshed /* Data lost */ 291*76404edcSAsim Jamshed TRACE_RTO("Stream %d: Retransmit data. snd_nxt: %u, snd_una: %u\n", 292*76404edcSAsim Jamshed cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una); 293*76404edcSAsim Jamshed 294*76404edcSAsim Jamshed } else if (cur_stream->state == TCP_ST_LAST_ACK) { 295*76404edcSAsim Jamshed /* FIN/ACK lost */ 296*76404edcSAsim Jamshed TRACE_RTO("Stream %d: Retransmit FIN/ACK. " 297*76404edcSAsim Jamshed "snd_nxt: %u, snd_una: %u\n", 298*76404edcSAsim Jamshed cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una); 299*76404edcSAsim Jamshed 300*76404edcSAsim Jamshed } else if (cur_stream->state == TCP_ST_FIN_WAIT_1) { 301*76404edcSAsim Jamshed /* FIN lost */ 302*76404edcSAsim Jamshed TRACE_RTO("Stream %d: Retransmit FIN. snd_nxt: %u, snd_una: %u\n", 303*76404edcSAsim Jamshed cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una); 304*76404edcSAsim Jamshed } else if (cur_stream->state == TCP_ST_CLOSING) { 305*76404edcSAsim Jamshed TRACE_RTO("Stream %d: Retransmit ACK. snd_nxt: %u, snd_una: %u\n", 306*76404edcSAsim Jamshed cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una); 307*76404edcSAsim Jamshed //TRACE_DBG("Stream %d: Retransmitting at CLOSING\n", cur_stream->id); 308*76404edcSAsim Jamshed 309*76404edcSAsim Jamshed } else if (cur_stream->state == TCP_ST_FIN_WAIT_2) { 310*76404edcSAsim Jamshed TRACE_RTO("Stream %d: Retransmit ACK. snd_nxt: %u, snd_una: %u\n", 311*76404edcSAsim Jamshed cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una); 312*76404edcSAsim Jamshed } else { 313*76404edcSAsim Jamshed TRACE_ERROR("Stream %d: not implemented state! state: %s, rto: %u\n", 314*76404edcSAsim Jamshed cur_stream->id, 315*76404edcSAsim Jamshed TCPStateToString(cur_stream), cur_stream->sndvar->rto); 316*76404edcSAsim Jamshed assert(0); 317*76404edcSAsim Jamshed return 0; 318*76404edcSAsim Jamshed } 319*76404edcSAsim Jamshed 320*76404edcSAsim Jamshed cur_stream->snd_nxt = cur_stream->sndvar->snd_una; 321*76404edcSAsim Jamshed if (cur_stream->state == TCP_ST_ESTABLISHED || 322*76404edcSAsim Jamshed cur_stream->state == TCP_ST_CLOSE_WAIT) { 323*76404edcSAsim Jamshed /* retransmit data at ESTABLISHED state */ 324*76404edcSAsim Jamshed AddtoSendList(mtcp, cur_stream); 325*76404edcSAsim Jamshed 326*76404edcSAsim Jamshed } else if (cur_stream->state == TCP_ST_FIN_WAIT_1 || 327*76404edcSAsim Jamshed cur_stream->state == TCP_ST_CLOSING || 328*76404edcSAsim Jamshed cur_stream->state == TCP_ST_LAST_ACK) { 329*76404edcSAsim Jamshed 330*76404edcSAsim Jamshed if (cur_stream->sndvar->fss == 0) { 331*76404edcSAsim Jamshed TRACE_ERROR("Stream %u: fss not set.\n", cur_stream->id); 332*76404edcSAsim Jamshed } 333*76404edcSAsim Jamshed /* decide to retransmit data or control packet */ 334*76404edcSAsim Jamshed if (TCP_SEQ_LT(cur_stream->snd_nxt, cur_stream->sndvar->fss)) { 335*76404edcSAsim Jamshed /* need to retransmit data */ 336*76404edcSAsim Jamshed if (cur_stream->sndvar->on_control_list) { 337*76404edcSAsim Jamshed RemoveFromControlList(mtcp, cur_stream); 338*76404edcSAsim Jamshed } 339*76404edcSAsim Jamshed cur_stream->control_list_waiting = TRUE; 340*76404edcSAsim Jamshed AddtoSendList(mtcp, cur_stream); 341*76404edcSAsim Jamshed 342*76404edcSAsim Jamshed } else { 343*76404edcSAsim Jamshed /* need to retransmit control packet */ 344*76404edcSAsim Jamshed AddtoControlList(mtcp, cur_stream, cur_ts); 345*76404edcSAsim Jamshed } 346*76404edcSAsim Jamshed 347*76404edcSAsim Jamshed } else { 348*76404edcSAsim Jamshed AddtoControlList(mtcp, cur_stream, cur_ts); 349*76404edcSAsim Jamshed } 350*76404edcSAsim Jamshed 351*76404edcSAsim Jamshed return 1; 352*76404edcSAsim Jamshed } 353*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 354*76404edcSAsim Jamshed static inline void 355*76404edcSAsim Jamshed RearrangeRTOStore(mtcp_manager_t mtcp) { 356*76404edcSAsim Jamshed tcp_stream *walk, *next; 357*76404edcSAsim Jamshed struct rto_head* rto_list = &mtcp->rto_store->rto_list[RTO_HASH]; 358*76404edcSAsim Jamshed int cnt = 0; 359*76404edcSAsim Jamshed 360*76404edcSAsim Jamshed for (walk = TAILQ_FIRST(rto_list); 361*76404edcSAsim Jamshed walk != NULL; walk = next) { 362*76404edcSAsim Jamshed next = TAILQ_NEXT(walk, sndvar->timer_link); 363*76404edcSAsim Jamshed 364*76404edcSAsim Jamshed int diff = (int32_t)(mtcp->rto_store->rto_now_ts - walk->sndvar->ts_rto); 365*76404edcSAsim Jamshed if (diff < RTO_HASH) { 366*76404edcSAsim Jamshed //int offset = (diff + mtcp->rto_store->rto_now_idx) % RTO_HASH; 367*76404edcSAsim Jamshed int offset = ((diff + mtcp->rto_store->rto_now_idx) & (RTO_HASH - 1)); 368*76404edcSAsim Jamshed if (!TAILQ_EMPTY(&mtcp->rto_store->rto_list[RTO_HASH])) { 369*76404edcSAsim Jamshed TAILQ_REMOVE(&mtcp->rto_store->rto_list[RTO_HASH], 370*76404edcSAsim Jamshed walk, sndvar->timer_link); 371*76404edcSAsim Jamshed walk->on_rto_idx = offset; 372*76404edcSAsim Jamshed TAILQ_INSERT_TAIL(&(mtcp->rto_store->rto_list[offset]), 373*76404edcSAsim Jamshed walk, sndvar->timer_link); 374*76404edcSAsim Jamshed } 375*76404edcSAsim Jamshed } 376*76404edcSAsim Jamshed cnt++; 377*76404edcSAsim Jamshed } 378*76404edcSAsim Jamshed } 379*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 380*76404edcSAsim Jamshed void 381*76404edcSAsim Jamshed CheckRtmTimeout(mtcp_manager_t mtcp, uint32_t cur_ts, int thresh) 382*76404edcSAsim Jamshed { 383*76404edcSAsim Jamshed tcp_stream *walk, *next; 384*76404edcSAsim Jamshed struct rto_head* rto_list; 385*76404edcSAsim Jamshed int cnt; 386*76404edcSAsim Jamshed 387*76404edcSAsim Jamshed if (!mtcp->rto_list_cnt) { 388*76404edcSAsim Jamshed return; 389*76404edcSAsim Jamshed } 390*76404edcSAsim Jamshed 391*76404edcSAsim Jamshed STAT_COUNT(mtcp->runstat.rounds_rtocheck); 392*76404edcSAsim Jamshed 393*76404edcSAsim Jamshed cnt = 0; 394*76404edcSAsim Jamshed 395*76404edcSAsim Jamshed while (1) { 396*76404edcSAsim Jamshed 397*76404edcSAsim Jamshed rto_list = &mtcp->rto_store->rto_list[mtcp->rto_store->rto_now_idx]; 398*76404edcSAsim Jamshed if ((int32_t)(cur_ts - mtcp->rto_store->rto_now_ts) < 0) { 399*76404edcSAsim Jamshed break; 400*76404edcSAsim Jamshed } 401*76404edcSAsim Jamshed 402*76404edcSAsim Jamshed for (walk = TAILQ_FIRST(rto_list); 403*76404edcSAsim Jamshed walk != NULL; walk = next) { 404*76404edcSAsim Jamshed if (++cnt > thresh) { 405*76404edcSAsim Jamshed break; 406*76404edcSAsim Jamshed } 407*76404edcSAsim Jamshed next = TAILQ_NEXT(walk, sndvar->timer_link); 408*76404edcSAsim Jamshed 409*76404edcSAsim Jamshed TRACE_LOOP("Inside rto list. cnt: %u, stream: %d\n", 410*76404edcSAsim Jamshed cnt, walk->s_id); 411*76404edcSAsim Jamshed 412*76404edcSAsim Jamshed if (walk->on_rto_idx >= 0) { 413*76404edcSAsim Jamshed TAILQ_REMOVE(rto_list, walk, sndvar->timer_link); 414*76404edcSAsim Jamshed mtcp->rto_list_cnt--; 415*76404edcSAsim Jamshed walk->on_rto_idx = -1; 416*76404edcSAsim Jamshed HandleRTO(mtcp, cur_ts, walk); 417*76404edcSAsim Jamshed } else { 418*76404edcSAsim Jamshed TRACE_ERROR("Stream %d: not on rto list.\n", walk->id); 419*76404edcSAsim Jamshed #ifdef DUMP_STREAM 420*76404edcSAsim Jamshed DumpStream(mtcp, walk); 421*76404edcSAsim Jamshed #endif 422*76404edcSAsim Jamshed } 423*76404edcSAsim Jamshed } 424*76404edcSAsim Jamshed 425*76404edcSAsim Jamshed if (cnt > thresh) { 426*76404edcSAsim Jamshed break; 427*76404edcSAsim Jamshed } else { 428*76404edcSAsim Jamshed mtcp->rto_store->rto_now_idx = ((mtcp->rto_store->rto_now_idx + 1) & (RTO_HASH - 1)); 429*76404edcSAsim Jamshed mtcp->rto_store->rto_now_ts++; 430*76404edcSAsim Jamshed if (!((mtcp->rto_store->rto_now_idx & (1024 - 1)))) { 431*76404edcSAsim Jamshed RearrangeRTOStore(mtcp); 432*76404edcSAsim Jamshed } 433*76404edcSAsim Jamshed } 434*76404edcSAsim Jamshed 435*76404edcSAsim Jamshed } 436*76404edcSAsim Jamshed 437*76404edcSAsim Jamshed TRACE_ROUND("Checking retransmission timeout. cnt: %d\n", cnt); 438*76404edcSAsim Jamshed } 439*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 440*76404edcSAsim Jamshed void 441*76404edcSAsim Jamshed CheckTimewaitExpire(mtcp_manager_t mtcp, uint32_t cur_ts, int thresh) 442*76404edcSAsim Jamshed { 443*76404edcSAsim Jamshed tcp_stream *walk, *next; 444*76404edcSAsim Jamshed int cnt; 445*76404edcSAsim Jamshed 446*76404edcSAsim Jamshed STAT_COUNT(mtcp->runstat.rounds_twcheck); 447*76404edcSAsim Jamshed 448*76404edcSAsim Jamshed cnt = 0; 449*76404edcSAsim Jamshed 450*76404edcSAsim Jamshed for (walk = TAILQ_FIRST(&mtcp->timewait_list); 451*76404edcSAsim Jamshed walk != NULL; walk = next) { 452*76404edcSAsim Jamshed if (++cnt > thresh) 453*76404edcSAsim Jamshed break; 454*76404edcSAsim Jamshed next = TAILQ_NEXT(walk, sndvar->timer_link); 455*76404edcSAsim Jamshed 456*76404edcSAsim Jamshed TRACE_LOOP("Inside timewait list. cnt: %u, stream: %d\n", 457*76404edcSAsim Jamshed cnt, walk->s_id); 458*76404edcSAsim Jamshed 459*76404edcSAsim Jamshed if (walk->on_timewait_list) { 460*76404edcSAsim Jamshed if ((int32_t)(cur_ts - walk->rcvvar->ts_tw_expire) >= 0) { 461*76404edcSAsim Jamshed if (!walk->sndvar->on_control_list) { 462*76404edcSAsim Jamshed 463*76404edcSAsim Jamshed TAILQ_REMOVE(&mtcp->timewait_list, walk, sndvar->timer_link); 464*76404edcSAsim Jamshed walk->on_timewait_list = FALSE; 465*76404edcSAsim Jamshed mtcp->timewait_list_cnt--; 466*76404edcSAsim Jamshed 467*76404edcSAsim Jamshed walk->state = TCP_ST_CLOSED_RSVD; 468*76404edcSAsim Jamshed walk->close_reason = TCP_ACTIVE_CLOSE; 469*76404edcSAsim Jamshed walk->cb_events |= MOS_ON_TCP_STATE_CHANGE; 470*76404edcSAsim Jamshed TRACE_STATE("Stream %d: TCP_ST_CLOSED_RSVD\n", walk->id); 471*76404edcSAsim Jamshed DestroyTCPStream(mtcp, walk); 472*76404edcSAsim Jamshed } 473*76404edcSAsim Jamshed } else { 474*76404edcSAsim Jamshed break; 475*76404edcSAsim Jamshed } 476*76404edcSAsim Jamshed } else { 477*76404edcSAsim Jamshed TRACE_ERROR("Stream %d: not on timewait list.\n", walk->id); 478*76404edcSAsim Jamshed #ifdef DUMP_STREAM 479*76404edcSAsim Jamshed DumpStream(mtcp, walk); 480*76404edcSAsim Jamshed #endif 481*76404edcSAsim Jamshed } 482*76404edcSAsim Jamshed } 483*76404edcSAsim Jamshed 484*76404edcSAsim Jamshed TRACE_ROUND("Checking timewait timeout. cnt: %d\n", cnt); 485*76404edcSAsim Jamshed } 486*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 487*76404edcSAsim Jamshed void 488*76404edcSAsim Jamshed CheckConnectionTimeout(mtcp_manager_t mtcp, uint32_t cur_ts, int thresh) 489*76404edcSAsim Jamshed { 490*76404edcSAsim Jamshed tcp_stream *walk, *next; 491*76404edcSAsim Jamshed int cnt; 492*76404edcSAsim Jamshed 493*76404edcSAsim Jamshed STAT_COUNT(mtcp->runstat.rounds_tocheck); 494*76404edcSAsim Jamshed 495*76404edcSAsim Jamshed cnt = 0; 496*76404edcSAsim Jamshed for (walk = TAILQ_FIRST(&mtcp->timeout_list); 497*76404edcSAsim Jamshed walk != NULL; walk = next) { 498*76404edcSAsim Jamshed if (++cnt > thresh) 499*76404edcSAsim Jamshed break; 500*76404edcSAsim Jamshed next = TAILQ_NEXT(walk, sndvar->timeout_link); 501*76404edcSAsim Jamshed 502*76404edcSAsim Jamshed if ((int32_t)(cur_ts - walk->last_active_ts) >= 503*76404edcSAsim Jamshed g_config.mos->tcp_timeout) { 504*76404edcSAsim Jamshed 505*76404edcSAsim Jamshed TRACE_DBG("stream->sock->id: %d, stream-state: %s, streampair-state: %s\n", 506*76404edcSAsim Jamshed walk->socket->id, 507*76404edcSAsim Jamshed TCPStateToString(walk), 508*76404edcSAsim Jamshed TCPStateToString(walk->pair_stream)); 509*76404edcSAsim Jamshed 510*76404edcSAsim Jamshed walk->on_timeout_list = FALSE; 511*76404edcSAsim Jamshed TAILQ_REMOVE(&mtcp->timeout_list, walk, sndvar->timeout_link); 512*76404edcSAsim Jamshed mtcp->timeout_list_cnt--; 513*76404edcSAsim Jamshed walk->state = TCP_ST_CLOSED_RSVD; 514*76404edcSAsim Jamshed walk->close_reason = TCP_TIMEDOUT; 515*76404edcSAsim Jamshed walk->cb_events |= MOS_ON_TCP_STATE_CHANGE; 516*76404edcSAsim Jamshed if (walk->socket && HAS_STREAM_TYPE(walk, MOS_SOCK_STREAM)) { 517*76404edcSAsim Jamshed RaiseErrorEvent(mtcp, walk); 518*76404edcSAsim Jamshed } else { 519*76404edcSAsim Jamshed DestroyTCPStream(mtcp, walk); 520*76404edcSAsim Jamshed } 521*76404edcSAsim Jamshed } else { 522*76404edcSAsim Jamshed break; 523*76404edcSAsim Jamshed } 524*76404edcSAsim Jamshed 525*76404edcSAsim Jamshed } 526*76404edcSAsim Jamshed } 527*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 528*76404edcSAsim Jamshed #define TIMEVAL_ADD(a, b) \ 529*76404edcSAsim Jamshed do { (a)->tv_sec += (b)->tv_sec; \ 530*76404edcSAsim Jamshed if (((a)->tv_usec += (b)->tv_usec) > 1000000) { \ 531*76404edcSAsim Jamshed (a)->tv_sec++; (a)->tv_usec -= 1000000; } \ 532*76404edcSAsim Jamshed } while (0) 533*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 534*76404edcSAsim Jamshed static int 535*76404edcSAsim Jamshed RegTimer(mtcp_manager_t mtcp, struct timer *timer) 536*76404edcSAsim Jamshed { 537*76404edcSAsim Jamshed /* NOTE: This code assumes that the new timer expires later than existing 538*76404edcSAsim Jamshed * timers with high probability. */ 539*76404edcSAsim Jamshed struct timer *walk; 540*76404edcSAsim Jamshed 541*76404edcSAsim Jamshed TAILQ_FOREACH_REVERSE(walk, &mtcp->timer_list, timer_head, timer_link) { 542*76404edcSAsim Jamshed if (TIMEVAL_LT(&walk->exp, &timer->exp)) { 543*76404edcSAsim Jamshed TAILQ_INSERT_AFTER(&mtcp->timer_list, walk, timer, timer_link); 544*76404edcSAsim Jamshed return 0; 545*76404edcSAsim Jamshed } 546*76404edcSAsim Jamshed } 547*76404edcSAsim Jamshed 548*76404edcSAsim Jamshed assert(!walk); 549*76404edcSAsim Jamshed 550*76404edcSAsim Jamshed TAILQ_INSERT_HEAD(&mtcp->timer_list, timer, timer_link); 551*76404edcSAsim Jamshed return 0; 552*76404edcSAsim Jamshed } 553*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 554*76404edcSAsim Jamshed static struct timer * 555*76404edcSAsim Jamshed NewTimer(mtcp_manager_t mtcp, int id, struct timeval *timeout, callback_t cb) 556*76404edcSAsim Jamshed { 557*76404edcSAsim Jamshed #ifdef USE_TIMER_POOL 558*76404edcSAsim Jamshed struct timer *t = MPAllocateChunk(mtcp->timer_pool); 559*76404edcSAsim Jamshed #else 560*76404edcSAsim Jamshed struct timer *t = calloc(1, sizeof(struct timer)); 561*76404edcSAsim Jamshed #endif 562*76404edcSAsim Jamshed if (!t) 563*76404edcSAsim Jamshed return NULL; 564*76404edcSAsim Jamshed 565*76404edcSAsim Jamshed t->id = id; 566*76404edcSAsim Jamshed t->cb = cb; 567*76404edcSAsim Jamshed gettimeofday(&t->exp, NULL); 568*76404edcSAsim Jamshed TIMEVAL_ADD(&t->exp, timeout); 569*76404edcSAsim Jamshed 570*76404edcSAsim Jamshed return t; 571*76404edcSAsim Jamshed } 572*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 573*76404edcSAsim Jamshed void 574*76404edcSAsim Jamshed DelTimer(mtcp_manager_t mtcp, struct timer *timer) 575*76404edcSAsim Jamshed { 576*76404edcSAsim Jamshed TAILQ_REMOVE(&mtcp->timer_list, timer, timer_link); 577*76404edcSAsim Jamshed #ifdef USE_TIMER_POOL 578*76404edcSAsim Jamshed MPFreeChunk(mtcp->timer_pool, timer); 579*76404edcSAsim Jamshed #else 580*76404edcSAsim Jamshed free(timer); 581*76404edcSAsim Jamshed #endif 582*76404edcSAsim Jamshed } 583*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 584*76404edcSAsim Jamshed int 585*76404edcSAsim Jamshed mtcp_settimer(mctx_t mctx, int id, struct timeval *timeout, callback_t cb) 586*76404edcSAsim Jamshed { 587*76404edcSAsim Jamshed mtcp_manager_t mtcp = GetMTCPManager(mctx); 588*76404edcSAsim Jamshed if (!mtcp) 589*76404edcSAsim Jamshed return -1; 590*76404edcSAsim Jamshed 591*76404edcSAsim Jamshed struct timer *t = NewTimer(mtcp, id, timeout, cb); 592*76404edcSAsim Jamshed if (!t) 593*76404edcSAsim Jamshed return -1; 594*76404edcSAsim Jamshed 595*76404edcSAsim Jamshed RegTimer(mtcp, t); 596*76404edcSAsim Jamshed 597*76404edcSAsim Jamshed return 0; 598*76404edcSAsim Jamshed } 599*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/ 600