summaryrefslogtreecommitdiffstats
path: root/sys/kern/sys_pipe.c
diff options
context:
space:
mode:
authoranton <anton@openbsd.org>2019-11-29 15:15:10 +0000
committeranton <anton@openbsd.org>2019-11-29 15:15:10 +0000
commitbc80a0d63708bf21ebd0ec79b02c67f412c57718 (patch)
tree93b601d8ff701fdf9b5ad44d31e93cf58ccdf35d /sys/kern/sys_pipe.c
parentAdd defines for changer mode page codes. Move AUDIO_PAGE define to (diff)
downloadwireguard-openbsd-bc80a0d63708bf21ebd0ec79b02c67f412c57718.tar.xz
wireguard-openbsd-bc80a0d63708bf21ebd0ec79b02c67f412c57718.zip
Start protecting the pipe_busy field of struct pipe using a global
rwlock. This lock is shared among all pipes for simplicity. In the future, the lock will probably be replaced with one lock per pipe pair, just like FreeBSD and NetBSD does. While here, extract the common rundown wakeup logic into a dedicated function. Thanks to cheloha@ for testing and feedback. ok mpi@ visa@
Diffstat (limited to 'sys/kern/sys_pipe.c')
-rw-r--r--sys/kern/sys_pipe.c78
1 files changed, 54 insertions, 24 deletions
diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c
index 034d7937932..5586bab538f 100644
--- a/sys/kern/sys_pipe.c
+++ b/sys/kern/sys_pipe.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sys_pipe.c,v 1.99 2019/11/19 19:19:28 anton Exp $ */
+/* $OpenBSD: sys_pipe.c,v 1.100 2019/11/29 15:15:10 anton Exp $ */
/*
* Copyright (c) 1996 John S. Dyson
@@ -96,6 +96,11 @@ static unsigned int amountpipekva;
struct pool pipe_pool;
+/*
+ * Global lock protecting fields of `struct pipe'.
+ */
+struct rwlock pipe_lock = RWLOCK_INITIALIZER("pipeglk");
+
int dopipe(struct proc *, int *, int);
int pipelock(struct pipe *);
void pipeunlock(struct pipe *);
@@ -103,6 +108,7 @@ void pipeselwakeup(struct pipe *);
struct pipe *pipe_create(void);
void pipe_destroy(struct pipe *);
+int pipe_rundown(struct pipe *);
int pipe_buffer_realloc(struct pipe *, u_int);
void pipe_buffer_free(struct pipe *);
@@ -335,11 +341,19 @@ pipe_read(struct file *fp, struct uio *uio, int fflags)
KERNEL_LOCK();
- error = pipelock(rpipe);
- if (error)
- goto done;
-
+ rw_enter_write(&pipe_lock);
++rpipe->pipe_busy;
+ rw_exit_write(&pipe_lock);
+
+ error = pipelock(rpipe);
+ if (error) {
+ rw_enter_write(&pipe_lock);
+ --rpipe->pipe_busy;
+ pipe_rundown(rpipe);
+ rw_exit_write(&pipe_lock);
+ KERNEL_UNLOCK();
+ return (error);
+ }
while (uio->uio_resid) {
/*
@@ -415,15 +429,11 @@ pipe_read(struct file *fp, struct uio *uio, int fflags)
if (error == 0)
getnanotime(&rpipe->pipe_atime);
unlocked_error:
+ rw_enter_write(&pipe_lock);
+
--rpipe->pipe_busy;
- /*
- * PIPE_WANTD processing only makes sense if pipe_busy is 0.
- */
- if ((rpipe->pipe_busy == 0) && (rpipe->pipe_state & PIPE_WANTD)) {
- rpipe->pipe_state &= ~(PIPE_WANTD|PIPE_WANTW);
- wakeup(rpipe);
- } else if (rpipe->pipe_buffer.cnt < MINPIPESIZE) {
+ if (pipe_rundown(rpipe) == 0 && rpipe->pipe_buffer.cnt < MINPIPESIZE) {
/*
* Handle write blocking hysteresis.
*/
@@ -433,10 +443,11 @@ unlocked_error:
}
}
+ rw_exit_write(&pipe_lock);
+
if ((rpipe->pipe_buffer.size - rpipe->pipe_buffer.cnt) >= PIPE_BUF)
pipeselwakeup(rpipe);
-done:
KERNEL_UNLOCK();
return (error);
}
@@ -461,17 +472,16 @@ pipe_write(struct file *fp, struct uio *uio, int fflags)
return (EPIPE);
}
+ rw_enter_write(&pipe_lock);
++wpipe->pipe_busy;
+ rw_exit_write(&pipe_lock);
error = pipelock(wpipe);
if (error) {
- /* Failed to acquire lock, wakeup if run-down can proceed. */
+ rw_enter_write(&pipe_lock);
--wpipe->pipe_busy;
- if ((wpipe->pipe_busy == 0) &&
- (wpipe->pipe_state & PIPE_WANTD)) {
- wpipe->pipe_state &= ~(PIPE_WANTD | PIPE_WANTR);
- wakeup(wpipe);
- }
+ pipe_rundown(wpipe);
+ rw_exit_write(&pipe_lock);
KERNEL_UNLOCK();
return (error);
}
@@ -612,12 +622,11 @@ pipe_write(struct file *fp, struct uio *uio, int fflags)
pipeunlock(wpipe);
unlocked_error:
+ rw_enter_write(&pipe_lock);
+
--wpipe->pipe_busy;
- if ((wpipe->pipe_busy == 0) && (wpipe->pipe_state & PIPE_WANTD)) {
- wpipe->pipe_state &= ~(PIPE_WANTD | PIPE_WANTR);
- wakeup(wpipe);
- } else if (wpipe->pipe_buffer.cnt > 0) {
+ if (pipe_rundown(wpipe) == 0 && wpipe->pipe_buffer.cnt > 0) {
/*
* If we have put any characters in the buffer, we wake up
* the reader.
@@ -628,6 +637,8 @@ unlocked_error:
}
}
+ rw_exit_write(&pipe_lock);
+
/*
* Don't return EPIPE if I/O was successful
*/
@@ -810,11 +821,13 @@ pipe_destroy(struct pipe *cpipe)
* we want to close it down.
*/
cpipe->pipe_state |= PIPE_EOF;
+ rw_enter_write(&pipe_lock);
while (cpipe->pipe_busy) {
wakeup(cpipe);
cpipe->pipe_state |= PIPE_WANTD;
- tsleep(cpipe, PRIBIO, "pipecl", 0);
+ rwsleep_nsec(cpipe, &pipe_lock, PRIBIO, "pipecl", INFSLP);
}
+ rw_exit_write(&pipe_lock);
/*
* Disconnect from peer
@@ -834,6 +847,23 @@ pipe_destroy(struct pipe *cpipe)
pool_put(&pipe_pool, cpipe);
}
+/*
+ * Returns non-zero if a rundown is currently ongoing.
+ */
+int
+pipe_rundown(struct pipe *cpipe)
+{
+ rw_assert_wrlock(&pipe_lock);
+
+ if (cpipe->pipe_busy > 0 || (cpipe->pipe_state & PIPE_WANTD) == 0)
+ return (0);
+
+ /* Only wakeup pipe_destroy() once the pipe is no longer busy. */
+ cpipe->pipe_state &= ~(PIPE_WANTD | PIPE_WANTR | PIPE_WANTW);
+ wakeup(cpipe);
+ return (1);
+}
+
int
pipe_kqfilter(struct file *fp, struct knote *kn)
{