Restructure pipe_read in order to eliminate several race conditions.
Submitted by: Matthew Dillon <dillon@apollo.backplane.com> and myself
This commit is contained in:
parent
8759eb5c80
commit
3d41489171
@ -16,7 +16,7 @@
|
||||
* 4. Modifications may be freely made to this file if the above conditions
|
||||
* are met.
|
||||
*
|
||||
* $Id: sys_pipe.c,v 1.50 1999/02/04 23:50:49 dillon Exp $
|
||||
* $Id: sys_pipe.c,v 1.51 1999/04/04 21:41:15 dt Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -327,11 +327,15 @@ pipe_read(fp, uio, cred, flags)
|
||||
{
|
||||
|
||||
struct pipe *rpipe = (struct pipe *) fp->f_data;
|
||||
int error = 0;
|
||||
int error;
|
||||
int nread = 0;
|
||||
u_int size;
|
||||
|
||||
++rpipe->pipe_busy;
|
||||
error = pipelock(rpipe, 1);
|
||||
if (error)
|
||||
goto unlocked_error;
|
||||
|
||||
while (uio->uio_resid) {
|
||||
/*
|
||||
* normal pipe buffer receive
|
||||
@ -342,11 +346,9 @@ pipe_read(fp, uio, cred, flags)
|
||||
size = rpipe->pipe_buffer.cnt;
|
||||
if (size > (u_int) uio->uio_resid)
|
||||
size = (u_int) uio->uio_resid;
|
||||
if ((error = pipelock(rpipe,1)) == 0) {
|
||||
error = uiomove( &rpipe->pipe_buffer.buffer[rpipe->pipe_buffer.out],
|
||||
|
||||
error = uiomove(&rpipe->pipe_buffer.buffer[rpipe->pipe_buffer.out],
|
||||
size, uio);
|
||||
pipeunlock(rpipe);
|
||||
}
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
@ -355,21 +357,29 @@ pipe_read(fp, uio, cred, flags)
|
||||
rpipe->pipe_buffer.out = 0;
|
||||
|
||||
rpipe->pipe_buffer.cnt -= size;
|
||||
|
||||
/*
|
||||
* If there is no more to read in the pipe, reset
|
||||
* its pointers to the beginning. This improves
|
||||
* cache hit stats.
|
||||
*/
|
||||
if (rpipe->pipe_buffer.cnt == 0) {
|
||||
rpipe->pipe_buffer.in = 0;
|
||||
rpipe->pipe_buffer.out = 0;
|
||||
}
|
||||
nread += size;
|
||||
#ifndef PIPE_NODIRECT
|
||||
/*
|
||||
* Direct copy, bypassing a kernel buffer.
|
||||
*/
|
||||
} else if ((size = rpipe->pipe_map.cnt) &&
|
||||
(rpipe->pipe_state & PIPE_DIRECTW)) {
|
||||
caddr_t va;
|
||||
(rpipe->pipe_state & PIPE_DIRECTW)) {
|
||||
caddr_t va;
|
||||
if (size > (u_int) uio->uio_resid)
|
||||
size = (u_int) uio->uio_resid;
|
||||
if ((error = pipelock(rpipe,1)) == 0) {
|
||||
va = (caddr_t) rpipe->pipe_map.kva + rpipe->pipe_map.pos;
|
||||
error = uiomove(va, size, uio);
|
||||
pipeunlock(rpipe);
|
||||
}
|
||||
|
||||
va = (caddr_t) rpipe->pipe_map.kva + rpipe->pipe_map.pos;
|
||||
error = uiomove(va, size, uio);
|
||||
if (error)
|
||||
break;
|
||||
nread += size;
|
||||
@ -381,32 +391,6 @@ pipe_read(fp, uio, cred, flags)
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/*
|
||||
* If there is no more to read in the pipe, reset
|
||||
* its pointers to the beginning. This improves
|
||||
* cache hit stats.
|
||||
*
|
||||
* We get this over with now because it may block
|
||||
* and cause the state to change out from under us,
|
||||
* rather then have to re-test the state both before
|
||||
* and after this fragment.
|
||||
*/
|
||||
|
||||
if ((error = pipelock(rpipe,1)) == 0) {
|
||||
if (rpipe->pipe_buffer.cnt == 0) {
|
||||
rpipe->pipe_buffer.in = 0;
|
||||
rpipe->pipe_buffer.out = 0;
|
||||
}
|
||||
pipeunlock(rpipe);
|
||||
|
||||
/*
|
||||
* If pipe filled up due to pipelock
|
||||
* blocking, loop back up.
|
||||
*/
|
||||
if (rpipe->pipe_buffer.cnt > 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* detect EOF condition
|
||||
*/
|
||||
@ -424,55 +408,49 @@ pipe_read(fp, uio, cred, flags)
|
||||
}
|
||||
|
||||
/*
|
||||
* break if error (signal via pipelock), or if some
|
||||
* data was read
|
||||
* Break if some data was read.
|
||||
*/
|
||||
if (error || nread > 0)
|
||||
if (nread > 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Handle non-blocking mode operation
|
||||
* Unlock the pipe buffer for our remaining processing. We
|
||||
* will either break out with an error or we will sleep and
|
||||
* relock to loop.
|
||||
*/
|
||||
pipeunlock(rpipe);
|
||||
|
||||
if (fp->f_flag & FNONBLOCK) {
|
||||
/*
|
||||
* Handle non-blocking mode operation or
|
||||
* wait for more data.
|
||||
*/
|
||||
if (fp->f_flag & FNONBLOCK)
|
||||
error = EAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for more data
|
||||
*/
|
||||
|
||||
rpipe->pipe_state |= PIPE_WANTR;
|
||||
if ((error = tsleep(rpipe, PRIBIO|PCATCH, "piperd", 0)) != 0) {
|
||||
break;
|
||||
else {
|
||||
rpipe->pipe_state |= PIPE_WANTR;
|
||||
if ((error = tsleep(rpipe, PRIBIO|PCATCH, "piperd", 0)) == 0)
|
||||
error = pipelock(rpipe, 1);
|
||||
}
|
||||
if (error)
|
||||
goto unlocked_error;
|
||||
}
|
||||
}
|
||||
pipeunlock(rpipe);
|
||||
|
||||
if (error == 0)
|
||||
getnanotime(&rpipe->pipe_atime);
|
||||
|
||||
unlocked_error:
|
||||
--rpipe->pipe_busy;
|
||||
|
||||
/*
|
||||
* PIPE_WANT processing only makes sense if pipe_busy is 0.
|
||||
*/
|
||||
if ((rpipe->pipe_busy == 0) && (rpipe->pipe_state & PIPE_WANT)) {
|
||||
rpipe->pipe_state &= ~(PIPE_WANT|PIPE_WANTW);
|
||||
wakeup(rpipe);
|
||||
} else if (rpipe->pipe_buffer.cnt < MINPIPESIZE) {
|
||||
/*
|
||||
* If there is no more to read in the pipe, reset
|
||||
* its pointers to the beginning. This improves
|
||||
* cache hit stats.
|
||||
*/
|
||||
if (rpipe->pipe_buffer.cnt == 0) {
|
||||
if ((error == 0) && (error = pipelock(rpipe,1)) == 0) {
|
||||
rpipe->pipe_buffer.in = 0;
|
||||
rpipe->pipe_buffer.out = 0;
|
||||
pipeunlock(rpipe);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the "write-side" has been blocked, wake it up now.
|
||||
* Handle write blocking hysteresis.
|
||||
*/
|
||||
if (rpipe->pipe_state & PIPE_WANTW) {
|
||||
rpipe->pipe_state &= ~PIPE_WANTW;
|
||||
|
Loading…
Reference in New Issue
Block a user