Fix a race which was identified by Daniel McNeil 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. --- 25-akpm/fs/buffer.c | 29 +++++++++++++++-------------- 1 files changed, 15 insertions(+), 14 deletions(-) diff -puN fs/buffer.c~O_DIRECT-ll_rw_block-vs-block_write_full_page-fix fs/buffer.c --- 25/fs/buffer.c~O_DIRECT-ll_rw_block-vs-block_write_full_page-fix 2004-04-03 03:00:09.640765936 -0800 +++ 25-akpm/fs/buffer.c 2004-04-03 03:00:09.645765176 -0800 @@ -1806,23 +1806,24 @@ static int __block_write_full_page(struc do { get_bh(bh); - if (buffer_mapped(bh) && buffer_dirty(bh)) { - if (wbc->sync_mode != WB_SYNC_NONE) { - lock_buffer(bh); - } else { - if (test_set_buffer_locked(bh)) { + if (!buffer_mapped(bh)) + continue; + if (wbc->sync_mode != WB_SYNC_NONE) { + lock_buffer(bh); + } else { + if (test_set_buffer_locked(bh)) { + if (buffer_dirty(bh)) __set_page_dirty_nobuffers(page); - continue; - } - } - if (test_clear_buffer_dirty(bh)) { - if (!buffer_uptodate(bh)) - buffer_error(); - mark_buffer_async_write(bh); - } else { - unlock_buffer(bh); + continue; } } + 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); BUG_ON(PageWriteback(page)); _