NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/57259: ucom serial ports cannot be re-opened "too quickly" with O_NONBLOCK



>Number:         57259
>Category:       kern
>Synopsis:       ucom serial ports cannot be re-opened "too quickly" with O_NONBLOCK
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Mar 04 21:00:00 +0000 2023
>Originator:     Jason Thorpe
>Release:        NetBSD-10.0_BETA
>Organization:
RISCy Business
>Environment:
NetBSD neo2-nas 10.0_BETA NetBSD 10.0_BETA (GENERIC64) #0: Sun Feb 12 12:39:37 UTC 2023  mkrepro%mkrepro.NetBSD.org@localhost:/usr/src/sys/arch/evbarm/compile/GENERIC64 evbarm
>Description:
USB serial ports ("ucom" backed by uftdi in my case, but that may not matter) cannot be re-opened "too quickly" with O_NONBLOCK.

The symptom I observe is a process that has the serial port open exits, and immediately restarts and opens the device with O_NONBLOCK (because it's a portable program that does not want to deal with modem control nonsense, and opening with O_NONBLOCK is the portable way to do this).  This program occasionally gets EAGAIN on the open() call.  If there is a delay between exiting and restarting, the problem does not occur.

This is apparently not related to the HUPCL handling change made in several serial drivers in 2021 and 2022, as that code path does not consider O_NONBLOCK -- it waits for the 1 second hang-up window regardless of O_NONBLOCK (which seems perfectly fine -- the pause is not indefinite, and you wouldn't get EAGAIN if the driver had to block to acquire a mutex, for example).

My hypothesis is that it's related to the "closing" logic that is unique to the "ucom" driver among serial port drivers.  It uses the d_cancel entry point to split close into two phases, which is something that no other serial port drivers do.  Furthermore, this cancel/close logic specifically manipulates state that can cause EAGAIN to be returned to an open() call:

        if (state == UCOM_OPENING || sc->sc_closing) {
                if (flag & FNONBLOCK)
                        error = EWOULDBLOCK;
                else
                        error = cv_wait_sig(&sc->sc_statecv, &sc->sc_lock);
                mutex_exit(&sc->sc_lock);
                return error ? error : ERESTART;
        }

Looking at the "com" driver, for example, there appears to be nothing that would return EAGAIN (or the alternate spelling EWOULDBLOCK) in the open path.
>How-To-Repeat:
close, then re-open a "ucom" serial port with O_NONBLOCK very quickly.
>Fix:
Yes please.



Home | Main Index | Thread Index | Old Index