Commit c2179a48 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix race between ll_rw_block() and block_write_full_page()

Fix a race which was identified by Daniel McNeil <daniel@osdl.org>

If a buffer_head is under I/O due to JBD's ordered data writeout (which uses
ll_rw_block()) then either filemap_fdatawrite() or filemap_fdatawait() need
to wait on the buffer's existing I/O.

Presently neither will do so, because __block_write_full_page() will not
actually submit any I/O and will hence not mark the page as being under
writeback.

The best-performing fix would be to somehow mark the page as being under
writeback and defer waiting for the ll_rw_block-initiated I/O until
filemap_fdatawait()-time.  But this is hard, because in
__block_write_full_page() we do not have control of the buffer_head's end_io
handler.  Possibly we could make JBD call into end_buffer_async_write(), but
that gets nasty.

This patch makes __block_write_full_page() wait for any buffer_head I/O to
complete before inspecting the buffer_head state.  It only does this in the
case where __block_write_full_page() was called for a "data-integrity" write:
(wbc->sync_mode != WB_SYNC_NONE).

Probably it doesn't matter, because kjournald is currently submitting (or has
already submitted) all dirty buffers anyway.
parent bc0e2bbf
...@@ -1806,23 +1806,24 @@ static int __block_write_full_page(struct inode *inode, struct page *page, ...@@ -1806,23 +1806,24 @@ static int __block_write_full_page(struct inode *inode, struct page *page,
do { do {
get_bh(bh); get_bh(bh);
if (buffer_mapped(bh) && buffer_dirty(bh)) { if (!buffer_mapped(bh))
if (wbc->sync_mode != WB_SYNC_NONE) { continue;
lock_buffer(bh); if (wbc->sync_mode != WB_SYNC_NONE) {
} else { lock_buffer(bh);
if (test_set_buffer_locked(bh)) { } else {
if (test_set_buffer_locked(bh)) {
if (buffer_dirty(bh))
__set_page_dirty_nobuffers(page); __set_page_dirty_nobuffers(page);
continue; continue;
}
}
if (test_clear_buffer_dirty(bh)) {
if (!buffer_uptodate(bh))
buffer_error();
mark_buffer_async_write(bh);
} else {
unlock_buffer(bh);
} }
} }
if (test_clear_buffer_dirty(bh)) {
if (!buffer_uptodate(bh))
buffer_error();
mark_buffer_async_write(bh);
} else {
unlock_buffer(bh);
}
} while ((bh = bh->b_this_page) != head); } while ((bh = bh->b_this_page) != head);
BUG_ON(PageWriteback(page)); BUG_ON(PageWriteback(page));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment