History

watch_queue: Fix lack of barrier/sync/lock between post and read

There's nothing to synchronise post_one_notification() versus pipe_read(). Whilst posting is done under pipe->rd_wait.lock, the reader only takes pipe->mutex which cannot bar notification posting as that may need to be made from contexts that cannot sleep. Fix this by setting pipe->head with a barrier in post_one_notification() and reading pipe->head with a barrier in pipe_read(). If that's not sufficient, the rd_wait.lock will need to be taken, possibly in a ->confirm() op so that it only applies to notifications. The lock would, however, have to be dropped before copy_page_to_iter() is invoked.

David Howells committed 4 years ago

Commit 2ed147f1 parent 7ea1a012 files · +3 −2Browse files

Unified · Split

2 files
  1. fs/pipe.c +2 −1
  2. kernel/watch_queue.c +1 −1
fs/pipe.c+2−1
@@ -253,7 +253,8 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to) · 252 unmodified lines
253253 */
254254 was_full = pipe_full(pipe->head, pipe->tail, pipe->max_usage);
255255 for (;;) {
256− unsigned int head = pipe->head;
256+ /* Read ->head with a barrier vs post_one_notification() */
257+ unsigned int head = smp_load_acquire(&pipe->head);
257258 unsigned int tail = pipe->tail;
258259 unsigned int mask = pipe->ring_size - 1;
259260
kernel/watch_queue.c+1−1
@@ -113,7 +113,7 @@ static bool post_one_notification(struct watch_queue *wqueue, · 112 unmodified lines
113113 buf->offset = offset;
114114 buf->len = len;
115115 buf->flags = PIPE_BUF_FLAG_WHOLE;
116− pipe->head = head + 1;
116+ smp_store_release(&pipe->head, head + 1); /* vs pipe_read() */
117117
118118 if (!test_and_clear_bit(note, wqueue->notes_bitmap)) {
119119 spin_unlock_irq(&pipe->rd_wait.lock);