Skip to content

Commit e7d553d

Browse files
committed
pipe: Add notification lossage handling
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 <[email protected]>
1 parent 8cfba76 commit e7d553d

File tree

4 files changed

+40
-0
lines changed

4 files changed

+40
-0
lines changed

fs/pipe.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,30 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
314314
unsigned int tail = pipe->tail;
315315
unsigned int mask = pipe->ring_size - 1;
316316

317+
#ifdef CONFIG_WATCH_QUEUE
318+
if (pipe->note_loss) {
319+
struct watch_notification n;
320+
321+
if (total_len < 8) {
322+
if (ret == 0)
323+
ret = -ENOBUFS;
324+
break;
325+
}
326+
327+
n.type = WATCH_TYPE_META;
328+
n.subtype = WATCH_META_LOSS_NOTIFICATION;
329+
n.info = watch_sizeof(n);
330+
if (copy_to_iter(&n, sizeof(n), to) != sizeof(n)) {
331+
if (ret == 0)
332+
ret = -EFAULT;
333+
break;
334+
}
335+
ret += sizeof(n);
336+
total_len -= sizeof(n);
337+
pipe->note_loss = false;
338+
}
339+
#endif
340+
317341
if (!pipe_empty(head, tail)) {
318342
struct pipe_buffer *buf = &pipe->bufs[tail & mask];
319343
size_t chars = buf->len;
@@ -355,6 +379,10 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
355379
if (!buf->len) {
356380
pipe_buf_release(pipe, buf);
357381
spin_lock_irq(&pipe->rd_wait.lock);
382+
#ifdef CONFIG_WATCH_QUEUE
383+
if (buf->flags & PIPE_BUF_FLAG_LOSS)
384+
pipe->note_loss = true;
385+
#endif
358386
tail++;
359387
pipe->tail = tail;
360388
spin_unlock_irq(&pipe->rd_wait.lock);

include/linux/pipe_fs_i.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */
1010
#define PIPE_BUF_FLAG_PACKET 0x08 /* read() as a packet */
1111
#define PIPE_BUF_FLAG_WHOLE 0x10 /* read() must return entire buffer or error */
12+
#ifdef CONFIG_WATCH_QUEUE
13+
#define PIPE_BUF_FLAG_LOSS 0x20 /* Message loss happened after this buffer */
14+
#endif
1215

1316
/**
1417
* struct pipe_buffer - a linux kernel pipe buffer
@@ -34,6 +37,7 @@ struct pipe_buffer {
3437
* @wr_wait: writer wait point in case of full pipe
3538
* @head: The point of buffer production
3639
* @tail: The point of buffer consumption
40+
* @note_loss: The next read() should insert a data-lost message
3741
* @max_usage: The maximum number of slots that may be used in the ring
3842
* @ring_size: total number of buffers (should be a power of 2)
3943
* @nr_accounted: The amount this pipe accounts for in user->pipe_bufs
@@ -56,6 +60,9 @@ struct pipe_inode_info {
5660
unsigned int tail;
5761
unsigned int max_usage;
5862
unsigned int ring_size;
63+
#ifdef CONFIG_WATCH_QUEUE
64+
bool note_loss;
65+
#endif
5966
unsigned int nr_accounted;
6067
unsigned int readers;
6168
unsigned int writers;

kernel/watch_queue.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ static bool post_one_notification(struct watch_queue *wqueue,
132132
return done;
133133

134134
lost:
135+
buf = &pipe->bufs[(head - 1) & mask];
136+
buf->flags |= PIPE_BUF_FLAG_LOSS;
135137
goto out;
136138
}
137139

samples/watch_queue/watch_test.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ static void consumer(int fd)
120120
(n.n.info & WATCH_INFO_ID) >>
121121
WATCH_INFO_ID__SHIFT);
122122
break;
123+
case WATCH_META_LOSS_NOTIFICATION:
124+
printf("-- LOSS --\n");
125+
break;
123126
default:
124127
printf("other meta record\n");
125128
break;

0 commit comments

Comments
 (0)