diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-18 04:48:07 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-18 04:48:07 +0100 |
commit | 7eb9b2f56c9812d03ac63031869bcc42151067b1 (patch) | |
tree | c9e1ae248e586b9ca3ac0420c88c1b171dca47af | |
parent | Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/... (diff) | |
parent | [XFS] Fix a race in xfs_submit_ioend() where we can be completing I/O for (diff) | |
download | linux-7eb9b2f56c9812d03ac63031869bcc42151067b1.tar.xz linux-7eb9b2f56c9812d03ac63031869bcc42151067b1.zip |
Merge git://oss.sgi.com:8090/oss/git/xfs-2.6
-rw-r--r-- | fs/xfs/linux-2.6/xfs_aops.c | 29 |
1 files changed, 26 insertions, 3 deletions
diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index d1db8c17a74e..120626789406 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -336,24 +336,47 @@ static inline int bio_add_buffer(struct bio *bio, struct buffer_head *bh) } /* - * Submit all of the bios for all of the ioends we have saved up, - * covering the initial writepage page and also any probed pages. + * Submit all of the bios for all of the ioends we have saved up, covering the + * initial writepage page and also any probed pages. + * + * Because we may have multiple ioends spanning a page, we need to start + * writeback on all the buffers before we submit them for I/O. If we mark the + * buffers as we got, then we can end up with a page that only has buffers + * marked async write and I/O complete on can occur before we mark the other + * buffers async write. + * + * The end result of this is that we trip a bug in end_page_writeback() because + * we call it twice for the one page as the code in end_buffer_async_write() + * assumes that all buffers on the page are started at the same time. + * + * The fix is two passes across the ioend list - one to start writeback on the + * bufferheads, and then the second one submit them for I/O. */ STATIC void xfs_submit_ioend( xfs_ioend_t *ioend) { + xfs_ioend_t *head = ioend; xfs_ioend_t *next; struct buffer_head *bh; struct bio *bio; sector_t lastblock = 0; + /* Pass 1 - start writeback */ + do { + next = ioend->io_list; + for (bh = ioend->io_buffer_head; bh; bh = bh->b_private) { + xfs_start_buffer_writeback(bh); + } + } while ((ioend = next) != NULL); + + /* Pass 2 - submit I/O */ + ioend = head; do { next = ioend->io_list; bio = NULL; for (bh = ioend->io_buffer_head; bh; bh = bh->b_private) { - xfs_start_buffer_writeback(bh); if (!bio) { retry: |