inpcb/in6pcb: Split port token
commit2524c2255f04b57788d6494f5a2f2426d3ed9d67
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Tue, 25 Mar 2014 12:50:53 +0000 (25 20:50 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 5 Apr 2014 09:57:42 +0000 (5 17:57 +0800)
tree63e1e10ce3b2f25d91d3a7d3d6b6ec0210ab4640
parent85325fa509ab6e21e3ba8f8099422ea9e10a462d
inpcb/in6pcb: Split port token

The original single local port space is devided into ncpus2 local port
space groups.  We denote local port space group as PG(N), N=[0, ncpus2).

Property of PG(N):
- PG(N) only contains local ports matching following condition:
  (host_order(port) & ncpus2_mask) == N
- PG(N) is protected by its own token.

On explicit local port bind(2) path and accept(2) path, PG(N) is selected
by using the local port already available (accept(2)) or supplied
(bind(2)):
    N = host_order(port) & ncpus2_mask

On implicit local port selection path (bind(2) and connect(2)), PG(N) is
selected and used in the following way:
    N = mycpuid;
    N1 = N;
again:
    if (find free port in PG(N)) {
        DONE;
    } else {
        N = (N + 1) & ncpus2_mask;
        if (N != N1)
            goto again;
        FAILED;
    }

PG(N) is now recorded in inpcb struct, so when inpcb is destroyed, we
know which port space group it should use.

On i7-3770 w/ Intel 82599ES, using tools/kq_connect_client:
Port token contention rate on each hyperthread is reduced from 120K/s to
40K/s.  Admittedly the contention rate is still high but it is much
better than before.

Now the major source of port token contention is the contention between
implicit local port select path and inpcb destroy path.  There may be a
way to choose local port which could hash the inpcb to the current CPU;
this needs more investigation.
sys/netinet/in_pcb.c
sys/netinet/in_pcb.h
sys/netinet/ip_divert.c
sys/netinet/raw_ip.c
sys/netinet/tcp_subr.c
sys/netinet/udp_usrreq.c
sys/netinet6/in6_pcb.c
sys/netinet6/in6_src.c