patch-1.3.62 linux/net/ipv4/tcp_timer.c
Next file: linux/net/ipv4/timer.c
Previous file: linux/net/ipv4/tcp_output.c
Back to the patch index
Back to the overall index
- Lines: 288
- Date:
Sun Feb 11 13:28:37 1996
- Orig file:
v1.3.61/linux/net/ipv4/tcp_timer.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.61/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
@@ -0,0 +1,287 @@
+/*
+ * 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: @(#)tcp.c 1.0.16 05/25/93
+ *
+ * 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>
+
+/*
+ * Reset the retransmission timer
+ */
+
+void tcp_reset_xmit_timer(struct sock *sk, int why, unsigned long when)
+{
+ del_timer(&sk->retransmit_timer);
+ sk->ip_xmit_timeout = why;
+ if((long)when < 0)
+ {
+ when=3;
+ printk("Error: Negative timer in xmit_timer\n");
+ }
+ sk->retransmit_timer.expires=jiffies+when;
+ add_timer(&sk->retransmit_timer);
+}
+
+/*
+ * This is the normal code called for timeouts. It does the retransmission
+ * and then does backoff. tcp_do_retransmit is separated out because
+ * tcp_ack needs to send stuff from the retransmit queue without
+ * initiating a backoff.
+ */
+
+
+static void tcp_retransmit_time(struct sock *sk, int all)
+{
+ tcp_do_retransmit(sk, all);
+
+ /*
+ * Increase the timeout each time we retransmit. Note that
+ * we do not increase the rtt estimate. rto is initialized
+ * from rtt, but increases here. Jacobson (SIGCOMM 88) suggests
+ * that doubling rto each time is the least we can get away with.
+ * In KA9Q, Karn uses this for the first few times, and then
+ * goes to quadratic. netBSD doubles, but only goes up to *64,
+ * and clamps at 1 to 64 sec afterwards. Note that 120 sec is
+ * defined in the protocol as the maximum possible RTT. I guess
+ * we'll have to use something other than TCP to talk to the
+ * University of Mars.
+ *
+ * PAWS allows us longer timeouts and large windows, so once
+ * implemented ftp to mars will work nicely. We will have to fix
+ * the 120 second clamps though!
+ */
+
+ sk->retransmits++;
+ sk->prot->retransmits++;
+ sk->backoff++;
+ sk->rto = min(sk->rto << 1, 120*HZ);
+ tcp_reset_xmit_timer(sk, TIME_WRITE, sk->rto);
+}
+
+/*
+ * A timer event has trigger a tcp retransmit timeout. The
+ * socket xmit queue is ready and set up to send. Because
+ * the ack receive code keeps the queue straight we do
+ * nothing clever here.
+ */
+
+void tcp_retransmit(struct sock *sk, int all)
+{
+ if (all)
+ {
+ tcp_retransmit_time(sk, all);
+ return;
+ }
+
+ sk->ssthresh = sk->cong_window >> 1; /* remember window where we lost */
+ /* sk->ssthresh in theory can be zero. I guess that's OK */
+ sk->cong_count = 0;
+
+ sk->cong_window = 1;
+
+ /* Do the actual retransmit. */
+ tcp_retransmit_time(sk, all);
+}
+
+/*
+ * A write timeout has occurred. Process the after effects.
+ */
+
+static int tcp_write_timeout(struct sock *sk)
+{
+ /*
+ * Look for a 'soft' timeout.
+ */
+ if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
+ || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1))
+ {
+ /*
+ * Attempt to recover if arp has changed (unlikely!) or
+ * a route has shifted (not supported prior to 1.3).
+ */
+ ip_rt_advice(&sk->ip_route_cache, 0);
+ }
+
+ /*
+ * Have we tried to SYN too many times (repent repent 8))
+ */
+
+ if(sk->retransmits > TCP_SYN_RETRIES && sk->state==TCP_SYN_SENT)
+ {
+ if(sk->err_soft)
+ sk->err=sk->err_soft;
+ else
+ sk->err=ETIMEDOUT;
+ sk->error_report(sk);
+ del_timer(&sk->retransmit_timer);
+ tcp_statistics.TcpAttemptFails++; /* Is this right ??? - FIXME - */
+ tcp_set_state(sk,TCP_CLOSE);
+ /* Don't FIN, we got nothing back */
+ release_sock(sk);
+ return 0;
+ }
+ /*
+ * Has it gone just too far ?
+ */
+ if (sk->retransmits > TCP_RETR2)
+ {
+ if(sk->err_soft)
+ sk->err = sk->err_soft;
+ else
+ sk->err = ETIMEDOUT;
+ sk->error_report(sk);
+ del_timer(&sk->retransmit_timer);
+ /*
+ * Time wait the socket
+ */
+ if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING )
+ {
+ tcp_set_state(sk,TCP_TIME_WAIT);
+ tcp_reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+ }
+ else
+ {
+ /*
+ * Clean up time.
+ */
+ tcp_set_state(sk, TCP_CLOSE);
+ release_sock(sk);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * The TCP retransmit timer. This lacks a few small details.
+ *
+ * 1. An initial rtt timeout on the probe0 should cause what we can
+ * of the first write queue buffer to be split and sent.
+ * 2. On a 'major timeout' as defined by RFC1122 we shouldn't report
+ * ETIMEDOUT if we know an additional 'soft' error caused this.
+ * tcp_err should save a 'soft error' for us.
+ */
+
+void tcp_retransmit_timer(unsigned long data)
+{
+ struct sock *sk = (struct sock*)data;
+ int why = sk->ip_xmit_timeout;
+
+ /*
+ * We are reset. We will send no more retransmits.
+ */
+
+ if(sk->zapped)
+ return;
+
+ /*
+ * Only process if socket is not in use
+ */
+
+ cli();
+ if (sk->inuse || in_bh)
+ {
+ /* Try again in 1 second */
+ sk->retransmit_timer.expires = jiffies+HZ;
+ add_timer(&sk->retransmit_timer);
+ sti();
+ return;
+ }
+
+ sk->inuse = 1;
+ sti();
+
+
+ if (sk->ack_backlog && !sk->dead)
+ sk->data_ready(sk,0);
+
+ /* Now we need to figure out why the socket was on the timer. */
+
+ switch (why)
+ {
+ /* Window probing */
+ case TIME_PROBE0:
+ tcp_send_probe0(sk);
+ tcp_write_timeout(sk);
+ break;
+ /* Retransmitting */
+ case TIME_WRITE:
+ /* It could be we got here because we needed to send an ack.
+ * So we need to check for that.
+ */
+ {
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ skb = sk->send_head;
+ if (!skb)
+ {
+ if (sk->ack_backlog)
+ tcp_read_wakeup(sk);
+ restore_flags(flags);
+ }
+ else
+ {
+ /*
+ * Kicked by a delayed ack. Reset timer
+ * correctly now
+ */
+ if (jiffies < skb->when + sk->rto)
+ {
+ if (sk->ack_backlog)
+ tcp_read_wakeup(sk);
+ tcp_reset_xmit_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies);
+ restore_flags(flags);
+ break;
+ }
+ restore_flags(flags);
+ /*
+ * Retransmission
+ */
+ sk->retransmits++;
+ sk->prot->retransmits++;
+ sk->prot->retransmit (sk, 0);
+ tcp_write_timeout(sk);
+ }
+ break;
+ }
+ /* Sending Keepalives */
+ case TIME_KEEPOPEN:
+ /*
+ * this reset_timer() call is a hack, this is not
+ * how KEEPOPEN is supposed to work.
+ */
+ tcp_reset_xmit_timer (sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
+
+ /* Send something to keep the connection open. */
+ if (sk->prot->write_wakeup)
+ sk->prot->write_wakeup (sk);
+ sk->retransmits++;
+ sk->prot->retransmits++;
+ tcp_write_timeout(sk);
+ break;
+ default:
+ printk ("rexmit_timer: timer expired - reason unknown\n");
+ break;
+ }
+ release_sock(sk);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this