Page MenuHomeFreeBSD

fcntl(F_SETFL): Don't unconditionally invoke FIONBIO and FIOASYNC
ClosedPublic

Authored by jhb on Sep 5 2025, 7:50 PM.
Tags
None
Referenced Files
Unknown Object (File)
Sun, Oct 12, 8:25 AM
Unknown Object (File)
Sun, Oct 12, 8:25 AM
Unknown Object (File)
Sun, Oct 12, 8:25 AM
Unknown Object (File)
Sun, Oct 12, 8:25 AM
Unknown Object (File)
Sat, Oct 11, 9:45 PM
Unknown Object (File)
Tue, Oct 7, 4:11 AM
Unknown Object (File)
Mon, Oct 6, 7:16 AM
Unknown Object (File)
Fri, Oct 3, 10:53 AM
Subscribers

Details

Summary

Currently, F_SETFL always invokes FIONBIO and FIOASYNC ioctls on the
file descriptor even if the state of the associated flag has not
changed. This means that a character device driver that implements
non-blocking I/O but not async I/O needs a handler for FIOASYNC that
permits setting the value to 0. This also means that fcntl(fd,
F_SETFL, fcntl(fd, F_GETFL)) can fail for a character device driver
that does not handle both FIONBIO and FIOASYNC. These requirements
are not obvious nor well documented.

Instead, only invoke FIONBIO and FIOASYNC if the relevant flag changes
state. This only requires a device driver to implement support for
FIONBIO or FIOASYNC if it supports the corresponding flag.

While here, if a request aims to toggle both F_NOBLOCK and F_ASYNC and
FIOASYNC fails, pass the previous state of F_NONBLOCK to FIONBIO
instead of always disabling non-blocking I/O and then possibly
reverting the flag back to on in f_flags.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

jhb requested review of this revision.Sep 5 2025, 7:50 PM

The bug of always reverting back to disabling FIONBIO dates back to the very first version of fcntl() 42 years ago: https://svnweb.freebsd.org/csrg/sys/kern/kern_descrip.c?r1=12748&r2=12747&pathrev=12748

While @sam merged the commit, the log claims it is "bill's code", so presumably a Bill Joy bug?

We need some file lock to prevent parallel threads from corrupting nonblock/async state vs flags.

This revision is now accepted and ready to land.Sep 5 2025, 10:50 PM

Interesting history of this bug.

We need some file lock to prevent parallel threads from corrupting nonblock/async state vs flags.

Worth an XXX comment in the code for now?

In D52403#1196680, @kib wrote:

We need some file lock to prevent parallel threads from corrupting nonblock/async state vs flags.

That is an old bug though? If it is just F_SETFL I would be happy to use a pool of sx locks or the like just for F_SETFL.

In D52403#1198039, @jhb wrote:
In D52403#1196680, @kib wrote:

We need some file lock to prevent parallel threads from corrupting nonblock/async state vs flags.

That is an old bug though? If it is just F_SETFL I would be happy to use a pool of sx locks or the like just for F_SETFL.

This is a separate bug and should get a separate fix.

Do we want to introduce sx pool? I think a pair of bit in f_vnread_flag similar to FOFFSET_LOCKED/WANT_LOCK and the existing mtx pool would be enough.

In D52403#1198184, @kib wrote:
In D52403#1198039, @jhb wrote:
In D52403#1196680, @kib wrote:

We need some file lock to prevent parallel threads from corrupting nonblock/async state vs flags.

That is an old bug though? If it is just F_SETFL I would be happy to use a pool of sx locks or the like just for F_SETFL.

This is a separate bug and should get a separate fix.

Do we want to introduce sx pool? I think a pair of bit in f_vnread_flag similar to FOFFSET_LOCKED/WANT_LOCK and the existing mtx pool would be enough.

Well, an sx lock would be less overhead (2 atomics to lock/unlock vs 4 to lock/unlock the mutex twice in the uncontested case), and home-grown locks aren't covered by WITNESS.