📄 tcp_timer.c
字号:
/* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * Implementation of the Transmission Control Protocol(TCP). * * Version: $Id: tcp_timer.c,v 1.62.2.4 1999/09/23 19:21:39 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Mark Evans, <evansmp@uhura.aston.ac.uk> * Corey Minyard <wf-rch!minyard@relay.EU.net> * Florian La Roche, <flla@stud.uni-sb.de> * Charles Hedrick, <hedrick@klinzhai.rutgers.edu> * Linus Torvalds, <torvalds@cs.helsinki.fi> * Alan Cox, <gw4pts@gw4pts.ampr.org> * Matthew Dillon, <dillon@apollo.west.oic.com> * Arnt Gulbrandsen, <agulbra@nvg.unit.no> * Jorge Cwik, <jorge@laser.satlink.net> */#include <net/tcp.h>int sysctl_tcp_syn_retries = TCP_SYN_RETRIES; int sysctl_tcp_keepalive_time = TCP_KEEPALIVE_TIME;int sysctl_tcp_keepalive_probes = TCP_KEEPALIVE_PROBES;int sysctl_tcp_retries1 = TCP_RETR1;int sysctl_tcp_retries2 = TCP_RETR2;static void tcp_sltimer_handler(unsigned long);static void tcp_syn_recv_timer(unsigned long);static void tcp_keepalive(unsigned long data);static void tcp_twkill(unsigned long);struct timer_list tcp_slow_timer = { NULL, NULL, 0, 0, tcp_sltimer_handler,};struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX] = { {ATOMIC_INIT(0), TCP_SYNACK_PERIOD, 0, tcp_syn_recv_timer},/* SYNACK */ {ATOMIC_INIT(0), TCP_KEEPALIVE_PERIOD, 0, tcp_keepalive}, /* KEEPALIVE */ {ATOMIC_INIT(0), TCP_TWKILL_PERIOD, 0, tcp_twkill} /* TWKILL */};const char timer_bug_msg[] = KERN_DEBUG "tcpbug: unknown timer value\n";/* * Using different timers for retransmit, delayed acks and probes * We may wish use just one timer maintaining a list of expire jiffies * to optimize. */void tcp_init_xmit_timers(struct sock *sk){ init_timer(&sk->tp_pinfo.af_tcp.retransmit_timer); sk->tp_pinfo.af_tcp.retransmit_timer.function=&tcp_retransmit_timer; sk->tp_pinfo.af_tcp.retransmit_timer.data = (unsigned long) sk; init_timer(&sk->tp_pinfo.af_tcp.delack_timer); sk->tp_pinfo.af_tcp.delack_timer.function=&tcp_delack_timer; sk->tp_pinfo.af_tcp.delack_timer.data = (unsigned long) sk; init_timer(&sk->tp_pinfo.af_tcp.probe_timer); sk->tp_pinfo.af_tcp.probe_timer.function=&tcp_probe_timer; sk->tp_pinfo.af_tcp.probe_timer.data = (unsigned long) sk;}/* * Reset the retransmission timer */ void tcp_reset_xmit_timer(struct sock *sk, int what, unsigned long when){ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; switch (what) { case TIME_RETRANS: /* When seting the transmit timer the probe timer * should not be set. * The delayed ack timer can be set if we are changing the * retransmit timer when removing acked frames. */ if(tp->probe_timer.prev) del_timer(&tp->probe_timer); mod_timer(&tp->retransmit_timer, jiffies+when); break; case TIME_DACK: mod_timer(&tp->delack_timer, jiffies+when); break; case TIME_PROBE0: mod_timer(&tp->probe_timer, jiffies+when); break; case TIME_WRITE: printk(KERN_DEBUG "bug: tcp_reset_xmit_timer TIME_WRITE\n"); break; default: printk(KERN_DEBUG "bug: unknown timer value\n"); };}void tcp_clear_xmit_timers(struct sock *sk){ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; if(tp->retransmit_timer.prev) del_timer(&tp->retransmit_timer); if(tp->delack_timer.prev) del_timer(&tp->delack_timer); if(tp->probe_timer.prev) del_timer(&tp->probe_timer);}static int tcp_write_err(struct sock *sk, int force){ sk->err = sk->err_soft ? sk->err_soft : ETIMEDOUT; sk->error_report(sk); tcp_clear_xmit_timers(sk); /* Time wait the socket. */ if (!force && ((1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING))) { tcp_time_wait(sk); } else { /* Clean up time. */ tcp_set_state(sk, TCP_CLOSE); return 0; } return 1;}/* A write timeout has occurred. Process the after effects. */static int tcp_write_timeout(struct sock *sk){ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); /* Look for a 'soft' timeout. */ if ((sk->state == TCP_ESTABLISHED && tp->retransmits && (tp->retransmits % TCP_QUICK_TRIES) == 0) || (sk->state != TCP_ESTABLISHED && tp->retransmits > sysctl_tcp_retries1)) { dst_negative_advice(&sk->dst_cache); } /* Have we tried to SYN too many times (repent repent 8)) */ if(tp->retransmits > sysctl_tcp_syn_retries && sk->state==TCP_SYN_SENT) { tcp_write_err(sk, 1); /* Don't FIN, we got nothing back */ return 0; } /* Has it gone just too far? */ if (tp->retransmits > sysctl_tcp_retries2) return tcp_write_err(sk, 0); return 1;}void tcp_delack_timer(unsigned long data){ struct sock *sk = (struct sock*)data; if(!sk->zapped && sk->tp_pinfo.af_tcp.delayed_acks && sk->state != TCP_CLOSE) { /* If socket is currently locked, defer the ACK. */ if (!atomic_read(&sk->sock_readers)) tcp_send_ack(sk); else tcp_send_delayed_ack(&(sk->tp_pinfo.af_tcp), HZ/10); }}void tcp_probe_timer(unsigned long data){ struct sock *sk = (struct sock*)data; struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; if(sk->zapped) return; if (atomic_read(&sk->sock_readers)) { /* Try again later. */ tcp_reset_xmit_timer(sk, TIME_PROBE0, HZ/5); return; } /* *WARNING* RFC 1122 forbids this * It doesn't AFAIK, because we kill the retransmit timer -AK * FIXME: We ought not to do it, Solaris 2.5 actually has fixing * this behaviour in Solaris down as a bug fix. [AC] */ if (tp->probes_out > sysctl_tcp_retries2) { if(sk->err_soft) sk->err = sk->err_soft; else sk->err = ETIMEDOUT; sk->error_report(sk); if ((1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING)) { /* Time wait the socket. */ tcp_time_wait(sk); } else { /* Clean up time. */ tcp_set_state(sk, TCP_CLOSE); } } else { /* Only send another probe if we didn't close things up. */ tcp_send_probe0(sk); }}static __inline__ int tcp_keepopen_proc(struct sock *sk){ int res = 0; if ((1<<sk->state) & (TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_FIN_WAIT2)) { struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; __u32 elapsed = tcp_time_stamp - tp->rcv_tstamp; if (elapsed >= sysctl_tcp_keepalive_time) { if (tp->probes_out > sysctl_tcp_keepalive_probes) { if(sk->err_soft) sk->err = sk->err_soft; else sk->err = ETIMEDOUT; tcp_set_state(sk, TCP_CLOSE); sk->shutdown = SHUTDOWN_MASK; if (!sk->dead) sk->state_change(sk); } else { tp->probes_out++; tp->pending = TIME_KEEPOPEN; tcp_write_wakeup(sk); res = 1; } } } return res;}/* Kill off TIME_WAIT sockets once their lifetime has expired. */int tcp_tw_death_row_slot = 0;static struct tcp_tw_bucket *tcp_tw_death_row[TCP_TWKILL_SLOTS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };extern void tcp_timewait_kill(struct tcp_tw_bucket *tw);static void tcp_twkill(unsigned long data){ struct tcp_tw_bucket *tw; int killed = 0; tw = tcp_tw_death_row[tcp_tw_death_row_slot]; tcp_tw_death_row[tcp_tw_death_row_slot] = NULL; while(tw != NULL) { struct tcp_tw_bucket *next = tw->next_death; tcp_timewait_kill(tw); killed++; tw = next; } if(killed != 0) { struct tcp_sl_timer *slt = (struct tcp_sl_timer *)data; atomic_sub(killed, &slt->count); } tcp_tw_death_row_slot = ((tcp_tw_death_row_slot + 1) & (TCP_TWKILL_SLOTS - 1));}/* These are always called from BH context. See callers in * tcp_input.c to verify this. */void tcp_tw_schedule(struct tcp_tw_bucket *tw){ int slot = (tcp_tw_death_row_slot - 1) & (TCP_TWKILL_SLOTS - 1); struct tcp_tw_bucket **tpp = &tcp_tw_death_row[slot]; if((tw->next_death = *tpp) != NULL) (*tpp)->pprev_death = &tw->next_death; *tpp = tw; tw->pprev_death = tpp; tw->death_slot = slot; tcp_inc_slow_timer(TCP_SLT_TWKILL);}/* Happens rarely if at all, no care about scalability here. */void tcp_tw_reschedule(struct tcp_tw_bucket *tw){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -