Skip to content

Commit

Permalink
pipe: Add notification lossage handling
Browse files Browse the repository at this point in the history
Add handling for loss of notifications by having read() insert a
loss-notification message after it has read the pipe buffer that was last
in the ring when the loss occurred.

Lossage can come about either by running out of notification descriptors or
by running out of space in the pipe ring.

Signed-off-by: David Howells <dhowells@redhat.com>
  • Loading branch information
David Howells committed May 19, 2020
1 parent 8cfba76 commit e7d553d
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 0 deletions.
28 changes: 28 additions & 0 deletions fs/pipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,30 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
unsigned int tail = pipe->tail;
unsigned int mask = pipe->ring_size - 1;

#ifdef CONFIG_WATCH_QUEUE
if (pipe->note_loss) {
struct watch_notification n;

if (total_len < 8) {
if (ret == 0)
ret = -ENOBUFS;
break;
}

n.type = WATCH_TYPE_META;
n.subtype = WATCH_META_LOSS_NOTIFICATION;
n.info = watch_sizeof(n);
if (copy_to_iter(&n, sizeof(n), to) != sizeof(n)) {
if (ret == 0)
ret = -EFAULT;
break;
}
ret += sizeof(n);
total_len -= sizeof(n);
pipe->note_loss = false;
}
#endif

if (!pipe_empty(head, tail)) {
struct pipe_buffer *buf = &pipe->bufs[tail & mask];
size_t chars = buf->len;
Expand Down Expand Up @@ -355,6 +379,10 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
if (!buf->len) {
pipe_buf_release(pipe, buf);
spin_lock_irq(&pipe->rd_wait.lock);
#ifdef CONFIG_WATCH_QUEUE
if (buf->flags & PIPE_BUF_FLAG_LOSS)
pipe->note_loss = true;
#endif
tail++;
pipe->tail = tail;
spin_unlock_irq(&pipe->rd_wait.lock);
Expand Down
7 changes: 7 additions & 0 deletions include/linux/pipe_fs_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */
#define PIPE_BUF_FLAG_PACKET 0x08 /* read() as a packet */
#define PIPE_BUF_FLAG_WHOLE 0x10 /* read() must return entire buffer or error */
#ifdef CONFIG_WATCH_QUEUE
#define PIPE_BUF_FLAG_LOSS 0x20 /* Message loss happened after this buffer */
#endif

/**
* struct pipe_buffer - a linux kernel pipe buffer
Expand All @@ -34,6 +37,7 @@ struct pipe_buffer {
* @wr_wait: writer wait point in case of full pipe
* @head: The point of buffer production
* @tail: The point of buffer consumption
* @note_loss: The next read() should insert a data-lost message
* @max_usage: The maximum number of slots that may be used in the ring
* @ring_size: total number of buffers (should be a power of 2)
* @nr_accounted: The amount this pipe accounts for in user->pipe_bufs
Expand All @@ -56,6 +60,9 @@ struct pipe_inode_info {
unsigned int tail;
unsigned int max_usage;
unsigned int ring_size;
#ifdef CONFIG_WATCH_QUEUE
bool note_loss;
#endif
unsigned int nr_accounted;
unsigned int readers;
unsigned int writers;
Expand Down
2 changes: 2 additions & 0 deletions kernel/watch_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ static bool post_one_notification(struct watch_queue *wqueue,
return done;

lost:
buf = &pipe->bufs[(head - 1) & mask];
buf->flags |= PIPE_BUF_FLAG_LOSS;
goto out;
}

Expand Down
3 changes: 3 additions & 0 deletions samples/watch_queue/watch_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ static void consumer(int fd)
(n.n.info & WATCH_INFO_ID) >>
WATCH_INFO_ID__SHIFT);
break;
case WATCH_META_LOSS_NOTIFICATION:
printf("-- LOSS --\n");
break;
default:
printf("other meta record\n");
break;
Expand Down

0 comments on commit e7d553d

Please sign in to comment.