SOCK_RAW and in_pcb.c

Gleb Smirnoff <[email protected]> Mon, 16 Mar 2026 11:16:37 -0700
Newsgroups gmane.os.freebsd.architechture
Message-ID <[email protected]>
  Hi,

the context and motivation of this email is that I'm working on improving
parallelism on connect(2) on TCP sockets.  UDP will be also affected as a side
effect :) The goal is to get rid of two fat locks that lock the inpcb database
for a given protocol - the inp_info_lock and inp_hash_lock.  In my working
branch I haved achieved getting rid of inp_info_lock and pushing
INP_HASH_WLOCK() out of TCP and UDP files into in_pcb.c:

https://github.com/freebsd/freebsd-src/compare/main...glebius:FreeBSD:inpcb

The following step would be to decompose this fat lock into per-slot locks. But
then I stumbled on SOCK_RAW.  The netinet/raw_ip.c is the last out of in_pcb.c
consumer of INP_HASH_WLOCK().

TLDR version: I want to divorce in_pcb.c and AF_INET/SOCK_RAW and
AF_INET6/SOCK_RAW.

Why did I come to such decision?  First, let's refresh on what SOCK_RAW input
routine is supposed to do.  It is supposed to deliver an incoming packet to
multiple destinations.  The best description of it is given in Stevens's UNIX
Network Programming (Section 28.4 in the last edition). Second, let's look at
the historical implementation of rip_input():

https://github.com/sergev/4.4BSD-Lite2/blob/master/usr/src/sys/netinet/raw_ip.c#L84

It is simple as 2 cents.  Now, let's take a look into our modern
raw_ip6.c:rip6_input().  Algorithmically it does the same.  However, a ton of
complexity is provided underneath - an SMR-protected UMA zone, port(!) hash and
other stuff that in_pcb.c does for TCP and UDP.  But we still end up with
scanning all sockets.  All of the complex in_pcb.c machinery was designed to
speed up the fast path in_pcblookup(), that finds a single inpcb that matches a
connection.  But in_pcblookup() is not applicable to SOCK_RAW, as there will
never be a single pcb that answers the question where "shall a datagram go?".

For IPv4's rip_input() there is one difference - it first scans a hash slot
with raw sockets that are protocol bound and also did bind(2) and did
connect(2), and only then scans all unbound or loosely bound sockets.  With
loosely bound I mean sockets that did either bind(2) or connect(2).  This
difference between rip_input() and rip6_input() originates from 9ed324c9a588f,
and this revision provides the only practical case when there are a lot of raw
sockets in a system - a VPN concentrator.  A better solution for the problem
solved by 9ed324c9a588f would be a kernel module that registers into
ip_protox[] and does its own optimized PPTP processing.  However, I won't call
9ed324c9a588f a wrong change.  The mpd5 port is still alive and with the modern
internet the popularity of different VPNs that run over different IP protocols
is growing. Although any specific VPN will benefit from a specific kernel
module, we still want to improve a case when we got thousands of tightely bound
raw sockets.  We should also extend improvements of 9ed324c9a588f to IPv6.

One other point I'd like to note is that speculatively the de2d47842e88 (SMR
for inpcbs) was a negative performance change for SOCK_RAW.  The inpcb
iteration with help of inp_next() supposedly is more expensive than historic
INP_INFO_RLOCK(); LIST_FOREACH(..., cause we lock every single inpcb.  This is
fine for TCP and UDP where iteration never happens in the fast path, rather we
are focused on iteration to never have affect on the fast path and we accept
that iteratation is more expensive.  But for SOCK_RAW iteration is required on
the fast path.

Here is the plan:
- a shared code between IPv4 and IPv6
- rawpcb database per VNET that has: a list of unbound pcbs and tree of
  src-bound pcbs.  We could have also a tree of dst-bound and src+dst-bound,
  but I would speculate that practical hosts/routers that have a large
  (local IP address count * raw socket count) product do not exist.  The
  current hash that serves only the src+dst+proto-bound pcbs, IMHO, is too
  restrictive and supposedly only mpd5 benefits from it.  I'd speculate that
  src-bound lookup database will not degrade mpd5 performance, but will
  provide improvements for other raw users.
- the database is protected by rwlock, that is rlocked on the fast path and
  wlocked by socket(2), bind(2) and connect(2).
- setsockopt(2) needs to take care to not use raw's so_pcb as inpcb any more

Any comments?

--
Gleb Smirnoff