On Sat, Mar 19, 2016 at 10:41:06PM +0100, Alexander Bluhm wrote:
> The drawback is that the the cache lookup has to be done in two syn
> caches when an ACK arrives.

This can be prevented most of the time.  Switch the cache only after
100000 uses.  So most of the time the passive cache is empty and
then no lookup is done.

bluhm

Index: netinet/tcp_input.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/tcp_input.c,v
retrieving revision 1.314
diff -u -p -r1.314 tcp_input.c
--- netinet/tcp_input.c 7 Mar 2016 18:44:00 -0000       1.314
+++ netinet/tcp_input.c 20 Mar 2016 17:47:08 -0000
@@ -3260,40 +3260,46 @@ tcp_mss_adv(struct mbuf *m, int af)
 int    tcp_syn_cache_size = TCP_SYN_HASH_SIZE;
 int    tcp_syn_cache_limit = TCP_SYN_HASH_SIZE*TCP_SYN_BUCKET_SIZE;
 int    tcp_syn_bucket_limit = 3*TCP_SYN_BUCKET_SIZE;
-int    tcp_syn_cache_count;
-struct syn_cache_head tcp_syn_cache[TCP_SYN_HASH_SIZE];
-u_int32_t tcp_syn_hash[5];
-
-#define SYN_HASH(sa, sp, dp) \
-       (((sa)->s_addr ^ tcp_syn_hash[0]) *                             \
-       (((((u_int32_t)(dp))<<16) + ((u_int32_t)(sp))) ^ tcp_syn_hash[4]))
+int    tcp_syn_use_limit = 100000;
+
+struct syn_cache_set {
+        struct         syn_cache_head scs_buckethead[TCP_SYN_HASH_SIZE];
+        int            scs_count;
+        int            scs_use;
+        u_int32_t      scs_random[5];
+} tcp_syn_cache[2];
+int tcp_syn_cache_active;
+
+#define SYN_HASH(sa, sp, dp, rand) \
+       (((sa)->s_addr ^ (rand)[0]) *                           \
+       (((((u_int32_t)(dp))<<16) + ((u_int32_t)(sp))) ^ (rand)[4]))
 #ifndef INET6
-#define        SYN_HASHALL(hash, src, dst) \
+#define        SYN_HASHALL(hash, src, dst, rand) \
 do {                                                                   \
        hash = SYN_HASH(&satosin(src)->sin_addr,                        \
                satosin(src)->sin_port,                                 \
-               satosin(dst)->sin_port);                                \
+               satosin(dst)->sin_port, (rand));                        \
 } while (/*CONSTCOND*/ 0)
 #else
-#define SYN_HASH6(sa, sp, dp) \
-       (((sa)->s6_addr32[0] ^ tcp_syn_hash[0]) *                       \
-       ((sa)->s6_addr32[1] ^ tcp_syn_hash[1]) *                        \
-       ((sa)->s6_addr32[2] ^ tcp_syn_hash[2]) *                        \
-       ((sa)->s6_addr32[3] ^ tcp_syn_hash[3]) *                        \
-       (((((u_int32_t)(dp))<<16) + ((u_int32_t)(sp))) ^ tcp_syn_hash[4]))
+#define SYN_HASH6(sa, sp, dp, rand) \
+       (((sa)->s6_addr32[0] ^ (rand)[0]) *                     \
+       ((sa)->s6_addr32[1] ^ (rand)[1]) *                      \
+       ((sa)->s6_addr32[2] ^ (rand)[2]) *                      \
+       ((sa)->s6_addr32[3] ^ (rand)[3]) *                      \
+       (((((u_int32_t)(dp))<<16) + ((u_int32_t)(sp))) ^ (rand)[4]))
 
-#define SYN_HASHALL(hash, src, dst) \
+#define SYN_HASHALL(hash, src, dst, rand) \
 do {                                                                   \
        switch ((src)->sa_family) {                                     \
        case AF_INET:                                                   \
                hash = SYN_HASH(&satosin(src)->sin_addr,                \
                        satosin(src)->sin_port,                         \
-                       satosin(dst)->sin_port);                        \
+                       satosin(dst)->sin_port, (rand));                \
                break;                                                  \
        case AF_INET6:                                                  \
                hash = SYN_HASH6(&satosin6(src)->sin6_addr,             \
                        satosin6(src)->sin6_port,                       \
-                       satosin6(dst)->sin6_port);                      \
+                       satosin6(dst)->sin6_port, (rand));              \
                break;                                                  \
        default:                                                        \
                hash = 0;                                               \
@@ -3305,13 +3311,12 @@ void
 syn_cache_rm(struct syn_cache *sc)
 {
        sc->sc_flags |= SCF_DEAD;
-       TAILQ_REMOVE(&tcp_syn_cache[sc->sc_bucketidx].sch_bucket,
-           sc, sc_bucketq);
+       TAILQ_REMOVE(&sc->sc_buckethead->sch_bucket, sc, sc_bucketq);
        sc->sc_tp = NULL;
        LIST_REMOVE(sc, sc_tpq);
-       tcp_syn_cache[sc->sc_bucketidx].sch_length--;
+       sc->sc_buckethead->sch_length--;
        timeout_del(&sc->sc_timer);
-       tcp_syn_cache_count--;
+       sc->sc_set->scs_count--;
 }
 
 void
@@ -3351,8 +3356,10 @@ syn_cache_init(void)
        int i;
 
        /* Initialize the hash buckets. */
-       for (i = 0; i < tcp_syn_cache_size; i++)
-               TAILQ_INIT(&tcp_syn_cache[i].sch_bucket);
+       for (i = 0; i < tcp_syn_cache_size; i++) {
+               TAILQ_INIT(&tcp_syn_cache[0].scs_buckethead[i].sch_bucket);
+               TAILQ_INIT(&tcp_syn_cache[1].scs_buckethead[i].sch_bucket);
+       }
 
        /* Initialize the syn cache pool. */
        pool_init(&syn_cache_pool, sizeof(struct syn_cache), 0, 0, 0,
@@ -3363,26 +3370,33 @@ syn_cache_init(void)
 void
 syn_cache_insert(struct syn_cache *sc, struct tcpcb *tp)
 {
+       struct syn_cache_set *set = &tcp_syn_cache[tcp_syn_cache_active];
        struct syn_cache_head *scp;
        struct syn_cache *sc2;
        int s;
 
+       s = splsoftnet();
+
        /*
         * If there are no entries in the hash table, reinitialize
-        * the hash secrets.
+        * the hash secrets.  To avoid useless cache swaps and
+        * and reinitialization, use it until the limit is reached.
         */
-       if (tcp_syn_cache_count == 0)
-               arc4random_buf(tcp_syn_hash, sizeof(tcp_syn_hash));
+       if (set->scs_count == 0 && set->scs_use <= 0) {
+               arc4random_buf(set->scs_random, sizeof(set->scs_random));
+               set->scs_use = tcp_syn_use_limit;
+               tcpstat.tcps_sc_seedrandom++;
+       }
 
-       SYN_HASHALL(sc->sc_hash, &sc->sc_src.sa, &sc->sc_dst.sa);
-       sc->sc_bucketidx = sc->sc_hash % tcp_syn_cache_size;
-       scp = &tcp_syn_cache[sc->sc_bucketidx];
+       SYN_HASHALL(sc->sc_hash, &sc->sc_src.sa, &sc->sc_dst.sa,
+           set->scs_random);
+       scp = &set->scs_buckethead[sc->sc_hash % tcp_syn_cache_size];
+       sc->sc_buckethead = scp;
 
        /*
         * Make sure that we don't overflow the per-bucket
         * limit or the total cache size limit.
         */
-       s = splsoftnet();
        if (scp->sch_length >= tcp_syn_bucket_limit) {
                tcpstat.tcps_sc_bucketoverflow++;
                /*
@@ -3400,7 +3414,7 @@ syn_cache_insert(struct syn_cache *sc, s
 #endif
                syn_cache_rm(sc2);
                syn_cache_put(sc2);
-       } else if (tcp_syn_cache_count >= tcp_syn_cache_limit) {
+       } else if (set->scs_count >= tcp_syn_cache_limit) {
                struct syn_cache_head *scp2, *sce;
 
                tcpstat.tcps_sc_overflowed++;
@@ -3414,10 +3428,10 @@ syn_cache_insert(struct syn_cache *sc, s
                 */
                scp2 = scp;
                if (TAILQ_EMPTY(&scp2->sch_bucket)) {
-                       sce = &tcp_syn_cache[tcp_syn_cache_size];
+                       sce = &set->scs_buckethead[tcp_syn_cache_size];
                        for (++scp2; scp2 != scp; scp2++) {
                                if (scp2 >= sce)
-                                       scp2 = &tcp_syn_cache[0];
+                                       scp2 = &set->scs_buckethead[0];
                                if (! TAILQ_EMPTY(&scp2->sch_bucket))
                                        break;
                        }
@@ -3449,9 +3463,20 @@ syn_cache_insert(struct syn_cache *sc, s
        /* Put it into the bucket. */
        TAILQ_INSERT_TAIL(&scp->sch_bucket, sc, sc_bucketq);
        scp->sch_length++;
-       tcp_syn_cache_count++;
+       sc->sc_set = set;
+       set->scs_count++;
+       set->scs_use--;
 
        tcpstat.tcps_sc_added++;
+
+       /*
+        * If the active cache has exceeded its use limit and
+        * the passive syn cache is empty, exchange their roles.
+        */
+       if (set->scs_use <= 0 &&
+           tcp_syn_cache[!tcp_syn_cache_active].scs_count == 0)
+               tcp_syn_cache_active = !tcp_syn_cache_active;
+
        splx(s);
 }
 
@@ -3546,25 +3571,31 @@ struct syn_cache *
 syn_cache_lookup(struct sockaddr *src, struct sockaddr *dst,
     struct syn_cache_head **headp, u_int rtableid)
 {
+       struct syn_cache_set *sets[2];
        struct syn_cache *sc;
        struct syn_cache_head *scp;
        u_int32_t hash;
+       int i;
 
        splsoftassert(IPL_SOFTNET);
 
-       if (tcp_syn_cache_count == 0)
-               return (NULL);
-
-       SYN_HASHALL(hash, src, dst);
-       scp = &tcp_syn_cache[hash % tcp_syn_cache_size];
-       *headp = scp;
-       TAILQ_FOREACH(sc, &scp->sch_bucket, sc_bucketq) {
-               if (sc->sc_hash != hash)
+       /* Check the active cache first, the passive cache is likely emtpy. */
+       sets[0] = &tcp_syn_cache[tcp_syn_cache_active];
+       sets[1] = &tcp_syn_cache[!tcp_syn_cache_active];
+       for (i = 0; i < 2; i++) {
+               if (sets[i]->scs_count == 0)
                        continue;
-               if (!bcmp(&sc->sc_src, src, src->sa_len) &&
-                   !bcmp(&sc->sc_dst, dst, dst->sa_len) &&
-                   rtable_l2(rtableid) == rtable_l2(sc->sc_rtableid))
-                       return (sc);
+               SYN_HASHALL(hash, src, dst, sets[i]->scs_random);
+               scp = &sets[i]->scs_buckethead[hash % tcp_syn_cache_size];
+               *headp = scp;
+               TAILQ_FOREACH(sc, &scp->sch_bucket, sc_bucketq) {
+                       if (sc->sc_hash != hash)
+                               continue;
+                       if (!bcmp(&sc->sc_src, src, src->sa_len) &&
+                           !bcmp(&sc->sc_dst, dst, dst->sa_len) &&
+                           rtable_l2(rtableid) == rtable_l2(sc->sc_rtableid))
+                               return (sc);
+               }
        }
        return (NULL);
 }
Index: netinet/tcp_var.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/tcp_var.h,v
retrieving revision 1.109
diff -u -p -r1.109 tcp_var.h
--- netinet/tcp_var.h   27 Aug 2015 20:56:16 -0000      1.109
+++ netinet/tcp_var.h   20 Mar 2016 10:54:18 -0000
@@ -271,7 +271,8 @@ struct syn_cache {
 #define sc_route6      sc_route_u.route6
 #endif
        long sc_win;                            /* advertised window */
-       int sc_bucketidx;                       /* our bucket index */
+       struct syn_cache_head *sc_buckethead;   /* our bucket index */
+       struct syn_cache_set *sc_set;           /* our syn cache set */
        u_int32_t sc_hash;
        u_int32_t sc_timestamp;                 /* timestamp from SYN */
        u_int32_t sc_modulate;                  /* our timestamp modulator */
@@ -440,6 +441,7 @@ struct      tcpstat {
        u_int64_t tcps_sc_dropped;      /* # of SYNs dropped (no route/mem) */
        u_int64_t tcps_sc_collisions;   /* # of hash collisions */
        u_int64_t tcps_sc_retransmitted;/* # of retransmissions */
+       u_int64_t tcps_sc_seedrandom;   /* # of syn cache seeds with random */
 
        u_int64_t tcps_conndrained;     /* # of connections drained */
 

Reply via email to