Hi,

I am working on a diff to run UDP input in parallel.  Btrace kstack
analysis shows that SIP hash for PCB lookup is quite expensive.
When running in parallel we get lock contention on the PCB table
mutex.

So it results in better performance to calculate the hash value
before taking the mutex.  Code gets a bit more complicated as I
have to pass the value around.

The hash secret has to be constant as hash calculation must not
depend on values protected by the table mutex.  I see no security
benefit in reseeding when the hash table gets resized.

Analysis also shows that asserting a rw_lock while holding a
mutex is a bit expensive.  Just remove the netlock assert.

ok?

bluhm

Index: netinet/in_pcb.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.276
diff -u -p -r1.276 in_pcb.c
--- netinet/in_pcb.c    3 Oct 2022 16:43:52 -0000       1.276
+++ netinet/in_pcb.c    22 Jun 2023 06:26:14 -0000
@@ -121,15 +121,15 @@ struct baddynamicports rootonlyports;
 struct pool inpcb_pool;
 
 void   in_pcbhash_insert(struct inpcb *);
-struct inpcb *in_pcbhash_lookup(struct inpcbtable *, u_int,
+struct inpcb *in_pcbhash_lookup(struct inpcbtable *, uint64_t, u_int,
     const struct in_addr *, u_short, const struct in_addr *, u_short);
 int    in_pcbresize(struct inpcbtable *, int);
 
 #define        INPCBHASH_LOADFACTOR(_x)        (((_x) * 3) / 4)
 
-struct inpcbhead *in_pcbhash(struct inpcbtable *, u_int,
+uint64_t in_pcbhash(struct inpcbtable *, u_int,
     const struct in_addr *, u_short, const struct in_addr *, u_short);
-struct inpcbhead *in_pcblhash(struct inpcbtable *, u_int, u_short);
+uint64_t in_pcblhash(struct inpcbtable *, u_int, u_short);
 
 /*
  * in_pcb is used for inet and inet6.  in6_pcb only contains special
@@ -142,7 +142,7 @@ in_init(void)
            IPL_SOFTNET, 0, "inpcb", NULL);
 }
 
-struct inpcbhead *
+uint64_t
 in_pcbhash(struct inpcbtable *table, u_int rdomain,
     const struct in_addr *faddr, u_short fport,
     const struct in_addr *laddr, u_short lport)
@@ -156,11 +156,10 @@ in_pcbhash(struct inpcbtable *table, u_i
        SipHash24_Update(&ctx, &fport, sizeof(fport));
        SipHash24_Update(&ctx, laddr, sizeof(*laddr));
        SipHash24_Update(&ctx, &lport, sizeof(lport));
-
-       return (&table->inpt_hashtbl[SipHash24_End(&ctx) & table->inpt_mask]);
+       return SipHash24_End(&ctx);
 }
 
-struct inpcbhead *
+uint64_t
 in_pcblhash(struct inpcbtable *table, u_int rdomain, u_short lport)
 {
        SIPHASH_CTX ctx;
@@ -169,8 +168,7 @@ in_pcblhash(struct inpcbtable *table, u_
        SipHash24_Init(&ctx, &table->inpt_lkey);
        SipHash24_Update(&ctx, &nrdom, sizeof(nrdom));
        SipHash24_Update(&ctx, &lport, sizeof(lport));
-
-       return (&table->inpt_lhashtbl[SipHash24_End(&ctx) & table->inpt_lmask]);
+       return SipHash24_End(&ctx);
 }
 
 void
@@ -816,11 +814,14 @@ in_pcblookup_local(struct inpcbtable *ta
        struct in6_addr *laddr6 = (struct in6_addr *)laddrp;
 #endif
        struct inpcbhead *head;
+       uint64_t lhash;
        u_int rdomain;
 
        rdomain = rtable_l2(rtable);
+       lhash = in_pcblhash(table, rdomain, lport);
+
        mtx_enter(&table->inpt_mtx);
-       head = in_pcblhash(table, rdomain, lport);
+       head = &table->inpt_lhashtbl[lhash & table->inpt_lmask];
        LIST_FOREACH(inp, head, inp_lhash) {
                if (rtable_l2(inp->inp_rtableid) != rdomain)
                        continue;
@@ -1056,37 +1057,38 @@ in_pcbhash_insert(struct inpcb *inp)
 {
        struct inpcbtable *table = inp->inp_table;
        struct inpcbhead *head;
+       uint64_t hash, lhash;
 
-       NET_ASSERT_LOCKED();
        MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
 
-       head = in_pcblhash(table, inp->inp_rtableid, inp->inp_lport);
+       lhash = in_pcblhash(table, inp->inp_rtableid, inp->inp_lport);
+       head = &table->inpt_lhashtbl[lhash & table->inpt_lmask];
        LIST_INSERT_HEAD(head, inp, inp_lhash);
 #ifdef INET6
        if (inp->inp_flags & INP_IPV6)
-               head = in6_pcbhash(table, rtable_l2(inp->inp_rtableid),
+               hash = in6_pcbhash(table, rtable_l2(inp->inp_rtableid),
                    &inp->inp_faddr6, inp->inp_fport,
                    &inp->inp_laddr6, inp->inp_lport);
        else
 #endif /* INET6 */
-               head = in_pcbhash(table, rtable_l2(inp->inp_rtableid),
+               hash = in_pcbhash(table, rtable_l2(inp->inp_rtableid),
                    &inp->inp_faddr, inp->inp_fport,
                    &inp->inp_laddr, inp->inp_lport);
+       head = &table->inpt_hashtbl[hash & table->inpt_mask];
        LIST_INSERT_HEAD(head, inp, inp_hash);
 }
 
 struct inpcb *
-in_pcbhash_lookup(struct inpcbtable *table, u_int rdomain,
+in_pcbhash_lookup(struct inpcbtable *table, uint64_t hash, u_int rdomain,
     const struct in_addr *faddr, u_short fport,
     const struct in_addr *laddr, u_short lport)
 {
        struct inpcbhead *head;
        struct inpcb *inp;
 
-       NET_ASSERT_LOCKED();
        MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
 
-       head = in_pcbhash(table, rdomain, faddr, fport, laddr, lport);
+       head = &table->inpt_hashtbl[hash & table->inpt_mask];
        LIST_FOREACH(inp, head, inp_hash) {
 #ifdef INET6
                if (ISSET(inp->inp_flags, INP_IPV6))
@@ -1140,8 +1142,6 @@ in_pcbresize(struct inpcbtable *table, i
        table->inpt_mask = nmask;
        table->inpt_lmask = nlmask;
        table->inpt_size = hashsize;
-       arc4random_buf(&table->inpt_key, sizeof(table->inpt_key));
-       arc4random_buf(&table->inpt_lkey, sizeof(table->inpt_lkey));
 
        TAILQ_FOREACH(inp, &table->inpt_queue, inp_queue) {
                LIST_REMOVE(inp, inp_lhash);
@@ -1172,13 +1172,18 @@ in_pcblookup(struct inpcbtable *table, s
     u_int fport, struct in_addr laddr, u_int lport, u_int rtable)
 {
        struct inpcb *inp;
+       uint64_t hash;
        u_int rdomain;
 
        rdomain = rtable_l2(rtable);
+       hash = in_pcbhash(table, rdomain, &faddr, fport, &laddr, lport);
+
        mtx_enter(&table->inpt_mtx);
-       inp = in_pcbhash_lookup(table, rdomain, &faddr, fport, &laddr, lport);
+       inp = in_pcbhash_lookup(table, hash, rdomain,
+           &faddr, fport, &laddr, lport);
        in_pcbref(inp);
        mtx_leave(&table->inpt_mtx);
+
 #ifdef DIAGNOSTIC
        if (inp == NULL && in_pcbnotifymiss) {
                printf("%s: faddr=%08x fport=%d laddr=%08x lport=%d rdom=%u\n",
@@ -1202,6 +1207,7 @@ in_pcblookup_listen(struct inpcbtable *t
 {
        const struct in_addr *key1, *key2;
        struct inpcb *inp;
+       uint64_t hash;
        u_int16_t lport = lport_arg;
        u_int rdomain;
 
@@ -1239,14 +1245,20 @@ in_pcblookup_listen(struct inpcbtable *t
 #endif
 
        rdomain = rtable_l2(rtable);
+       hash = in_pcbhash(table, rdomain, &zeroin_addr, 0, key1, lport);
+
        mtx_enter(&table->inpt_mtx);
-       inp = in_pcbhash_lookup(table, rdomain, &zeroin_addr, 0, key1, lport);
+       inp = in_pcbhash_lookup(table, hash, rdomain,
+           &zeroin_addr, 0, key1, lport);
        if (inp == NULL && key1->s_addr != key2->s_addr) {
-               inp = in_pcbhash_lookup(table, rdomain,
+               hash = in_pcbhash(table, rdomain,
+                   &zeroin_addr, 0, key2, lport);
+               inp = in_pcbhash_lookup(table, hash, rdomain,
                    &zeroin_addr, 0, key2, lport);
        }
        in_pcbref(inp);
        mtx_leave(&table->inpt_mtx);
+
 #ifdef DIAGNOSTIC
        if (inp == NULL && in_pcbnotifymiss) {
                printf("%s: laddr=%08x lport=%d rdom=%u\n",
Index: netinet/in_pcb.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/in_pcb.h,v
retrieving revision 1.135
diff -u -p -r1.135 in_pcb.h
--- netinet/in_pcb.h    3 Oct 2022 16:43:52 -0000       1.135
+++ netinet/in_pcb.h    21 Jun 2023 18:43:30 -0000
@@ -172,7 +172,7 @@ struct inpcbtable {
        TAILQ_HEAD(inpthead, inpcb) inpt_queue; /* [t] inet PCB queue */
        struct  inpcbhead *inpt_hashtbl;        /* [t] local and foreign hash */
        struct  inpcbhead *inpt_lhashtbl;       /* [t] local port hash */
-       SIPHASH_KEY inpt_key, inpt_lkey;        /* [t] secrets for hashes */
+       SIPHASH_KEY inpt_key, inpt_lkey;        /* [I] secrets for hashes */
        u_long  inpt_mask, inpt_lmask;          /* [t] hash masks */
        int     inpt_count, inpt_size;          /* [t] queue count, hash size */
 };
@@ -294,8 +294,7 @@ struct inpcb *
         in_pcblookup_listen(struct inpcbtable *, struct in_addr, u_int,
            struct mbuf *, u_int);
 #ifdef INET6
-struct inpcbhead *
-        in6_pcbhash(struct inpcbtable *, u_int, const struct in6_addr *,
+uint64_t in6_pcbhash(struct inpcbtable *, u_int, const struct in6_addr *,
            u_short, const struct in6_addr *, u_short);
 struct inpcb *
         in6_pcblookup(struct inpcbtable *, const struct in6_addr *,
Index: netinet6/in6_pcb.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/in6_pcb.c,v
retrieving revision 1.123
diff -u -p -r1.123 in6_pcb.c
--- netinet6/in6_pcb.c  3 Sep 2022 22:43:38 -0000       1.123
+++ netinet6/in6_pcb.c  21 Jun 2023 18:43:30 -0000
@@ -126,10 +126,10 @@
 
 const struct in6_addr zeroin6_addr;
 
-struct inpcb *in6_pcbhash_lookup(struct inpcbtable *, u_int,
+struct inpcb *in6_pcbhash_lookup(struct inpcbtable *, uint64_t, u_int,
     const struct in6_addr *, u_short, const struct in6_addr *, u_short);
 
-struct inpcbhead *
+uint64_t
 in6_pcbhash(struct inpcbtable *table, u_int rdomain,
     const struct in6_addr *faddr, u_short fport,
     const struct in6_addr *laddr, u_short lport)
@@ -143,8 +143,7 @@ in6_pcbhash(struct inpcbtable *table, u_
        SipHash24_Update(&ctx, &fport, sizeof(fport));
        SipHash24_Update(&ctx, laddr, sizeof(*laddr));
        SipHash24_Update(&ctx, &lport, sizeof(lport));
-
-       return (&table->inpt_hashtbl[SipHash24_End(&ctx) & table->inpt_mask]);
+       return SipHash24_End(&ctx);
 }
 
 int
@@ -541,7 +540,7 @@ in6_pcbnotify(struct inpcbtable *table, 
 }
 
 struct inpcb *
-in6_pcbhash_lookup(struct inpcbtable *table, u_int rdomain,
+in6_pcbhash_lookup(struct inpcbtable *table, uint64_t hash, u_int rdomain,
     const struct in6_addr *faddr, u_short fport,
     const struct in6_addr *laddr, u_short lport)
 {
@@ -551,7 +550,7 @@ in6_pcbhash_lookup(struct inpcbtable *ta
        NET_ASSERT_LOCKED();
        MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
 
-       head = in6_pcbhash(table, rdomain, faddr, fport, laddr, lport);
+       head = &table->inpt_hashtbl[hash & table->inpt_mask];
        LIST_FOREACH(inp, head, inp_hash) {
                if (!ISSET(inp->inp_flags, INP_IPV6))
                        continue;
@@ -581,13 +580,18 @@ in6_pcblookup(struct inpcbtable *table, 
     u_int fport, const struct in6_addr *laddr, u_int lport, u_int rtable)
 {
        struct inpcb *inp;
+       uint64_t hash;
        u_int rdomain;
 
        rdomain = rtable_l2(rtable);
+       hash = in6_pcbhash(table, rdomain, faddr, fport, laddr, lport);
+
        mtx_enter(&table->inpt_mtx);
-       inp = in6_pcbhash_lookup(table, rdomain, faddr, fport, laddr, lport);
+       inp = in6_pcbhash_lookup(table, hash, rdomain,
+           faddr, fport, laddr, lport);
        in_pcbref(inp);
        mtx_leave(&table->inpt_mtx);
+
 #ifdef DIAGNOSTIC
        if (inp == NULL && in_pcbnotifymiss) {
                printf("%s: faddr= fport=%d laddr= lport=%d rdom=%u\n",
@@ -603,6 +607,7 @@ in6_pcblookup_listen(struct inpcbtable *
 {
        const struct in6_addr *key1, *key2;
        struct inpcb *inp;
+       uint64_t hash;
        u_int rdomain;
 
        key1 = laddr;
@@ -636,14 +641,20 @@ in6_pcblookup_listen(struct inpcbtable *
 #endif
 
        rdomain = rtable_l2(rtable);
+       hash = in6_pcbhash(table, rdomain, &zeroin6_addr, 0, key1, lport);
+
        mtx_enter(&table->inpt_mtx);
-       inp = in6_pcbhash_lookup(table, rdomain, &zeroin6_addr, 0, key1, lport);
+       inp = in6_pcbhash_lookup(table, hash, rdomain,
+           &zeroin6_addr, 0, key1, lport);
        if (inp == NULL && ! IN6_ARE_ADDR_EQUAL(key1, key2)) {
-               inp = in6_pcbhash_lookup(table, rdomain,
+               hash = in6_pcbhash(table, rdomain,
+                   &zeroin6_addr, 0, key2, lport);
+               inp = in6_pcbhash_lookup(table, hash, rdomain,
                    &zeroin6_addr, 0, key2, lport);
        }
        in_pcbref(inp);
        mtx_leave(&table->inpt_mtx);
+
 #ifdef DIAGNOSTIC
        if (inp == NULL && in_pcbnotifymiss) {
                printf("%s: laddr= lport=%d rdom=%u\n",

Reply via email to