v_count => v_usecount; v_lastr is being used; v_mounton is gone;
[unix-history] / usr / src / sys / netinet / tcp_output.c
index 3a725fb..1b69eb5 100644 (file)
@@ -1,13 +1,25 @@
 /*
 /*
- * Copyright (c) 1982 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
+ * All rights reserved.
  *
  *
- *     @(#)tcp_output.c        6.18 (Berkeley) %G%
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)tcp_output.c        7.18 (Berkeley) %G%
  */
 
 #include "param.h"
 #include "systm.h"
  */
 
 #include "param.h"
 #include "systm.h"
+#include "malloc.h"
 #include "mbuf.h"
 #include "protosw.h"
 #include "socket.h"
 #include "mbuf.h"
 #include "protosw.h"
 #include "socket.h"
@@ -42,7 +54,7 @@ tcp_output(tp)
        register struct tcpcb *tp;
 {
        register struct socket *so = tp->t_inpcb->inp_socket;
        register struct tcpcb *tp;
 {
        register struct socket *so = tp->t_inpcb->inp_socket;
-       register int len, win;
+       register long len, win;
        struct mbuf *m0;
        int off, flags, error;
        register struct mbuf *m;
        struct mbuf *m0;
        int off, flags, error;
        register struct mbuf *m;
@@ -61,7 +73,7 @@ tcp_output(tp)
 again:
        sendalot = 0;
        off = tp->snd_nxt - tp->snd_una;
 again:
        sendalot = 0;
        off = tp->snd_nxt - tp->snd_una;
-       win = MIN(tp->snd_wnd, tp->snd_cwnd);
+       win = min(tp->snd_wnd, tp->snd_cwnd);
 
        /*
         * If in persist timeout with window of 0, send 1 byte.
 
        /*
         * If in persist timeout with window of 0, send 1 byte.
@@ -78,44 +90,32 @@ again:
                }
        }
 
                }
        }
 
-       len = MIN(so->so_snd.sb_cc, win) - off;
+       len = min(so->so_snd.sb_cc, win) - off;
        flags = tcp_outflags[tp->t_state];
        flags = tcp_outflags[tp->t_state];
-       if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
-               flags &= ~TH_FIN;
 
        if (len < 0) {
                /*
                 * If FIN has been sent but not acked,
                 * but we haven't been called to retransmit,
 
        if (len < 0) {
                /*
                 * If FIN has been sent but not acked,
                 * but we haven't been called to retransmit,
-                * len will be -1; transmit if acking, otherwise no need.
-                * Otherwise, window shrank after we sent into it.
-                * If window shrank to 0, cancel pending retransmit
-                * and pull snd_nxt back to (closed) window.
-                * We will enter persist state below.
-                * If the window didn't close completely,
+                * len will be -1.  Otherwise, window shrank
+                * after we sent into it.  If window shrank to 0,
+                * cancel pending retransmit and pull snd_nxt
+                * back to (closed) window.  We will enter persist
+                * state below.  If the window didn't close completely,
                 * just wait for an ACK.
                 */
                 * just wait for an ACK.
                 */
-               if (flags & TH_FIN) {
-                       if (tp->t_flags & TF_ACKNOW)
-                               len = 0;
-                       else
-                               return (0);
-               } else if (win == 0) {
+               len = 0;
+               if (win == 0) {
                        tp->t_timer[TCPT_REXMT] = 0;
                        tp->snd_nxt = tp->snd_una;
                        tp->t_timer[TCPT_REXMT] = 0;
                        tp->snd_nxt = tp->snd_una;
-                       len = 0;
-               } else
-                       return (0);
+               }
        }
        if (len > tp->t_maxseg) {
                len = tp->t_maxseg;
        }
        if (len > tp->t_maxseg) {
                len = tp->t_maxseg;
-               /*
-                * Don't send more than one segment if retransmitting
-                * (or persisting, but then we shouldn't be here).
-                */
-               if (tp->t_rxtshift == 0)
-                       sendalot = 1;
+               sendalot = 1;
        }
        }
+       if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
+               flags &= ~TH_FIN;
        win = sbspace(&so->so_rcv);
 
 
        win = sbspace(&so->so_rcv);
 
 
@@ -148,7 +148,7 @@ again:
         * to send into a small window), then must resend.
         */
        if (len) {
         * to send into a small window), then must resend.
         */
        if (len) {
-               if (len == tp->t_maxseg || len >= TCP_MSS)      /* a lot */
+               if (len == tp->t_maxseg)
                        goto send;
                if ((idle || tp->t_flags & TF_NODELAY) &&
                    len + off >= so->so_snd.sb_cc)
                        goto send;
                if ((idle || tp->t_flags & TF_NODELAY) &&
                    len + off >= so->so_snd.sb_cc)
@@ -164,12 +164,18 @@ again:
        /*
         * Compare available window to amount of window
         * known to peer (as advertised window less
        /*
         * Compare available window to amount of window
         * known to peer (as advertised window less
-        * next expected input.)  If the difference is 35% or more of the
-        * maximum possible window, then want to send a window update to peer.
+        * next expected input).  If the difference is at least two
+        * max size segments or at least 35% of the maximum possible
+        * window, then want to send a window update to peer.
         */
         */
-       if (win > 0 &&
-           ((100*(win-(tp->rcv_adv-tp->rcv_nxt))/so->so_rcv.sb_hiwat) >= 35))
-               goto send;
+       if (win > 0) {
+               int adv = win - (tp->rcv_adv - tp->rcv_nxt);
+
+               if (so->so_rcv.sb_cc == 0 && adv >= 2 * tp->t_maxseg)
+                       goto send;
+               if (100 * adv / so->so_rcv.sb_hiwat >= 35)
+                       goto send;
+       }
 
        /*
         * TCP window updates are not reliable, rather a polling protocol
 
        /*
         * TCP window updates are not reliable, rather a polling protocol
@@ -210,17 +216,36 @@ send:
         * be transmitted, and initialize the header from
         * the template for sends on this connection.
         */
         * be transmitted, and initialize the header from
         * the template for sends on this connection.
         */
-       MGET(m, M_DONTWAIT, MT_HEADER);
+       MGETHDR(m, M_DONTWAIT, MT_HEADER);
        if (m == NULL)
                return (ENOBUFS);
        if (m == NULL)
                return (ENOBUFS);
-       m->m_off = MMAXOFF - sizeof (struct tcpiphdr);
+       m->m_data += max_linkhdr;
        m->m_len = sizeof (struct tcpiphdr);
        m->m_len = sizeof (struct tcpiphdr);
-       if (len) {
-               m->m_next = m_copy(so->so_snd.sb_mb, off, len);
-               if (m->m_next == 0)
-                       len = 0;
-       }
+       m->m_pkthdr.rcvif = (struct ifnet *)0;
        ti = mtod(m, struct tcpiphdr *);
        ti = mtod(m, struct tcpiphdr *);
+       if (len) {
+               if (tp->t_force && len == 1)
+                       tcpstat.tcps_sndprobe++;
+               else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
+                       tcpstat.tcps_sndrexmitpack++;
+                       tcpstat.tcps_sndrexmitbyte += len;
+               } else {
+                       tcpstat.tcps_sndpack++;
+                       tcpstat.tcps_sndbyte += len;
+               }
+               if (len <= MHLEN - sizeof (struct tcpiphdr) - max_linkhdr) {
+                       if (m->m_next == 0)
+                               len = 0;
+               }
+       } else if (tp->t_flags & TF_ACKNOW)
+               tcpstat.tcps_sndacks++;
+       else if (flags & (TH_SYN|TH_FIN|TH_RST))
+               tcpstat.tcps_sndctrl++;
+       else if (SEQ_GT(tp->snd_up, tp->snd_una))
+               tcpstat.tcps_sndurg++;
+       else
+               tcpstat.tcps_sndwinup++;
+
        if (tp->t_template == 0)
                panic("tcp_output");
        bcopy((caddr_t)tp->t_template, (caddr_t)ti, sizeof (struct tcpiphdr));
        if (tp->t_template == 0)
                panic("tcp_output");
        bcopy((caddr_t)tp->t_template, (caddr_t)ti, sizeof (struct tcpiphdr));
@@ -230,8 +255,8 @@ send:
         * window for use in delaying messages about window sizes.
         * If resending a FIN, be sure not to use a new sequence number.
         */
         * window for use in delaying messages about window sizes.
         * If resending a FIN, be sure not to use a new sequence number.
         */
-       if (flags & TH_FIN && tp->t_flags & TF_SENTFIN &&
-           tp->snd_nxt != tp->snd_una)
+       if (flags & TH_FIN && tp->t_flags & TF_SENTFIN && 
+           tp->snd_nxt == tp->snd_max)
                tp->snd_nxt--;
        ti->ti_seq = htonl(tp->snd_nxt);
        ti->ti_ack = htonl(tp->rcv_nxt);
                tp->snd_nxt--;
        ti->ti_seq = htonl(tp->snd_nxt);
        ti->ti_ack = htonl(tp->rcv_nxt);
@@ -240,18 +265,15 @@ send:
         * unless TCP set to not do any options.
         */
        opt = NULL;
         * unless TCP set to not do any options.
         */
        opt = NULL;
-       if (tp->t_state < TCPS_ESTABLISHED && (tp->t_flags & TF_NOOPT) == 0) {
+       if (flags & TH_SYN && (tp->t_flags & TF_NOOPT) == 0) {
                u_short mss;
 
                u_short mss;
 
-               mss = MIN(so->so_rcv.sb_hiwat / 2, tcp_mss(tp));
+               mss = min(so->so_rcv.sb_hiwat / 2, tcp_mss(tp));
                if (mss > IP_MSS - sizeof(struct tcpiphdr)) {
                        opt = tcp_initopt;
                        optlen = sizeof (tcp_initopt);
                        *(u_short *)(opt + 2) = htons(mss);
                }
                if (mss > IP_MSS - sizeof(struct tcpiphdr)) {
                        opt = tcp_initopt;
                        optlen = sizeof (tcp_initopt);
                        *(u_short *)(opt + 2) = htons(mss);
                }
-       } else if (tp->t_tcpopt) {
-               opt = mtod(tp->t_tcpopt, u_char *);
-               optlen = tp->t_tcpopt->m_len;
        }
        if (opt) {
                m0 = m->m_next;
        }
        if (opt) {
                m0 = m->m_next;
@@ -278,10 +300,14 @@ send:
         * Calculate receive window.  Don't shrink window,
         * but avoid silly window syndrome.
         */
         * Calculate receive window.  Don't shrink window,
         * but avoid silly window syndrome.
         */
-       if (win < so->so_rcv.sb_hiwat / 4 && win < tp->t_maxseg)
+       if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg)
                win = 0;
                win = 0;
-       if (win < (int)(tp->rcv_adv - tp->rcv_nxt))
-               win = (int)(tp->rcv_adv - tp->rcv_nxt);
+       if (win > IP_MAXPACKET)
+               win = IP_MAXPACKET;
+       if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
+               win = (long)(tp->rcv_adv - tp->rcv_nxt);
+       if (win > IP_MAXPACKET)
+               win = IP_MAXPACKET;
        ti->ti_win = htons((u_short)win);
        if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
                ti->ti_urp = htons((u_short)(tp->snd_up - tp->snd_nxt));
        ti->ti_win = htons((u_short)win);
        if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
                ti->ti_urp = htons((u_short)(tp->snd_up - tp->snd_nxt));
@@ -309,13 +335,16 @@ send:
        if (len + optlen)
                ti->ti_len = htons((u_short)(sizeof(struct tcphdr) +
                    optlen + len));
        if (len + optlen)
                ti->ti_len = htons((u_short)(sizeof(struct tcphdr) +
                    optlen + len));
-       ti->ti_sum = in_cksum(m, sizeof (struct tcpiphdr) + (int)optlen + len);
+       ti->ti_sum = in_cksum(m,
+           (int)(sizeof (struct tcpiphdr) + (int)optlen + len));
 
        /*
         * In transmit state, time the transmission and arrange for
         * the retransmit.  In persist state, just set snd_max.
         */
        if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
 
        /*
         * In transmit state, time the transmission and arrange for
         * the retransmit.  In persist state, just set snd_max.
         */
        if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
+               tcp_seq startseq = tp->snd_nxt;
+
                /*
                 * Advance snd_nxt over sequence space of this segment.
                 */
                /*
                 * Advance snd_nxt over sequence space of this segment.
                 */
@@ -334,24 +363,26 @@ send:
                         */
                        if (tp->t_rtt == 0) {
                                tp->t_rtt = 1;
                         */
                        if (tp->t_rtt == 0) {
                                tp->t_rtt = 1;
-                               tp->t_rtseq = tp->snd_nxt - len;
+                               tp->t_rtseq = startseq;
+                               tcpstat.tcps_segstimed++;
                        }
                }
 
                /*
                 * Set retransmit timer if not currently set,
                 * and not doing an ack or a keep-alive probe.
                        }
                }
 
                /*
                 * Set retransmit timer if not currently set,
                 * and not doing an ack or a keep-alive probe.
-                * Initial value for retransmit timer is tcp_beta*tp->t_srtt.
+                * Initial value for retransmit timer is smoothed
+                * round-trip time + 2 * round-trip time variance.
                 * Initialize shift counter which is used for backoff
                 * of retransmit time.
                 */
                if (tp->t_timer[TCPT_REXMT] == 0 &&
                    tp->snd_nxt != tp->snd_una) {
                 * Initialize shift counter which is used for backoff
                 * of retransmit time.
                 */
                if (tp->t_timer[TCPT_REXMT] == 0 &&
                    tp->snd_nxt != tp->snd_una) {
-                       TCPT_RANGESET(tp->t_timer[TCPT_REXMT],
-                         tcp_beta * (tp->t_srtt ? tp->t_srtt : TCPTV_SRTTDFLT),
-                         TCPTV_MIN, TCPTV_MAX);
-                       tp->t_rxtshift = 0;
-                       tp->t_timer[TCPT_PERSIST] = 0;
+                       tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+                       if (tp->t_timer[TCPT_PERSIST]) {
+                               tp->t_timer[TCPT_PERSIST] = 0;
+                               tp->t_rxtshift = 0;
+                       }
                }
        } else
                if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
                }
        } else
                if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
@@ -368,11 +399,24 @@ send:
         * send to IP level.
         */
        ((struct ip *)ti)->ip_len = sizeof (struct tcpiphdr) + optlen + len;
         * send to IP level.
         */
        ((struct ip *)ti)->ip_len = sizeof (struct tcpiphdr) + optlen + len;
+       if (m->m_flags & M_PKTHDR)
+               m->m_pkthdr.len = ((struct ip *)ti)->ip_len;
        ((struct ip *)ti)->ip_ttl = TCP_TTL;
        ((struct ip *)ti)->ip_ttl = TCP_TTL;
+#if BSD>=43
        error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
            so->so_options & SO_DONTROUTE);
        error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
            so->so_options & SO_DONTROUTE);
-       if (error)
+#else
+       error = ip_output(m, (struct mbuf *)0, &tp->t_inpcb->inp_route, 
+                         so->so_options & SO_DONTROUTE);
+#endif
+       if (error) {
+               if (error == ENOBUFS) {
+                       tcp_quench(tp->t_inpcb);
+                       return (0);
+               }
                return (error);
                return (error);
+       }
+       tcpstat.tcps_sndtotal++;
 
        /*
         * Data sent (as far as we can tell).
 
        /*
         * Data sent (as far as we can tell).
@@ -391,6 +435,7 @@ send:
 tcp_setpersist(tp)
        register struct tcpcb *tp;
 {
 tcp_setpersist(tp)
        register struct tcpcb *tp;
 {
+       register t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
 
        if (tp->t_timer[TCPT_REXMT])
                panic("tcp_output REXMT");
 
        if (tp->t_timer[TCPT_REXMT])
                panic("tcp_output REXMT");
@@ -398,9 +443,8 @@ tcp_setpersist(tp)
         * Start/restart persistance timer.
         */
        TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
         * Start/restart persistance timer.
         */
        TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
-           ((int)(tcp_beta * tp->t_srtt)) << tp->t_rxtshift,
-           TCPTV_PERSMIN, TCPTV_MAX);
-       tp->t_rxtshift++;
-       if (tp->t_rxtshift >= TCP_MAXRXTSHIFT)
-               tp->t_rxtshift = 0;
+           t * tcp_backoff[tp->t_rxtshift],
+           TCPTV_PERSMIN, TCPTV_PERSMAX);
+       if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
+               tp->t_rxtshift++;
 }
 }