# HG changeset patch # User Taylor R Campbell # Date 1790107552 0 # Tue Sep 22 20:05:52 2026 +0000 # Branch trunk # Node ID b29a9798f1ecfba262c69dfe61a37052024592a1 # Parent 36a6badd1330b34f4bc9f5383c97a5ee8c7e2393 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Rename pipe variables to make more sense. - wpipe for the writer side of a pipe, associated with a file open for FWRITE. - rpipe for the writer side of a pipe, associated with a file open for FREAD. - pipe for either side of the pipe. - ppipe for the peer corresponding to pipe. No functional change intended. Prompted by trying to wrap my head around this pipe(2) code in order to address: PR kern/59056: poll POLLHUP bugs diff -r 36a6badd1330 -r b29a9798f1ec sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 16:15:58 2026 +0000 +++ b/sys/kern/sys_pipe.c Tue Sep 22 20:05:52 2026 +0000 @@ -571,34 +571,34 @@ pipe_write(file_t *fp, off_t *offset, st unsigned int wakeup_state = 0; /* We want to write to our peer */ - rpipe = fp->f_pipe; - lock = rpipe->pipe_lock; + wpipe = fp->f_pipe; + lock = wpipe->pipe_lock; error = 0; mutex_enter(lock); - wpipe = rpipe->pipe_peer; + rpipe = wpipe->pipe_peer; /* * Detect loss of pipe read side, issue SIGPIPE if lost. */ - if (wpipe == NULL || (wpipe->pipe_state & PIPE_EOF) != 0) { + if (rpipe == NULL || (rpipe->pipe_state & PIPE_EOF) != 0) { mutex_exit(lock); return EPIPE; } - ++wpipe->pipe_busy; + ++rpipe->pipe_busy; /* Acquire the long-term pipe lock */ - if ((error = pipelock(wpipe, true)) != 0) { - --wpipe->pipe_busy; - if (wpipe->pipe_busy == 0) { - wpipe->pipe_state &= ~PIPE_RESTART; - cv_broadcast(&wpipe->pipe_draincv); + if ((error = pipelock(rpipe, true)) != 0) { + --rpipe->pipe_busy; + if (rpipe->pipe_busy == 0) { + rpipe->pipe_state &= ~PIPE_RESTART; + cv_broadcast(&rpipe->pipe_draincv); } mutex_exit(lock); return (error); } - bp = &wpipe->pipe_buffer; + bp = &rpipe->pipe_buffer; /* * If it is advantageous to resize the pipe buffer, do so. @@ -607,7 +607,7 @@ pipe_write(file_t *fp, off_t *offset, st (nbigpipe < maxbigpipes) && (bp->size <= PIPE_SIZE) && (bp->cnt == 0)) { - if (pipespace(wpipe, BIG_PIPE_SIZE) == 0) + if (pipespace(rpipe, BIG_PIPE_SIZE) == 0) atomic_inc_uint(&nbigpipe); } @@ -675,7 +675,7 @@ pipe_write(file_t *fp, off_t *offset, st /* * If the "read-side" has been blocked, wake it up now. */ - cv_broadcast(&wpipe->pipe_rcv); + cv_broadcast(&rpipe->pipe_rcv); /* * Don't block on non-blocking I/O. @@ -690,7 +690,7 @@ pipe_write(file_t *fp, off_t *offset, st * wake up select/poll. */ if (bp->cnt) - pipeselwakeup(wpipe, wpipe, POLL_IN); + pipeselwakeup(rpipe, rpipe, POLL_IN); if (wakeup_state & PIPE_RESTART) { error = ERESTART; @@ -701,27 +701,27 @@ pipe_write(file_t *fp, off_t *offset, st * If read side wants to go away, we just issue a signal * to ourselves. */ - if (wpipe->pipe_state & PIPE_EOF) { + if (rpipe->pipe_state & PIPE_EOF) { error = EPIPE; break; } - pipeunlock(wpipe); - error = cv_wait_sig(&wpipe->pipe_wcv, lock); - (void)pipelock(wpipe, false); + pipeunlock(rpipe); + error = cv_wait_sig(&rpipe->pipe_wcv, lock); + (void)pipelock(rpipe, false); if (error != 0) break; - wakeup_state = wpipe->pipe_state; + wakeup_state = rpipe->pipe_state; } } - --wpipe->pipe_busy; - if (wpipe->pipe_busy == 0) { - wpipe->pipe_state &= ~PIPE_RESTART; - cv_broadcast(&wpipe->pipe_draincv); + --rpipe->pipe_busy; + if (rpipe->pipe_busy == 0) { + rpipe->pipe_state &= ~PIPE_RESTART; + cv_broadcast(&rpipe->pipe_draincv); } if (bp->cnt > 0) { - cv_broadcast(&wpipe->pipe_rcv); + cv_broadcast(&rpipe->pipe_rcv); } /* @@ -731,20 +731,20 @@ pipe_write(file_t *fp, off_t *offset, st error = 0; if (error == 0) - getnanotime(&wpipe->pipe_mtime); + getnanotime(&rpipe->pipe_mtime); /* * We have something to offer, wake up select/poll. */ if (bp->cnt) - pipeselwakeup(wpipe, wpipe, POLL_IN); + pipeselwakeup(rpipe, rpipe, POLL_IN); /* * Arrange for next read(2) to do a signal. */ - wpipe->pipe_state |= PIPE_SIGNALR; + rpipe->pipe_state |= PIPE_SIGNALR; - pipeunlock(wpipe); + pipeunlock(rpipe); mutex_exit(lock); return (error); } @@ -817,43 +817,43 @@ pipe_ioctl(file_t *fp, u_long cmd, void int pipe_poll(file_t *fp, int events) { - struct pipe *rpipe = fp->f_pipe; - struct pipe *wpipe; + struct pipe *pipe = fp->f_pipe; + struct pipe *ppipe; int eof = 0; int revents = 0; - mutex_enter(rpipe->pipe_lock); - wpipe = rpipe->pipe_peer; + mutex_enter(pipe->pipe_lock); + ppipe = pipe->pipe_peer; if (events & (POLLIN | POLLRDNORM)) - if ((rpipe->pipe_buffer.cnt > 0) || - (rpipe->pipe_state & PIPE_EOF)) + if ((pipe->pipe_buffer.cnt > 0) || + (pipe->pipe_state & PIPE_EOF)) revents |= events & (POLLIN | POLLRDNORM); - eof |= (rpipe->pipe_state & PIPE_EOF); + eof |= (pipe->pipe_state & PIPE_EOF); - if (wpipe == NULL) + if (ppipe == NULL) revents |= events & (POLLOUT | POLLWRNORM); else { if (events & (POLLOUT | POLLWRNORM)) - if ((wpipe->pipe_state & PIPE_EOF) || ( - (wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt) >= PIPE_BUF)) + if ((ppipe->pipe_state & PIPE_EOF) || ( + (ppipe->pipe_buffer.size - ppipe->pipe_buffer.cnt) >= PIPE_BUF)) revents |= events & (POLLOUT | POLLWRNORM); - eof |= (wpipe->pipe_state & PIPE_EOF); + eof |= (ppipe->pipe_state & PIPE_EOF); } - if (wpipe == NULL || eof) + if (ppipe == NULL || eof) revents |= POLLHUP; if (revents == 0) { if (events & (POLLIN | POLLRDNORM)) - selrecord(curlwp, &rpipe->pipe_sel); + selrecord(curlwp, &pipe->pipe_sel); if (events & (POLLOUT | POLLWRNORM)) - selrecord(curlwp, &wpipe->pipe_sel); + selrecord(curlwp, &ppipe->pipe_sel); } - mutex_exit(rpipe->pipe_lock); + mutex_exit(pipe->pipe_lock); return (revents); } @@ -1065,18 +1065,18 @@ filt_pipedetach(struct knote *kn) static int filt_piperead(struct knote *kn, long hint) { - struct pipe *rpipe = ((file_t *)kn->kn_obj)->f_pipe; - struct pipe *wpipe; + struct pipe *pipe = ((file_t *)kn->kn_obj)->f_pipe; + struct pipe *ppipe; int rv; if ((hint & NOTE_SUBMIT) == 0) { - mutex_enter(rpipe->pipe_lock); + mutex_enter(pipe->pipe_lock); } - wpipe = rpipe->pipe_peer; - kn->kn_data = rpipe->pipe_buffer.cnt; + ppipe = pipe->pipe_peer; + kn->kn_data = pipe->pipe_buffer.cnt; - if ((rpipe->pipe_state & PIPE_EOF) || - (wpipe == NULL) || (wpipe->pipe_state & PIPE_EOF)) { + if ((pipe->pipe_state & PIPE_EOF) || + (ppipe == NULL) || (ppipe->pipe_state & PIPE_EOF)) { knote_set_eof(kn, 0); rv = 1; } else { @@ -1084,7 +1084,7 @@ filt_piperead(struct knote *kn, long hin } if ((hint & NOTE_SUBMIT) == 0) { - mutex_exit(rpipe->pipe_lock); + mutex_exit(pipe->pipe_lock); } return rv; } @@ -1092,26 +1092,26 @@ filt_piperead(struct knote *kn, long hin static int filt_pipewrite(struct knote *kn, long hint) { - struct pipe *rpipe = ((file_t *)kn->kn_obj)->f_pipe; - struct pipe *wpipe; + struct pipe *pipe = ((file_t *)kn->kn_obj)->f_pipe; + struct pipe *ppipe; int rv; if ((hint & NOTE_SUBMIT) == 0) { - mutex_enter(rpipe->pipe_lock); + mutex_enter(pipe->pipe_lock); } - wpipe = rpipe->pipe_peer; + ppipe = pipe->pipe_peer; - if ((wpipe == NULL) || (wpipe->pipe_state & PIPE_EOF)) { + if ((ppipe == NULL) || (ppipe->pipe_state & PIPE_EOF)) { kn->kn_data = 0; knote_set_eof(kn, 0); rv = 1; } else { - kn->kn_data = wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt; + kn->kn_data = ppipe->pipe_buffer.size - ppipe->pipe_buffer.cnt; rv = kn->kn_data >= PIPE_BUF; } if ((hint & NOTE_SUBMIT) == 0) { - mutex_exit(rpipe->pipe_lock); + mutex_exit(pipe->pipe_lock); } return rv; } # HG changeset patch # User Taylor R Campbell # Date 1790118143 0 # Tue Sep 22 23:02:23 2026 +0000 # Branch trunk # Node ID 1968c1a2304d0d7697bb77a384d6d10b01b5a5ef # Parent b29a9798f1ecfba262c69dfe61a37052024592a1 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Split new function pipefree out of pipeclose. Makes it easier to reason about pipeclose this way. Prompted by: PR kern/59056: poll POLLHUP bugs diff -r b29a9798f1ec -r 1968c1a2304d sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Tue Sep 22 20:05:52 2026 +0000 +++ b/sys/kern/sys_pipe.c Tue Sep 22 23:02:23 2026 +0000 @@ -127,6 +127,7 @@ static u_int nbigpipe = 0; static u_int amountpipekva = 0; static void pipeclose(struct pipe *); +static void pipefree(struct pipe *); static void pipe_free_kmem(struct pipe *); static int pipe_create(struct pipe **, pool_cache_t, struct timespec *); static int pipelock(struct pipe *, bool); @@ -258,8 +259,10 @@ pipe1(struct lwp *l, int *fildes, int fl free3: fd_abort(p, rf, fildes[0]); free2: - pipeclose(wpipe); - pipeclose(rpipe); + if (wpipe) + pipefree(wpipe); + if (rpipe) + pipefree(rpipe); return (error); } @@ -962,18 +965,13 @@ pipeclose(struct pipe *pipe) kmutex_t *lock; struct pipe *ppipe; - if (pipe == NULL) - return; - KASSERT(cv_is_valid(&pipe->pipe_rcv)); KASSERT(cv_is_valid(&pipe->pipe_wcv)); KASSERT(cv_is_valid(&pipe->pipe_draincv)); KASSERT(cv_is_valid(&pipe->pipe_lkcv)); lock = pipe->pipe_lock; - if (lock == NULL) - /* Must have failed during create */ - goto free_resources; + KASSERT(lock != NULL); mutex_enter(lock); pipeselwakeup(pipe, pipe, POLL_HUP); @@ -1016,7 +1014,13 @@ pipeclose(struct pipe *pipe) /* * Free resources. */ - free_resources: + pipefree(pipe); +} + +static void +pipefree(struct pipe *pipe) +{ + pipe->pipe_pgid = 0; pipe->pipe_state = PIPE_SIGNALR; pipe->pipe_peer = NULL; # HG changeset patch # User Taylor R Campbell # Date 1790123630 0 # Wed Sep 23 00:33:50 2026 +0000 # Branch trunk # Node ID 51ab4e75ffe2fd74a4389a052d2237ef4aba2a77 # Parent 1968c1a2304d0d7697bb77a384d6d10b01b5a5ef # EXP-Topic riastradh-pr59056-pollhup pipe(2): Fix wakeup of pending writers on close of write side. The job of pipe_restart is to wake any pending I/O operations on the file when it is about to be closed. New references cannot be taken for new I/O operations; once all existing references are drained, the system calls pipe_close. What pipe_restart did was to wake pipe->pipe_rcv and pipe->pipe_wcv. But the condvars of _which pipe_? After renaming the variables to match reality, it becomes clear that wpipe->pipe_wcv and wpipe->pipe_rcv are never used -- instead, pipe_read waits for rpipe->pipe_rcv, and pipe_write waits for rpipe->pipe_wcv. So pipe_restart on the write side of a pipe woke wpipe->pipe_rcv and wpipe->pipe_wcv, which nothing was waiting for, and failed to wake rpipe->pipe_rcv or rpipe->pipe_wcv. (Perhaps we should just have a single struct pipe::pipe_cv member, and have pipe_read use rpipe->pipe_cv and pipe_write use wpipe->pipe_cv. But that will be left for future cleanup.) PR kern/57659: closing pipe writefd fails to wake concurrent write on same writefd diff -r 1968c1a2304d -r 51ab4e75ffe2 sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Tue Sep 22 23:02:23 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 00:33:50 2026 +0000 @@ -902,6 +902,7 @@ static void pipe_restart(file_t *fp) { struct pipe *pipe = fp->f_pipe; + struct pipe *rpipe; /* * Unblock blocked reads/writes in order to allow close() to complete. @@ -909,11 +910,12 @@ pipe_restart(file_t *fp) * (Partial writes return the transfer length.) */ mutex_enter(pipe->pipe_lock); - pipe->pipe_state |= PIPE_RESTART; - /* Wakeup both cvs, maybe we only need one, but maybe there are some - * other paths where wakeup is needed, and it saves deciding which! */ - cv_broadcast(&pipe->pipe_rcv); - cv_broadcast(&pipe->pipe_wcv); + rpipe = (fp->f_flag & FREAD) ? pipe : pipe->pipe_peer; + if (rpipe != NULL) { + rpipe->pipe_state |= PIPE_RESTART; + cv_broadcast(&rpipe->pipe_rcv); + cv_broadcast(&rpipe->pipe_wcv); + } mutex_exit(pipe->pipe_lock); } diff -r 1968c1a2304d -r 51ab4e75ffe2 tests/kernel/t_fdrestart.c --- a/tests/kernel/t_fdrestart.c Tue Sep 22 23:02:23 2026 +0000 +++ b/tests/kernel/t_fdrestart.c Wed Sep 23 00:33:50 2026 +0000 @@ -641,7 +641,6 @@ ATF_TC_BODY(pipe_write, tc) memset(F, 0, sizeof(*F)); F->op = &dowrite; F->fd = fd[1]; - atf_tc_expect_fail("PR kern/57659"); testfdrestart(F); } # HG changeset patch # User Taylor R Campbell # Date 1790127572 0 # Wed Sep 23 01:39:32 2026 +0000 # Branch trunk # Node ID a3c53e94bdb60cf6bc7fe36f39be37ed2ec7faf3 # Parent 51ab4e75ffe2fd74a4389a052d2237ef4aba2a77 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Make pipe sides a little more symmetric. 1. When reading from or writing to a pipe, busy _this side_ of the pipe, not the other side. 2. In pipeclose, all operations on _this side_ of the pipe have already quiesced. But operations on the other side may not have. So wait for the _other side_ to be unbusied before disconnecting the peer (changing ppipe->pipe_peer from pipe to NULL). With (1) and (2) we can prove a simple property that makes reasoning about this code easier: If a pipe is busy, its peer pointer is stable even across cv_wait on the pipe lock. Without these changes I'm not sure I could prove that property (though I haven't exhibited a reproducer for any issues arising from its failure). 3. Make write wait on wpipe->pipe_wcv, and make read wait on rpipe->pipe_rcv, consistently, so that any waiters on one side of a pipe will always be waiting on one of _that side's_ condvars. This makes the logic in pipe_restart to wake any pending operations on _this side_ simpler -- it doesn't have to reach over to the peer, and it doesn't have to inadvertently wake the peer for no reason. In fact this brings the text of the code in pipe_restart back to what it was before I renamed the variables to reflect their actual content, suggesting there has been some confusion from the variable naming over the years... (In a future change, I would like to merge pipe_rcv and pipe_wcv into a single condvar per side -- no need to initialize two extra condvars per pipe that will never be used!) Preparation for: PR kern/59056: poll POLLHUP bugs diff -r 51ab4e75ffe2 -r a3c53e94bdb6 sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 00:33:50 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 01:39:32 2026 +0000 @@ -411,6 +411,7 @@ pipe_read(file_t *fp, off_t *offset, str int flags) { struct pipe *rpipe = fp->f_pipe; + struct pipe *wpipe; struct pipebuf *bp = &rpipe->pipe_buffer; kmutex_t *lock = rpipe->pipe_lock; int error; @@ -490,6 +491,9 @@ again: /* * Detect EOF condition. * Read returns 0 on EOF, no need to set error. + * + * XXX Why rpipe->pipe_state and not wpipe->pipe_state? + * XXX Distinguish reader-closed from writer-closed? */ if (rpipe->pipe_state & PIPE_EOF) break; @@ -509,7 +513,6 @@ again: */ pipeunlock(rpipe); -#if 1 /* XXX (dsl) I'm sure these aren't needed here ... */ /* * We want to read more, wake up select/poll. */ @@ -518,8 +521,8 @@ again: /* * If the "write-side" is blocked, wake it up now. */ - cv_broadcast(&rpipe->pipe_wcv); -#endif + wpipe = rpipe->pipe_peer; + cv_broadcast(&wpipe->pipe_wcv); if (wakeup_state & PIPE_RESTART) { error = ERESTART; @@ -545,7 +548,8 @@ unlocked_error: cv_broadcast(&rpipe->pipe_draincv); } if (bp->cnt < MINPIPESIZE) { - cv_broadcast(&rpipe->pipe_wcv); + if ((wpipe = rpipe->pipe_peer) != NULL) + cv_broadcast(&wpipe->pipe_wcv); } /* @@ -588,14 +592,14 @@ pipe_write(file_t *fp, off_t *offset, st mutex_exit(lock); return EPIPE; } - ++rpipe->pipe_busy; + ++wpipe->pipe_busy; /* Acquire the long-term pipe lock */ if ((error = pipelock(rpipe, true)) != 0) { - --rpipe->pipe_busy; - if (rpipe->pipe_busy == 0) { - rpipe->pipe_state &= ~PIPE_RESTART; - cv_broadcast(&rpipe->pipe_draincv); + --wpipe->pipe_busy; + if (wpipe->pipe_busy == 0) { + wpipe->pipe_state &= ~PIPE_RESTART; + cv_broadcast(&wpipe->pipe_draincv); } mutex_exit(lock); return (error); @@ -703,6 +707,11 @@ pipe_write(file_t *fp, off_t *offset, st /* * If read side wants to go away, we just issue a signal * to ourselves. + * + * XXX Shouldn't this happen before we uiomove anything? + * + * XXX Why rpipe->pipe_state and not wpipe->pipe_state? + * XXX Distinguish reader-closed from writer-closed? */ if (rpipe->pipe_state & PIPE_EOF) { error = EPIPE; @@ -710,18 +719,18 @@ pipe_write(file_t *fp, off_t *offset, st } pipeunlock(rpipe); - error = cv_wait_sig(&rpipe->pipe_wcv, lock); + error = cv_wait_sig(&wpipe->pipe_wcv, lock); (void)pipelock(rpipe, false); if (error != 0) break; - wakeup_state = rpipe->pipe_state; + wakeup_state = wpipe->pipe_state; } } - --rpipe->pipe_busy; - if (rpipe->pipe_busy == 0) { - rpipe->pipe_state &= ~PIPE_RESTART; - cv_broadcast(&rpipe->pipe_draincv); + --wpipe->pipe_busy; + if (wpipe->pipe_busy == 0) { + wpipe->pipe_state &= ~PIPE_RESTART; + cv_broadcast(&wpipe->pipe_draincv); } if (bp->cnt > 0) { cv_broadcast(&rpipe->pipe_rcv); @@ -729,6 +738,11 @@ pipe_write(file_t *fp, off_t *offset, st /* * Don't return EPIPE if I/O was successful + * + * XXX Shouldn't we avoid returning _any_ error if we + * transmitted _any_ positive number of bytes? Or does that + * happen downstream of here, and if so, why do we need to do + * that here? */ if (error == EPIPE && bp->cnt == 0 && uio->uio_resid == 0) error = 0; @@ -902,7 +916,6 @@ static void pipe_restart(file_t *fp) { struct pipe *pipe = fp->f_pipe; - struct pipe *rpipe; /* * Unblock blocked reads/writes in order to allow close() to complete. @@ -910,12 +923,14 @@ pipe_restart(file_t *fp) * (Partial writes return the transfer length.) */ mutex_enter(pipe->pipe_lock); - rpipe = (fp->f_flag & FREAD) ? pipe : pipe->pipe_peer; - if (rpipe != NULL) { - rpipe->pipe_state |= PIPE_RESTART; - cv_broadcast(&rpipe->pipe_rcv); - cv_broadcast(&rpipe->pipe_wcv); - } + pipe->pipe_state |= PIPE_RESTART; + /* + * At most one of these is in use at any time, depending on + * whether fp->f_flag has FREAD or FWRITE set, but there's no + * harm in waking both here. + */ + cv_broadcast(&pipe->pipe_rcv); + cv_broadcast(&pipe->pipe_wcv); mutex_exit(pipe->pipe_lock); } @@ -979,24 +994,32 @@ pipeclose(struct pipe *pipe) pipeselwakeup(pipe, pipe, POLL_HUP); /* - * If the other side is blocked, wake it up saying that - * we want to close it down. + * fd_close has issued .fo_restart to wake all waiters on this + * side of the pipe, blocked new references, and waited for all + * references to drain, so it should not be possible for there + * to be any waiters remaining. (Only one of the condvars was + * ever in use anyway depending on whether this is the reader + * side or the writer side of the pipe.) + */ + KASSERT(!cv_has_waiters(&pipe->pipe_rcv)); + KASSERT(!cv_has_waiters(&pipe->pipe_wcv)); + + /* + * If the other side is busy, wake it up saying that + * we want to close it down, which will prevent peers + * from starting new I/O. Once it is no longer busy, + * disconnect it. */ pipe->pipe_state |= PIPE_EOF; - if (pipe->pipe_busy) { - while (pipe->pipe_busy) { - cv_broadcast(&pipe->pipe_wcv); - cv_wait_sig(&pipe->pipe_draincv, lock); - } - } - - /* - * Disconnect from peer. - */ if ((ppipe = pipe->pipe_peer) != NULL) { pipeselwakeup(ppipe, ppipe, POLL_HUP); ppipe->pipe_state |= PIPE_EOF; - cv_broadcast(&ppipe->pipe_rcv); + if (ppipe->pipe_busy) { + cv_broadcast(&ppipe->pipe_rcv); + cv_broadcast(&ppipe->pipe_wcv); + while (ppipe->pipe_busy) + cv_wait_sig(&ppipe->pipe_draincv, lock); + } ppipe->pipe_peer = NULL; } # HG changeset patch # User Taylor R Campbell # Date 1790127869 0 # Wed Sep 23 01:44:29 2026 +0000 # Branch trunk # Node ID 2682df6741d057ae44cd21ab44b7e842c8435475 # Parent a3c53e94bdb60cf6bc7fe36f39be37ed2ec7faf3 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Simplify PIPE_RESTART handling. Now that it applies to each side independently, we can just make it final, because new I/O operations are not allowed on an file that has had its .fo_restart called. Makes reasoning about all this easier. XXX Consider renaming PIPE_RESTART to PIPE_CLOSING: the .fo_restart operation means the file is irreversibly destined to be closed soon and just needs any pending I/O on it to be interrupted and fail promptly so we can finally close the file. Preparation for: PR kern/59056: poll POLLHUP bugs diff -r a3c53e94bdb6 -r 2682df6741d0 sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 01:39:32 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 01:44:29 2026 +0000 @@ -544,7 +544,6 @@ again: unlocked_error: --rpipe->pipe_busy; if (rpipe->pipe_busy == 0) { - rpipe->pipe_state &= ~PIPE_RESTART; cv_broadcast(&rpipe->pipe_draincv); } if (bp->cnt < MINPIPESIZE) { @@ -598,7 +597,6 @@ pipe_write(file_t *fp, off_t *offset, st if ((error = pipelock(rpipe, true)) != 0) { --wpipe->pipe_busy; if (wpipe->pipe_busy == 0) { - wpipe->pipe_state &= ~PIPE_RESTART; cv_broadcast(&wpipe->pipe_draincv); } mutex_exit(lock); @@ -729,7 +727,6 @@ pipe_write(file_t *fp, off_t *offset, st --wpipe->pipe_busy; if (wpipe->pipe_busy == 0) { - wpipe->pipe_state &= ~PIPE_RESTART; cv_broadcast(&wpipe->pipe_draincv); } if (bp->cnt > 0) { # HG changeset patch # User Taylor R Campbell # Date 1790177677 0 # Wed Sep 23 15:34:37 2026 +0000 # Branch trunk # Node ID 171bf10ec3acf28812f91dc4c4713f8aded7f973 # Parent 2682df6741d057ae44cd21ab44b7e842c8435475 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Restructure select/poll/kqueue records. 1. When a thread is waiting on an endpoint of a pipe, have it wait on _that endpoint_ (i.e., cv_wait or selrecord), not on the other endpoint sometimes depending on the direction of the I/O. 2. Make poll(2) on the writer side of a pipe wake with POLLERR when the reader side is closed, because write would return immediately, and fail with EPIPE/SIGPIPE. See also: https://mail-index.NetBSD.org/tech-kern/2026/09/21/msg031255.html (In contrast, for the other way around, when the writer side of a pipe is closed, poll(2) on the reader is already defined to wake with POLLHUP, and while read would return immediately, it will not _fail_; it will simply report EOF, so there is no justification for POLLERR in that direction.) 3. For EVFILT_READ, require the reader side of a pipe; likewise, for EVFILT_WRITE, require the writer side of a pipe. That this wasn't enforced before was an accident, as far as I can tell, and made the kevent paths very confusing about which side of the pipe is which. PR kern/59056: poll POLLHUP bugs diff -r 2682df6741d0 -r 171bf10ec3ac sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 01:44:29 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 15:34:37 2026 +0000 @@ -126,13 +126,13 @@ static u_int nbigpipe = 0; */ static u_int amountpipekva = 0; -static void pipeclose(struct pipe *); +static void pipeclose(struct file *, struct pipe *); static void pipefree(struct pipe *); static void pipe_free_kmem(struct pipe *); static int pipe_create(struct pipe **, pool_cache_t, struct timespec *); static int pipelock(struct pipe *, bool); static inline void pipeunlock(struct pipe *); -static void pipeselwakeup(struct pipe *, struct pipe *, int); +static void pipeselwakeup(struct pipe *, int); static int pipespace(struct pipe *, int); static int pipe_ctor(void *, void *, int); static void pipe_dtor(void *, void *); @@ -369,14 +369,28 @@ pipeunlock(struct pipe *pipe) } /* - * Select/poll wakeup. This also sends SIGIO to peer connected to - * 'sigpipe' side of pipe. + * pipeselwakeup(pipe, code) + * + * Activity has happened on pipe's peer oncausing I/O to be + * available on pipe, so: + * + * 1. Wake any threads waiting in select/poll on pipe. + * + * 2. Deliver SIGIO to any process (group) configured to receive + * notifications about I/O on pipe. + * + * `code' is a siginfo_t si_code value in the POLL_* namespace for + * the type of notification the waiters will receive, and it + * should match the direction of the pipe -- POLL_OUT/POLL_ERR + * with the writer side, POLL_IN/POLL_HUP with the reader side. */ static void -pipeselwakeup(struct pipe *selp, struct pipe *sigp, int code) +pipeselwakeup(struct pipe *pipe, int code) { int band; + KASSERT(mutex_owned(pipe->pipe_lock)); + switch (code) { case POLL_IN: band = POLLIN|POLLRDNORM; @@ -398,12 +412,12 @@ pipeselwakeup(struct pipe *selp, struct break; } - selnotify(&selp->pipe_sel, band, NOTE_SUBMIT); + selnotify(&pipe->pipe_sel, band, NOTE_SUBMIT); - if (sigp == NULL || (sigp->pipe_state & PIPE_ASYNC) == 0) + if ((pipe->pipe_state & PIPE_ASYNC) == 0) return; - fownsignal(sigp->pipe_pgid, SIGIO, code, band, selp); + fownsignal(pipe->pipe_pgid, SIGIO, code, band, pipe); } static int @@ -514,14 +528,10 @@ again: pipeunlock(rpipe); /* - * We want to read more, wake up select/poll. - */ - pipeselwakeup(rpipe, rpipe->pipe_peer, POLL_OUT); - - /* * If the "write-side" is blocked, wake it up now. */ wpipe = rpipe->pipe_peer; + pipeselwakeup(wpipe, POLL_OUT); cv_broadcast(&wpipe->pipe_wcv); if (wakeup_state & PIPE_RESTART) { @@ -558,7 +568,8 @@ unlocked_error: */ if ((bp->size - bp->cnt) >= PIPE_BUF && (ocnt != bp->cnt || (rpipe->pipe_state & PIPE_SIGNALR))) { - pipeselwakeup(rpipe, rpipe->pipe_peer, POLL_OUT); + if ((wpipe = rpipe->pipe_peer) != NULL) + pipeselwakeup(wpipe, POLL_OUT); rpipe->pipe_state &= ~PIPE_SIGNALR; } @@ -695,7 +706,7 @@ pipe_write(file_t *fp, off_t *offset, st * wake up select/poll. */ if (bp->cnt) - pipeselwakeup(rpipe, rpipe, POLL_IN); + pipeselwakeup(rpipe, POLL_IN); if (wakeup_state & PIPE_RESTART) { error = ERESTART; @@ -751,7 +762,7 @@ pipe_write(file_t *fp, off_t *offset, st * We have something to offer, wake up select/poll. */ if (bp->cnt) - pipeselwakeup(rpipe, rpipe, POLL_IN); + pipeselwakeup(rpipe, POLL_IN); /* * Arrange for next read(2) to do a signal. @@ -833,40 +844,57 @@ pipe_poll(file_t *fp, int events) { struct pipe *pipe = fp->f_pipe; struct pipe *ppipe; - int eof = 0; int revents = 0; mutex_enter(pipe->pipe_lock); ppipe = pipe->pipe_peer; - if (events & (POLLIN | POLLRDNORM)) - if ((pipe->pipe_buffer.cnt > 0) || - (pipe->pipe_state & PIPE_EOF)) - revents |= events & (POLLIN | POLLRDNORM); - - eof |= (pipe->pipe_state & PIPE_EOF); + if (fp->f_flag & FREAD) { + struct pipe *rpipe = pipe; - if (ppipe == NULL) - revents |= events & (POLLOUT | POLLWRNORM); - else { - if (events & (POLLOUT | POLLWRNORM)) - if ((ppipe->pipe_state & PIPE_EOF) || ( - (ppipe->pipe_buffer.size - ppipe->pipe_buffer.cnt) >= PIPE_BUF)) - revents |= events & (POLLOUT | POLLWRNORM); + /* + * If the writer has been closed, then we can always + * read (possibly returning EOF) without blocking, so + * set POLLIN|POLLRDNORM if requested, and set POLLHUP + * unsolicited to notify reader of the fact. + * + * Otherwise, we can only read without blocking if + * there are bytes in the buffer. + */ + if (rpipe->pipe_state & PIPE_EOF) { + revents |= events & (POLLIN | POLLRDNORM); + revents |= POLLHUP; + } else if (rpipe->pipe_buffer.cnt > 0) { + revents |= events & (POLLIN | POLLRDNORM); + } + } else if (fp->f_flag & FWRITE) { + struct pipe *wpipe = pipe; + struct pipe *rpipe = ppipe; - eof |= (ppipe->pipe_state & PIPE_EOF); + /* + * If the reader has been closed, then any writes will + * immediately fail with EPIPE, so report + * POLLOUT|POLLWRNORM if requested and POLLERR + * unsolicited. + * + * Otherwise, we can only write without blocking if + * there are at least PIPE_BUF bytes free in the + * buffer. + */ + if (rpipe == NULL || (wpipe->pipe_state & PIPE_EOF) != 0) { + revents |= events & (POLLOUT | POLLWRNORM); + revents |= POLLERR; + } else if (rpipe->pipe_buffer.size - rpipe->pipe_buffer.cnt >= + PIPE_BUF) { + revents |= events & (POLLOUT | POLLWRNORM); + } + } else { + panic("file %p pipe %p invalid direction flag 0x%x", + fp, pipe, fp->f_flag); } - if (ppipe == NULL || eof) - revents |= POLLHUP; - - if (revents == 0) { - if (events & (POLLIN | POLLRDNORM)) - selrecord(curlwp, &pipe->pipe_sel); - - if (events & (POLLOUT | POLLWRNORM)) - selrecord(curlwp, &ppipe->pipe_sel); - } + if (revents == 0) + selrecord(curlwp, &pipe->pipe_sel); mutex_exit(pipe->pipe_lock); return (revents); @@ -905,7 +933,7 @@ pipe_close(file_t *fp) struct pipe *pipe = fp->f_pipe; fp->f_pipe = NULL; - pipeclose(pipe); + pipeclose(fp, pipe); return (0); } @@ -974,7 +1002,7 @@ pipe_free_kmem(struct pipe *pipe) * Shutdown the pipe. */ static void -pipeclose(struct pipe *pipe) +pipeclose(struct file *fp, struct pipe *pipe) { kmutex_t *lock; struct pipe *ppipe; @@ -988,7 +1016,6 @@ pipeclose(struct pipe *pipe) KASSERT(lock != NULL); mutex_enter(lock); - pipeselwakeup(pipe, pipe, POLL_HUP); /* * fd_close has issued .fo_restart to wake all waiters on this @@ -1002,14 +1029,35 @@ pipeclose(struct pipe *pipe) KASSERT(!cv_has_waiters(&pipe->pipe_wcv)); /* + * There may, however, be threads waiting in select/poll for + * I/O to be ready on this side of the pipe. Wake them (but + * don't send SIGIO as pipeselwakeup does) so they can fail + * with EBADF/POLLNVAL. + */ + selnotify(&pipe->pipe_sel, 0, NOTE_SUBMIT); + + /* * If the other side is busy, wake it up saying that * we want to close it down, which will prevent peers * from starting new I/O. Once it is no longer busy, * disconnect it. */ + KASSERT(pipe->pipe_peer != NULL || (pipe->pipe_state & PIPE_EOF) != 0); pipe->pipe_state |= PIPE_EOF; if ((ppipe = pipe->pipe_peer) != NULL) { - pipeselwakeup(ppipe, ppipe, POLL_HUP); + if (fp->f_flag & FREAD) { + struct pipe *wpipe = ppipe; + + pipeselwakeup(wpipe, POLL_ERR); + } else if (fp->f_flag & FWRITE) { + struct pipe *rpipe = ppipe; + + pipeselwakeup(rpipe, POLL_HUP); + } else { + panic("file %p pipe %p invalid direction flag 0x%x", + fp, pipe, fp->f_flag); + } + ppipe->pipe_state |= PIPE_EOF; if (ppipe->pipe_busy) { cv_broadcast(&ppipe->pipe_rcv); @@ -1065,24 +1113,6 @@ filt_pipedetach(struct knote *kn) lock = pipe->pipe_lock; mutex_enter(lock); - - switch(kn->kn_filter) { - case EVFILT_WRITE: - /* Need the peer structure, not our own. */ - pipe = pipe->pipe_peer; - - /* If reader end already closed, just return. */ - if (pipe == NULL) { - mutex_exit(lock); - return; - } - - break; - default: - /* Nothing to do. */ - break; - } - KASSERT(kn->kn_hook == pipe); selremove_knote(&pipe->pipe_sel, kn); mutex_exit(lock); @@ -1091,18 +1121,20 @@ filt_pipedetach(struct knote *kn) static int filt_piperead(struct knote *kn, long hint) { - struct pipe *pipe = ((file_t *)kn->kn_obj)->f_pipe; - struct pipe *ppipe; + struct pipe *rpipe = ((file_t *)kn->kn_obj)->f_pipe; + struct pipe *wpipe; int rv; if ((hint & NOTE_SUBMIT) == 0) { - mutex_enter(pipe->pipe_lock); + mutex_enter(rpipe->pipe_lock); + } else { + KASSERT(mutex_owned(rpipe->pipe_lock)); } - ppipe = pipe->pipe_peer; - kn->kn_data = pipe->pipe_buffer.cnt; + wpipe = rpipe->pipe_peer; + kn->kn_data = rpipe->pipe_buffer.cnt; - if ((pipe->pipe_state & PIPE_EOF) || - (ppipe == NULL) || (ppipe->pipe_state & PIPE_EOF)) { + if ((rpipe->pipe_state & PIPE_EOF) || + (wpipe == NULL) || (wpipe->pipe_state & PIPE_EOF)) { knote_set_eof(kn, 0); rv = 1; } else { @@ -1110,7 +1142,9 @@ filt_piperead(struct knote *kn, long hin } if ((hint & NOTE_SUBMIT) == 0) { - mutex_exit(pipe->pipe_lock); + mutex_exit(rpipe->pipe_lock); + } else { + KASSERT(mutex_owned(rpipe->pipe_lock)); } return rv; } @@ -1118,26 +1152,30 @@ filt_piperead(struct knote *kn, long hin static int filt_pipewrite(struct knote *kn, long hint) { - struct pipe *pipe = ((file_t *)kn->kn_obj)->f_pipe; - struct pipe *ppipe; + struct pipe *wpipe = ((file_t *)kn->kn_obj)->f_pipe; + struct pipe *rpipe; int rv; if ((hint & NOTE_SUBMIT) == 0) { - mutex_enter(pipe->pipe_lock); + mutex_enter(wpipe->pipe_lock); + } else { + KASSERT(mutex_owned(wpipe->pipe_lock)); } - ppipe = pipe->pipe_peer; + rpipe = wpipe->pipe_peer; - if ((ppipe == NULL) || (ppipe->pipe_state & PIPE_EOF)) { + if ((rpipe == NULL) || (rpipe->pipe_state & PIPE_EOF)) { kn->kn_data = 0; knote_set_eof(kn, 0); rv = 1; } else { - kn->kn_data = ppipe->pipe_buffer.size - ppipe->pipe_buffer.cnt; + kn->kn_data = rpipe->pipe_buffer.size - rpipe->pipe_buffer.cnt; rv = kn->kn_data >= PIPE_BUF; } if ((hint & NOTE_SUBMIT) == 0) { - mutex_exit(pipe->pipe_lock); + mutex_exit(wpipe->pipe_lock); + } else { + KASSERT(mutex_owned(wpipe->pipe_lock)); } return rv; } @@ -1169,16 +1207,18 @@ pipe_kqfilter(file_t *fp, struct knote * switch (kn->kn_filter) { case EVFILT_READ: + if ((fp->f_flag & FREAD) == 0) { + mutex_exit(lock); + return (EINVAL); + } kn->kn_fop = &pipe_rfiltops; break; case EVFILT_WRITE: + if ((fp->f_flag & FWRITE) == 0) { + mutex_exit(lock); + return (EINVAL); + } kn->kn_fop = &pipe_wfiltops; - pipe = pipe->pipe_peer; - if (pipe == NULL) { - /* Other end of pipe has been closed. */ - mutex_exit(lock); - return (EBADF); - } break; default: mutex_exit(lock); diff -r 2682df6741d0 -r 171bf10ec3ac tests/lib/libc/sys/t_poll.c --- a/tests/lib/libc/sys/t_poll.c Wed Sep 23 01:44:29 2026 +0000 +++ b/tests/lib/libc/sys/t_poll.c Wed Sep 23 15:34:37 2026 +0000 @@ -1499,14 +1499,6 @@ ATF_TC_BODY(pollclosedpeer_pipe_immediat { int writefd, readfd; - /* - * poll(2) returns POLLHUP|POLLOUT, which is forbidden -- - * POLLHUP and POLLOUT are mutually exclusive. And POLLHUP is - * only supposed to be returned by polling for read, not - * polling for write. So it should be POLLOUT. - */ - atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); - pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); @@ -1522,14 +1514,6 @@ ATF_TC_BODY(pollclosedpeer_pipe_immediat { int writefd, readfd; - /* - * poll(2) returns POLLHUP|POLLOUT, which is forbidden -- - * POLLHUP and POLLOUT are mutually exclusive. And POLLHUP is - * only supposed to be returned by polling for read, not - * polling for write. So it should be POLLOUT. - */ - atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); - pollclosed_pipe_setup(&writefd, &readfd); /* don't fill pipe buf */ check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); @@ -1575,14 +1559,6 @@ ATF_TC_BODY(pollclosedpeer_pipe_delayed_ { int writefd, readfd; - /* - * poll(2) returns POLLHUP|POLLOUT, which is forbidden -- - * POLLHUP and POLLOUT are mutually exclusive. And POLLHUP is - * only supposed to be returned by polling for read, not - * polling for write. So it should be POLLOUT. - */ - atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); - pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); check_pollclosedpeer_delayed_process(writefd, readfd, @@ -1615,14 +1591,6 @@ ATF_TC_BODY(pollclosedpeer_pipe_delayed_ { int writefd, readfd; - /* - * poll(2) returns POLLHUP|POLLOUT, which is forbidden -- - * POLLHUP and POLLOUT are mutually exclusive. And POLLHUP is - * only supposed to be returned by polling for read, not - * polling for write. So it should be POLLOUT. - */ - atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); - pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); check_pollclosedpeer_delayed_thread(writefd, readfd, # HG changeset patch # User Taylor R Campbell # Date 1790175593 0 # Wed Sep 23 14:59:53 2026 +0000 # Branch trunk # Node ID f2899acb7bb97ca7b25ff4cca611ccb9a340f6d3 # Parent 171bf10ec3acf28812f91dc4c4713f8aded7f973 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Don't cv_wait_sig in a loop without breaking on signal. If a signal does arrive, it will turn into a busy wait! Not helpful. But this wait should be limited to scheduling delays for other threads anyway, not for general I/O, so there's no need to be interruptible by a signal. Prompted by: PR kern/59056: poll POLLHUP bugs diff -r 171bf10ec3ac -r f2899acb7bb9 sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 15:34:37 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 14:59:53 2026 +0000 @@ -1063,7 +1063,7 @@ pipeclose(struct file *fp, struct pipe * cv_broadcast(&ppipe->pipe_rcv); cv_broadcast(&ppipe->pipe_wcv); while (ppipe->pipe_busy) - cv_wait_sig(&ppipe->pipe_draincv, lock); + cv_wait(&ppipe->pipe_draincv, lock); } ppipe->pipe_peer = NULL; } # HG changeset patch # User Taylor R Campbell # Date 1790181374 0 # Wed Sep 23 16:36:14 2026 +0000 # Branch trunk # Node ID 51ca040d15be5e71b8909f0e1336d3d2d7d143d9 # Parent f2899acb7bb97ca7b25ff4cca611ccb9a340f6d3 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Sort includes. No functional change intended. Preparation for: PR kern/58378: Kernel error code origination lacks dtrace probes diff -r f2899acb7bb9 -r 51ca040d15be sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 14:59:53 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 16:36:14 2026 +0000 @@ -58,26 +58,28 @@ __KERNEL_RCSID(0, "$NetBSD: sys_pipe.c,v 1.168 2025/07/16 19:14:13 kre Exp $"); #include -#include -#include +#include + +#include #include #include #include #include +#include #include -#include -#include +#include +#include #include +#include +#include #include -#include -#include -#include -#include +#include #include #include -#include -#include -#include +#include +#include +#include +#include static int pipe_read(file_t *, off_t *, struct uio *, kauth_cred_t, int); static int pipe_write(file_t *, off_t *, struct uio *, kauth_cred_t, int); # HG changeset patch # User Taylor R Campbell # Date 1790181425 0 # Wed Sep 23 16:37:05 2026 +0000 # Branch trunk # Node ID 138136da968376b2c10884a38f52b08c32a8a36f # Parent 51ca040d15be5e71b8909f0e1336d3d2d7d143d9 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Sprinkle SET_ERROR dtrace probes. PR kern/58378: Kernel error code origination lacks dtrace probes diff -r 51ca040d15be -r 138136da9683 sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 16:36:14 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 16:37:05 2026 +0000 @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -216,7 +217,7 @@ pipe1(struct lwp *l, int *fildes, int fl proc_t *p; if (flags & ~(O_CLOEXEC|O_CLOFORK|O_NONBLOCK|O_NOSIGPIPE)) - return EINVAL; + return SET_ERROR(EINVAL); p = curproc; rpipe = wpipe = NULL; getnanotime(&nt); @@ -257,7 +258,7 @@ pipe1(struct lwp *l, int *fildes, int fl fd_affix(p, rf, fildes[0]); fd_affix(p, wf, fildes[1]); - return (0); + return 0; free3: fd_abort(p, rf, fildes[0]); free2: @@ -266,7 +267,7 @@ free2: if (rpipe) pipefree(rpipe); - return (error); + return error; } /* @@ -290,7 +291,7 @@ pipespace(struct pipe *pipe, int size) buffer = (void *)uvm_km_alloc(kernel_map, round_page(size), 0, UVM_KMF_PAGEABLE); if (buffer == NULL) - return (ENOMEM); + return SET_ERROR(ENOMEM); atomic_add_int(&amountpipekva, size); } @@ -301,7 +302,7 @@ pipespace(struct pipe *pipe, int size) pipe->pipe_buffer.in = 0; pipe->pipe_buffer.out = 0; pipe->pipe_buffer.cnt = 0; - return (0); + return 0; } /* @@ -447,10 +448,10 @@ pipe_read(file_t *fp, off_t *offset, str */ if ((fp->f_flag & FNONBLOCK) != 0) { if (__predict_false(uio->uio_resid == 0)) - return (0); + return 0; if (atomic_load_relaxed(&bp->cnt) == 0 && (atomic_load_relaxed(&rpipe->pipe_state) & PIPE_EOF) == 0) - return (EAGAIN); + return SET_ERROR(EAGAIN); } mutex_enter(lock); @@ -518,7 +519,7 @@ again: * Don't block on non-blocking I/O. */ if (fp->f_flag & FNONBLOCK) { - error = EAGAIN; + error = SET_ERROR(EAGAIN); break; } @@ -537,7 +538,7 @@ again: cv_broadcast(&wpipe->pipe_wcv); if (wakeup_state & PIPE_RESTART) { - error = ERESTART; + error = SET_ERROR(ERESTART); goto unlocked_error; } @@ -576,7 +577,7 @@ unlocked_error: } mutex_exit(lock); - return (error); + return error; } static int @@ -602,7 +603,7 @@ pipe_write(file_t *fp, off_t *offset, st */ if (rpipe == NULL || (rpipe->pipe_state & PIPE_EOF) != 0) { mutex_exit(lock); - return EPIPE; + return SET_ERROR(EPIPE); } ++wpipe->pipe_busy; @@ -613,7 +614,7 @@ pipe_write(file_t *fp, off_t *offset, st cv_broadcast(&wpipe->pipe_draincv); } mutex_exit(lock); - return (error); + return error; } bp = &rpipe->pipe_buffer; @@ -699,7 +700,7 @@ pipe_write(file_t *fp, off_t *offset, st * Don't block on non-blocking I/O. */ if (fp->f_flag & FNONBLOCK) { - error = EAGAIN; + error = SET_ERROR(EAGAIN); break; } @@ -711,7 +712,7 @@ pipe_write(file_t *fp, off_t *offset, st pipeselwakeup(rpipe, POLL_IN); if (wakeup_state & PIPE_RESTART) { - error = ERESTART; + error = SET_ERROR(ERESTART); break; } @@ -725,7 +726,7 @@ pipe_write(file_t *fp, off_t *offset, st * XXX Distinguish reader-closed from writer-closed? */ if (rpipe->pipe_state & PIPE_EOF) { - error = EPIPE; + error = SET_ERROR(EPIPE); break; } @@ -773,7 +774,7 @@ pipe_write(file_t *fp, off_t *offset, st pipeunlock(rpipe); mutex_exit(lock); - return (error); + return error; } /* @@ -788,7 +789,7 @@ pipe_ioctl(file_t *fp, u_long cmd, void switch (cmd) { case FIONBIO: - return (0); + return 0; case FIOASYNC: mutex_enter(lock); @@ -798,13 +799,13 @@ pipe_ioctl(file_t *fp, u_long cmd, void pipe->pipe_state &= ~PIPE_ASYNC; } mutex_exit(lock); - return (0); + return 0; case FIONREAD: mutex_enter(lock); *(int *)data = pipe->pipe_buffer.cnt; mutex_exit(lock); - return (0); + return 0; case FIONWRITE: /* Look at other side */ @@ -815,7 +816,7 @@ pipe_ioctl(file_t *fp, u_long cmd, void else *(int *)data = pipe->pipe_buffer.cnt; mutex_exit(lock); - return (0); + return 0; case FIONSPACE: /* Look at other side */ @@ -827,7 +828,7 @@ pipe_ioctl(file_t *fp, u_long cmd, void *(int *)data = pipe->pipe_buffer.size - pipe->pipe_buffer.cnt; mutex_exit(lock); - return (0); + return 0; case TIOCSPGRP: case FIOSETOWN: @@ -838,7 +839,7 @@ pipe_ioctl(file_t *fp, u_long cmd, void return fgetown(pipe->pipe_pgid, cmd, data); } - return (EPASSTHROUGH); + return EPASSTHROUGH; } int @@ -899,7 +900,7 @@ pipe_poll(file_t *fp, int events) selrecord(curlwp, &pipe->pipe_sel); mutex_exit(pipe->pipe_lock); - return (revents); + return revents; } static int @@ -936,7 +937,7 @@ pipe_close(file_t *fp) fp->f_pipe = NULL; pipeclose(fp, pipe); - return (0); + return 0; } static void @@ -970,7 +971,7 @@ pipe_fpathconf(struct file *fp, int name *retval = PIPE_BUF; return 0; default: - return EINVAL; + return SET_ERROR(EINVAL); } } @@ -978,7 +979,7 @@ static int pipe_posix_fadvise(struct file *fp, off_t offset, off_t len, int advice) { - return ESPIPE; + return SET_ERROR(ESPIPE); } static void @@ -1211,27 +1212,27 @@ pipe_kqfilter(file_t *fp, struct knote * case EVFILT_READ: if ((fp->f_flag & FREAD) == 0) { mutex_exit(lock); - return (EINVAL); + return SET_ERROR(EINVAL); } kn->kn_fop = &pipe_rfiltops; break; case EVFILT_WRITE: if ((fp->f_flag & FWRITE) == 0) { mutex_exit(lock); - return (EINVAL); + return SET_ERROR(EINVAL); } kn->kn_fop = &pipe_wfiltops; break; default: mutex_exit(lock); - return (EINVAL); + return SET_ERROR(EINVAL); } kn->kn_hook = pipe; selrecord_knote(&pipe->pipe_sel, kn); mutex_exit(lock); - return (0); + return 0; } /*