Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 18 Dec 2025 18:23:10 +0000
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: b3aada5bfdb9 - stable/14 - fcntl(F_SETFL): Don't unconditionally invoke FIONBIO and FIOASYNC
Message-ID:  <6944468e.2674f.2f543ec6@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch stable/14 has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=b3aada5bfdb9da22fdd33a67ca508074da6d1b5b

commit b3aada5bfdb9da22fdd33a67ca508074da6d1b5b
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2025-09-10 14:22:19 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2025-12-18 17:01:56 +0000

    fcntl(F_SETFL): Don't unconditionally invoke FIONBIO and FIOASYNC
    
    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.
    
    Reviewed by:    mckusick, imp, kib, emaste
    Differential Revision:  https://reviews.freebsd.org/D52403
    
    (cherry picked from commit 3c152a3de42a7d077e8d19159b679c3fb7572820)
---
 sys/kern/kern_descrip.c | 32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)

diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 0e6af0f5deff..e8bbc5d9ee1c 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -578,20 +578,26 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg)
 		} while (atomic_cmpset_int(&fp->f_flag, flg, tmp) == 0);
 		got_set = tmp & ~flg;
 		got_cleared = flg & ~tmp;
-		tmp = fp->f_flag & FNONBLOCK;
-		error = fo_ioctl(fp, FIONBIO, &tmp, td->td_ucred, td);
-		if (error != 0)
-			goto revert_f_setfl;
-		tmp = fp->f_flag & FASYNC;
-		error = fo_ioctl(fp, FIOASYNC, &tmp, td->td_ucred, td);
-		if (error == 0) {
-			fdrop(fp, td);
-			break;
+		if (((got_set | got_cleared) & FNONBLOCK) != 0) {
+			tmp = fp->f_flag & FNONBLOCK;
+			error = fo_ioctl(fp, FIONBIO, &tmp, td->td_ucred, td);
+			if (error != 0)
+				goto revert_flags;
+		}
+		if (((got_set | got_cleared) & FASYNC) != 0) {
+			tmp = fp->f_flag & FASYNC;
+			error = fo_ioctl(fp, FIOASYNC, &tmp, td->td_ucred, td);
+			if (error != 0)
+				goto revert_nonblock;
+		}
+		fdrop(fp, td);
+		break;
+revert_nonblock:
+		if (((got_set | got_cleared) & FNONBLOCK) != 0) {
+			tmp = ~fp->f_flag & FNONBLOCK;
+			(void)fo_ioctl(fp, FIONBIO, &tmp, td->td_ucred, td);
 		}
-		atomic_clear_int(&fp->f_flag, FNONBLOCK);
-		tmp = 0;
-		(void)fo_ioctl(fp, FIONBIO, &tmp, td->td_ucred, td);
-revert_f_setfl:
+revert_flags:
 		do {
 			tmp = flg = fp->f_flag;
 			tmp &= ~FCNTLFLAGS;


help

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6944468e.2674f.2f543ec6>