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",