tcp: Support settable IW parameters
[dragonfly.git] / sys / netinet / tcp_subr.c
index 62e76f4..79dafc2 100644 (file)
@@ -65,7 +65,6 @@
  *
  *     @(#)tcp_subr.c  8.2 (Berkeley) 5/24/95
  * $FreeBSD: src/sys/netinet/tcp_subr.c,v 1.73.2.31 2003/01/24 05:11:34 sam Exp $
- * $DragonFly: src/sys/netinet/tcp_subr.c,v 1.63 2008/11/11 10:46:58 sephe Exp $
  */
 
 #include "opt_compat.h"
 #if !defined(KTR_TCP)
 #define KTR_TCP                KTR_ALL
 #endif
-KTR_INFO_MASTER(tcp);
 /*
+KTR_INFO_MASTER(tcp);
 KTR_INFO(KTR_TCP, tcp, rxmsg, 0, "tcp getmsg", 0);
 KTR_INFO(KTR_TCP, tcp, wait, 1, "tcp waitmsg", 0);
 KTR_INFO(KTR_TCP, tcp, delayed, 2, "tcp execute delayed ops", 0);
-*/
 #define logtcp(name)   KTR_LOG(tcp_ ## name)
+*/
 
 struct inpcbinfo tcbinfo[MAXCPU];
 struct tcpcbackqhead tcpcbackq[MAXCPU];
 
 static struct lwkt_token tcp_port_token =
-               LWKT_TOKEN_MP_INITIALIZER(tcp_port_token);
+               LWKT_TOKEN_INITIALIZER(tcp_port_token);
 
 int tcp_mssdflt = TCP_MSS;
 SYSCTL_INT(_net_inet_tcp, TCPCTL_MSSDFLT, mssdflt, CTLFLAG_RW,
@@ -247,6 +246,23 @@ static int tcp_inflight_stab = 50;
 SYSCTL_INT(_net_inet_tcp, OID_AUTO, inflight_stab, CTLFLAG_RW,
     &tcp_inflight_stab, 0, "Slop in maximal packets / 10 (20 = 3 packets)");
 
+static int tcp_do_rfc3390 = 1;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, rfc3390, CTLFLAG_RW,
+    &tcp_do_rfc3390, 0,
+    "Enable RFC 3390 (Increasing TCP's Initial Congestion Window)");
+
+static u_long tcp_iw_maxsegs = 4;
+SYSCTL_ULONG(_net_inet_tcp, OID_AUTO, iwmaxsegs, CTLFLAG_RW,
+    &tcp_iw_maxsegs, 0, "TCP IW segments max");
+
+static u_long tcp_iw_capsegs = 3;
+SYSCTL_ULONG(_net_inet_tcp, OID_AUTO, iwcapsegs, CTLFLAG_RW,
+    &tcp_iw_capsegs, 0, "TCP IW segments");
+
+int tcp_low_rtobase = 1;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, low_rtobase, CTLFLAG_RW,
+    &tcp_low_rtobase, 0, "Lowering the Initial RTO (RFC 6298)");
+
 static MALLOC_DEFINE(M_TCPTEMP, "tcptemp", "TCP Templates for Keepalives");
 static struct malloc_pipe tcptemp_mpipe;
 
@@ -331,7 +347,7 @@ tcp_init(void)
         * allocation to fail so do not specify MPF_INT.
         */
        mpipe_init(&tcptemp_mpipe, M_TCPTEMP, sizeof(struct tcptemp),
-                   25, -1, 0, NULL);
+                   25, -1, 0, NULL, NULL, NULL);
 
        tcp_delacktime = TCPTV_DELACK;
        tcp_keepinit = TCPTV_KEEP_INIT;
@@ -695,6 +711,12 @@ tcp_newtcpcb(struct inpcb *inp)
        tp->tt_msg = &it->inp_tp_timermsg;
        bzero(tp->tt_msg, sizeof(*tp->tt_msg));
 
+       tp->t_keepinit = tcp_keepinit;
+       tp->t_keepidle = tcp_keepidle;
+       tp->t_keepintvl = tcp_keepintvl;
+       tp->t_keepcnt = tcp_keepcnt;
+       tp->t_maxidle = tp->t_keepintvl * tp->t_keepcnt;
+
        if (tcp_do_rfc1323)
                tp->t_flags = (TF_REQ_SCALE | TF_REQ_TSTMP);
        tp->t_inpcb = inp;      /* XXX */
@@ -747,45 +769,28 @@ tcp_drop(struct tcpcb *tp, int error)
 
 #ifdef SMP
 
-struct netmsg_remwildcard {
+struct netmsg_listen_detach {
        struct netmsg_base      base;
-       struct inpcb            *nm_inp;
-       struct inpcbinfo        *nm_pcbinfo;
-#if defined(INET6)
-       int                     nm_isinet6;
-#else
-       int                     nm_unused01;
-#endif
+       struct tcpcb            *nm_tp;
 };
 
-/*
- * Wildcard inpcb's on SMP boxes must be removed from all cpus before the
- * inp can be detached.  We do this by cycling through the cpus, ending up
- * on the cpu controlling the inp last and then doing the disconnect.
- */
 static void
-in_pcbremwildcardhash_handler(netmsg_t msg)
+tcp_listen_detach_handler(netmsg_t msg)
 {
-       struct netmsg_remwildcard *nmsg = (struct netmsg_remwildcard *)msg;
-       int cpu;
+       struct netmsg_listen_detach *nmsg = (struct netmsg_listen_detach *)msg;
+       struct tcpcb *tp = nmsg->nm_tp;
+       int cpu = mycpuid, nextcpu;
 
-       cpu = nmsg->nm_pcbinfo->cpu;
+       if (tp->t_flags & TF_LISTEN)
+               syncache_destroy(tp);
 
-       if (cpu == nmsg->nm_inp->inp_pcbinfo->cpu) {
-               /* note: detach removes any wildcard hash entry */
-#ifdef INET6
-               if (nmsg->nm_isinet6)
-                       in6_pcbdetach(nmsg->nm_inp);
-               else
-#endif
-                       in_pcbdetach(nmsg->nm_inp);
+       in_pcbremwildcardhash_oncpu(tp->t_inpcb, &tcbinfo[cpu]);
+
+       nextcpu = cpu + 1;
+       if (nextcpu < ncpus2)
+               lwkt_forwardmsg(cpu_portfn(nextcpu), &nmsg->base.lmsg);
+       else
                lwkt_replymsg(&nmsg->base.lmsg, 0);
-       } else {
-               in_pcbremwildcardhash_oncpu(nmsg->nm_inp, nmsg->nm_pcbinfo);
-               cpu = (cpu + 1) % ncpus2;
-               nmsg->nm_pcbinfo = &tcbinfo[cpu];
-               lwkt_forwardmsg(cpu_portfn(cpu), &nmsg->base.lmsg);
-       }
 }
 
 #endif
@@ -804,9 +809,6 @@ tcp_close(struct tcpcb *tp)
        struct socket *so = inp->inp_socket;
        struct rtentry *rt;
        boolean_t dosavessthresh;
-#ifdef SMP
-       int cpu;
-#endif
 #ifdef INET6
        boolean_t isipv6 = ((inp->inp_vflag & INP_IPV6) != 0);
        boolean_t isafinet6 = (INP_CHECK_SOCKAF(so, AF_INET6) != 0);
@@ -814,17 +816,41 @@ tcp_close(struct tcpcb *tp)
        const boolean_t isipv6 = FALSE;
 #endif
 
+#ifdef SMP
        /*
-        * The tp is not instantly destroyed in the wildcard case.  Setting
-        * the state to TCPS_TERMINATING will prevent the TCP stack from
-        * messing with it, though it should be noted that this change may
-        * not take effect on other cpus until we have chained the wildcard
-        * hash removal.
+        * INP_WILDCARD_MP indicates that listen(2) has been called on
+        * this socket.  This implies:
+        * - A wildcard inp's hash is replicated for each protocol thread.
+        * - Syncache for this inp grows independently in each protocol
+        *   thread.
+        * - There is more than one cpu
+        *
+        * We have to chain a message to the rest of the protocol threads
+        * to cleanup the wildcard hash and the syncache.  The cleanup
+        * in the current protocol thread is defered till the end of this
+        * function.
         *
-        * XXX we currently depend on the BGL to synchronize the tp->t_state
-        * update and prevent other tcp protocol threads from accepting new
-        * connections on the listen socket we might be trying to close down.
+        * NOTE:
+        * After cleanup the inp's hash and syncache entries, this inp will
+        * no longer be available to the rest of the protocol threads, so we
+        * are safe to whack the inp in the following code.
         */
+       if (inp->inp_flags & INP_WILDCARD_MP) {
+               struct netmsg_listen_detach nmsg;
+
+               KKASSERT(so->so_port == cpu_portfn(0));
+               KKASSERT(&curthread->td_msgport == cpu_portfn(0));
+               KKASSERT(inp->inp_pcbinfo == &tcbinfo[0]);
+
+               netmsg_init(&nmsg.base, NULL, &curthread->td_msgport,
+                           MSGF_PRIORITY, tcp_listen_detach_handler);
+               nmsg.nm_tp = tp;
+               lwkt_domsg(cpu_portfn(1), &nmsg.base.lmsg, 0);
+
+               inp->inp_flags &= ~INP_WILDCARD_MP;
+       }
+#endif
+
        KKASSERT(tp->t_state != TCPS_TERMINATING);
        tp->t_state = TCPS_TERMINATING;
 
@@ -948,7 +974,7 @@ no_valid_rt:
        while((q = LIST_FIRST(&tp->t_segq)) != NULL) {
                LIST_REMOVE(q, tqe_q);
                m_freem(q->tqe_m);
-               FREE(q, M_TSEGQ);
+               kfree(q, M_TSEGQ);
                atomic_add_int(&tcp_reass_qsize, -1);
        }
        /* throw away SACK blocks in scoreboard*/
@@ -960,45 +986,21 @@ no_valid_rt:
        /* note: pcb detached later on */
 
        tcp_destroy_timermsg(tp);
-       if (tp->t_flags & TF_SYNCACHE)
+
+       if (tp->t_flags & TF_LISTEN)
                syncache_destroy(tp);
 
        /*
-        * Discard the inp.  In the SMP case a wildcard inp's hash (created
-        * by a listen socket or an INADDR_ANY udp socket) is replicated
-        * for each protocol thread and must be removed in the context of
-        * that thread.  This is accomplished by chaining the message
-        * through the cpus.
-        *
-        * If the inp is not wildcarded we simply detach, which will remove
-        * the any hashes still present for this inp.
+        * NOTE:
+        * pcbdetach removes any wildcard hash entry on the current CPU.
         */
-#ifdef SMP
-       if (inp->inp_flags & INP_WILDCARD_MP) {
-               struct netmsg_remwildcard *nmsg;
-
-               cpu = (inp->inp_pcbinfo->cpu + 1) % ncpus2;
-               nmsg = kmalloc(sizeof(struct netmsg_remwildcard),
-                              M_LWKTMSG, M_INTWAIT);
-               netmsg_init(&nmsg->base, NULL, &netisr_afree_rport,
-                           0, in_pcbremwildcardhash_handler);
-#ifdef INET6
-               nmsg->nm_isinet6 = isafinet6;
-#endif
-               nmsg->nm_inp = inp;
-               nmsg->nm_pcbinfo = &tcbinfo[cpu];
-               lwkt_sendmsg(cpu_portfn(cpu), &nmsg->base.lmsg);
-       } else
-#endif
-       {
-               /* note: detach removes any wildcard hash entry */
 #ifdef INET6
-               if (isafinet6)
-                       in6_pcbdetach(inp);
-               else
+       if (isafinet6)
+               in6_pcbdetach(inp);
+       else
 #endif
-                       in_pcbdetach(inp);
-       }
+               in_pcbdetach(inp);
+
        tcpstat.tcps_closed++;
        return (NULL);
 }
@@ -1024,7 +1026,7 @@ tcp_drain_oncpu(struct inpcbhead *head)
                    (te = LIST_FIRST(&tcpb->t_segq)) != NULL) {
                        LIST_REMOVE(te, tqe_q);
                        m_freem(te->tqe_m);
-                       FREE(te, M_TSEGQ);
+                       kfree(te, M_TSEGQ);
                        atomic_add_int(&tcp_reass_qsize, -1);
                        /* retry */
                } else {
@@ -1970,6 +1972,52 @@ tcp_xmit_bandwidth_limit(struct tcpcb *tp, tcp_seq ack_seq)
        tp->snd_bwnd = bwnd;
 }
 
+u_long
+tcp_initial_window(const struct tcpcb *tp)
+{
+       if (tcp_do_rfc3390) {
+               /*
+                * RFC3390:
+                * "If the SYN or SYN/ACK is lost, the initial window
+                *  used by a sender after a correctly transmitted SYN
+                *  MUST be one segment consisting of MSS bytes."
+                *
+                * However, we do something a little bit more aggressive
+                * then RFC3390 here:
+                * - Only if time spent in the SYN or SYN|ACK retransmition
+                *   >= 3 seconds, the IW is reduced.  We do this mainly
+                *   because when RFC3390 is published, the initial RTO is
+                *   still 3 seconds (the threshold we test here), while
+                *   after RFC6298, the initial RTO is 1 second.  This
+                *   behaviour probably still falls within the spirit of
+                *   RFC3390.
+                * - When IW is reduced, 2*MSS is used instead of 1*MSS.
+                *   Mainly to avoid sender and receiver deadlock until
+                *   delayed ACK timer expires.  And even RFC2581 does not
+                *   try to reduce IW upon SYN or SYN|ACK retransmition
+                *   timeout.
+                *
+                * See also:
+                * http://tools.ietf.org/html/draft-ietf-tcpm-initcwnd-03
+                */
+               if (tp->t_rxtsyn >= TCPTV_RTOBASE3) {
+                       return (2 * tp->t_maxseg);
+               } else {
+                       return min(tcp_iw_maxsegs * tp->t_maxseg,
+                                  max(2 * tp->t_maxseg,
+                                      tcp_iw_capsegs * 1460));
+               }
+       } else {
+               /*
+                * Even RFC2581 (back to 1999) allows 2*SMSS IW.
+                *
+                * Mainly to avoid sender and receiver deadlock
+                * until delayed ACK timer expires.
+                */
+               return (2 * tp->t_maxseg);
+       }
+}
+
 #ifdef TCP_SIGNATURE
 /*
  * Compute TCP-MD5 hash of a TCP segment. (RFC2385)