On 9/22/25 17:05, Konstantin Belousov wrote:
On Mon, Sep 22, 2025 at 09:50:09AM +0100, John Baldwin wrote:
On 9/22/25 04:41, John Baldwin wrote:
On 9/19/25 10:19, Konstantin Belousov wrote:
The branch main has been updated by kib:
URL:
https://cgit.FreeBSD.org/src/commit/?id=40a42785dbba93cc5196178fc49d340c1a89cabe
commit 40a42785dbba93cc5196178fc49d340c1a89cabe
Author: Konstantin Belousov <[email protected]>
AuthorDate: 2025-09-11 10:05:04 +0000
Commit: Konstantin Belousov <[email protected]>
CommitDate: 2025-09-19 14:19:13 +0000
fcntl(F_SETFL): only allow one thread to perform F_SETFL
Use f_vflags file locking for this.
Allowing more than one thread handling F_SETFL might cause de-sync
between real driver state and flags.
Reviewed by: markj
Tested by: pho
Sponsored by: The FreeBSD Foundation
MFC after: 2 weeks
Differential revision: https://reviews.freebsd.org/D52487
Thanks for fixing this. I still slightly worry that "home-grown" locks
aren't visible to WITNESS and it's checking.
I was also expecting this to require more changes, but apparently if a
process directly invokes FIONBIO on a file descriptor, f_flags isn't
updated currently. I wonder if that is a bug. (Similarly for FIOASYNC.)
Oh, we do handle that, but poorly. We don't revert on errors, and this
should be updated to use fsetfl_lock now I think:
kern_ioctl(...)
{
...
switch (com) {
...
case FIONBIO:
if ((tmp = *(int *)data))
atomic_set_int(&fp->f_flag, FNONBLOCK);
else
atomic_clear_int(&fp->f_flag, FNONBLOCK);
data = (void *)&tmp;
break;
case FIOASYNC:
if ((tmp = *(int *)data))
atomic_set_int(&fp->f_flag, FASYNC);
else
atomic_clear_int(&fp->f_flag, FASYNC);
data = (void *)&tmp;
break;
}
error = fo_ioctl(fp, com, data, td->td_ucred, td);
out:
I think instead we want something like:
int f_flag;
switch (com) {
...
case FIONBIO:
case FIOASYNC:
fsetfl_lock(fp);
tmp = *(int *)data;
f_flag = com == FIONBIO ? FNONBLOCK : FASYNC;
if ((fp->f_flag & f_flag) != 0) {
This is wrong, should be:
if (((fp->f_flag & f_flag) != 0) == (tmp != 0))
fsetfl_unlock(fp);
goto out;
}
data = (void *)&tmp;
break;
}
error = fo_ioctl(fp, com, data, td->td_ucred, td);
switch (com) {
...
case FIONBIO:
case FIOASYNC:
if (error == 0) {
if (tmp)
Probably 'if (tmp != 0)'
atomic_set_int(&fp->f_flag, f_flag);
else
atomic_clear_int(&fp->f_flag, f_flag);
}
fsetfl_unlock(fp);
break;
}
out:
This only updates the flag if the underlying ioctl succeeds, and it also
avoids invoking the underlying ioctl if the flag is already in the correct\
state.
So will you handle this?
I can. Do you think this is a good idea? :)
--
John Baldwin