Page MenuHomeFreeBSD

unix: enqueue datagrams to a named peer without a temporary connection
Needs ReviewPublic

Authored by inquire_JohnEricson.me on Mon, Aug 10, 9:16 PM.
Tags
None
Referenced Files
F167428763: D58772.id183838.diff
Fri, Aug 21, 4:29 PM
F167427067: D58772.id183838.diff
Fri, Aug 21, 4:17 PM
Unknown Object (File)
Wed, Aug 19, 11:38 AM
Unknown Object (File)
Wed, Aug 19, 6:29 AM
Unknown Object (File)
Wed, Aug 19, 4:46 AM
Unknown Object (File)
Tue, Aug 18, 8:00 PM
Unknown Object (File)
Tue, Aug 18, 2:00 PM
Unknown Object (File)
Tue, Aug 18, 1:38 PM
Subscribers

Details

Reviewers
markj
glebius
Summary

Preface: What happened here was I was wondering if we could get rid of
the reference out parameter, and the LLM went in a different direction
than I imagined.

I thought the benefit would just be skipping some of the checks
about the local-side socket that unp_connectat starts with, since
uipc_sosend_dgram would always be leaving us in a more-known state.
The LLM agreed with that, though it is not clear to me that the elision
of some of those checks is totally legit. It also went further trying to
make uipc_sosend_dgram more stateless and
also skip the
unp_connect_peer final third of unp_connectat. That was unexpected
to me!

I consider it poor form to submit patches I don't fully understand, so
I am sorry to be violating my own precept. And certainly everyone should
feel free to ignore this one accordingly. Nonetheless I am submitting it
because
if it works as advertised --- both getting rid of the hacky
extra param, and perhaps improving performance/concurrency --- that's a
pretty neat double win.


uipc_sosend_dgram() reached a peer named by sendto(2) by calling
unp_connectat(), and undid it with unp_disconnect() a few lines later.
Nothing of the connection was used in between: the enqueue selects
&so2->so_rcv, the peer's shared receive buffer, precisely because the
send is unconnected, and the per-sender buffer linkage a connection
establishes is guarded by addr == NULL.

Resolve the peer directly instead. unp_resolve_peer() already returns a
referenced peer socket, so the path becomes: resolve, check the socket
type, lock the pair, enqueue, unlock and release. That spares every
unconnected datagram an unp_connect2() and unp_disconnect() pair.

It also removes the mechanism that existed only to serve this caller.
unp_connectat() no longer takes an out pointer to hand back a referenced
peer with its PCB left locked, and unp_connect_peer() no longer takes
return_peer_locked, this having been its only user.

Two errors the temporary connection used to raise are now raised
explicitly: an empty sun_path gives EINVAL, since AT_FDCWD cannot
name a peer by descriptor, and an already connected socket gives
EISCONN. dgram_sendto_empty_path and dgram_sendto_connected cover
them. dgram_sendto_self covers a socket naming itself, the one case
where the pair of PCBs to lock is a single PCB. Two others go away: a
concurrent connect(2) no longer fails the send with EALREADY, and the
send no longer sets UNP_CONNECTING, so it no longer serialises against
one.

Concurrent sends are unaffected, SOCK_IO_SEND_LOCK() being held across
the whole send: a second sendto(2) blocks there regardless. The sending
socket's unp_conn is no longer transiently set either, so
getpeername(2) on it can no longer observe a peer that is not there.

Signed-off-by: John Ericson <John.Ericson@Obsidian.Systems>
Assisted-by: Claude Code (Claude Opus 4.8 and Fable 5)

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 75508
Build 72391: arc lint + arc unit

Event Timeline

sys/kern/uipc_usrreq.c
2107

This is a bit bulky for the stack, but it's probably okay. But why not use SUNPATHLEN?

2131

Can we reorder the cleanup path so that this sorele() happens only there?

This looks like a nice improvement. I note in particular that connecting and disconnecting a datagram socket pair requires a global lock today (UNP_REF_LIST_LOCK()), so this should help concurrency a lot. I hope @glebius can take a look, as he's worked quite a bit on the data path here.

As far as I can see, the unp_refs list is only used by tools like netstat/sockstat/etc. for enumerating sockets, I don't see a problem if we just bypass that mechanism for an unconnected send(). Maybe I'm missing something.

I remember I disliked this temporary connection, but I don't remember why did I leave it as is. I will look deeper later.