Skip to content

Commit

Permalink
pipe: Use head and tail pointers for the ring, not cursor and length
Browse files Browse the repository at this point in the history
Convert pipes to use head and tail pointers for the buffer ring rather than
pointer and length as the latter requires two atomic ops to update (or a
combined op) whereas the former only requires one.

 (1) The head pointer is the point at which production occurs and points to
     the slot in which the next buffer will be placed.  This is equivalent
     to pipe->curbuf + pipe->nrbufs.

     The head pointer belongs to the write-side.

 (2) The tail pointer is the point at which consumption occurs.  It points
     to the next slot to be consumed.  This is equivalent to pipe->curbuf.

     The tail pointer belongs to the read-side.

 (3) head and tail are allowed to run to UINT_MAX and wrap naturally.  They
     are only masked off when the array is being accessed, e.g.:

	pipe->bufs[head & mask]

     This means that it is not necessary to have a dead slot in the ring as
     head == tail isn't ambiguous.

 (4) The ring is empty if "head == tail".

     A helper, pipe_empty(), is provided for this.

 (5) The occupancy of the ring is "head - tail".

     A helper, pipe_occupancy(), is provided for this.

 (6) The number of free slots in the ring is "pipe->ring_size - occupancy".

     A helper, pipe_space_for_user() is provided to indicate how many slots
     userspace may use.

 (7) The ring is full if "head - tail >= pipe->ring_size".

     A helper, pipe_full(), is provided for this.

Signed-off-by: David Howells <dhowells@redhat.com>
  • Loading branch information
David Howells committed Oct 31, 2019
1 parent f94df98 commit 8cefc10
Show file tree
Hide file tree
Showing 7 changed files with 448 additions and 292 deletions.
16 changes: 7 additions & 9 deletions drivers/char/virtio_console.c
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
.pos = *ppos,
.u.data = &sgl,
};
unsigned int occupancy;

/*
* Rproc_serial does not yet support splice. To support splice
Expand All @@ -929,29 +930,26 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
if (is_rproc_serial(port->out_vq->vdev))
return -EINVAL;

/*
* pipe->nrbufs == 0 means there are no data to transfer,
* so this returns just 0 for no data.
*/
pipe_lock(pipe);
if (!pipe->nrbufs) {
ret = 0;
ret = 0;
if (pipe_empty(pipe->head, pipe->tail))
goto error_out;
}

ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
if (ret < 0)
goto error_out;

buf = alloc_buf(port->portdev->vdev, 0, pipe->nrbufs);
occupancy = pipe_occupancy(pipe->head, pipe->tail);
buf = alloc_buf(port->portdev->vdev, 0, occupancy);

if (!buf) {
ret = -ENOMEM;
goto error_out;
}

sgl.n = 0;
sgl.len = 0;
sgl.size = pipe->nrbufs;
sgl.size = occupancy;
sgl.sg = buf->sg;
sg_init_table(sgl.sg, sgl.size);
ret = __splice_from_pipe(pipe, &sd, pipe_to_sg);
Expand Down
31 changes: 18 additions & 13 deletions fs/fuse/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
cs->pipebufs++;
cs->nr_segs--;
} else {
if (cs->nr_segs == cs->pipe->buffers)
if (cs->nr_segs >= cs->pipe->ring_size)
return -EIO;

page = alloc_page(GFP_HIGHUSER);
Expand Down Expand Up @@ -879,7 +879,7 @@ static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
struct pipe_buffer *buf;
int err;

if (cs->nr_segs == cs->pipe->buffers)
if (cs->nr_segs >= cs->pipe->ring_size)
return -EIO;

err = unlock_request(cs->req);
Expand Down Expand Up @@ -1341,7 +1341,7 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
if (!fud)
return -EPERM;

bufs = kvmalloc_array(pipe->buffers, sizeof(struct pipe_buffer),
bufs = kvmalloc_array(pipe->ring_size, sizeof(struct pipe_buffer),
GFP_KERNEL);
if (!bufs)
return -ENOMEM;
Expand All @@ -1353,7 +1353,7 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
if (ret < 0)
goto out;

if (pipe->nrbufs + cs.nr_segs > pipe->buffers) {
if (pipe_occupancy(pipe->head, pipe->tail) + cs.nr_segs > pipe->ring_size) {
ret = -EIO;
goto out;
}
Expand Down Expand Up @@ -1935,6 +1935,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
struct file *out, loff_t *ppos,
size_t len, unsigned int flags)
{
unsigned int head, tail, mask, count;
unsigned nbuf;
unsigned idx;
struct pipe_buffer *bufs;
Expand All @@ -1949,17 +1950,21 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,

pipe_lock(pipe);

bufs = kvmalloc_array(pipe->nrbufs, sizeof(struct pipe_buffer),
GFP_KERNEL);
head = pipe->head;
tail = pipe->tail;
mask = pipe->ring_size - 1;
count = head - tail;

bufs = kvmalloc_array(count, sizeof(struct pipe_buffer), GFP_KERNEL);
if (!bufs) {
pipe_unlock(pipe);
return -ENOMEM;
}

nbuf = 0;
rem = 0;
for (idx = 0; idx < pipe->nrbufs && rem < len; idx++)
rem += pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len;
for (idx = tail; idx < head && rem < len; idx++)
rem += pipe->bufs[idx & mask].len;

ret = -EINVAL;
if (rem < len)
Expand All @@ -1970,16 +1975,16 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
struct pipe_buffer *ibuf;
struct pipe_buffer *obuf;

BUG_ON(nbuf >= pipe->buffers);
BUG_ON(!pipe->nrbufs);
ibuf = &pipe->bufs[pipe->curbuf];
BUG_ON(nbuf >= pipe->ring_size);
BUG_ON(tail == head);
ibuf = &pipe->bufs[tail & mask];
obuf = &bufs[nbuf];

if (rem >= ibuf->len) {
*obuf = *ibuf;
ibuf->ops = NULL;
pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
pipe->nrbufs--;
tail++;
pipe->tail = tail;
} else {
if (!pipe_buf_get(pipe, ibuf))
goto out_free;
Expand Down
Loading

0 comments on commit 8cefc10

Please sign in to comment.