diff options
author | David Chinner <dgc@sgi.com> | 2006-01-17 21:38:12 -0500 |
---|---|---|
committer | Nathan Scott <nathans@sgi.com> | 2006-01-17 21:38:12 -0500 |
commit | d88992f660936049f5f38d74ea5a86b5c1491a48 (patch) | |
tree | 3c3ac2e25d33e4bd59193b9d4ecf7a1bee3e4d0e /fs/xfs/linux-2.6 | |
parent | 2664b25051f7ab96b22b199aa2f5ef6a949a4296 (diff) |
[XFS] Fix a race in xfs_submit_ioend() where we can be completing I/O for
a page while we are still submitting other buffers on the same page for
I/O.
SGI-PV: 948197
SGI-Modid: xfs-linux-melb:xfs-kern:25004a
Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Nathan Scott <nathans@sgi.com>
Diffstat (limited to 'fs/xfs/linux-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) | |||
336 | } | 336 | } |
337 | 337 | ||
338 | /* | 338 | /* |
339 | * Submit all of the bios for all of the ioends we have saved up, | 339 | * Submit all of the bios for all of the ioends we have saved up, covering the |
340 | * covering the initial writepage page and also any probed pages. | 340 | * initial writepage page and also any probed pages. |
341 | * | ||
342 | * Because we may have multiple ioends spanning a page, we need to start | ||
343 | * writeback on all the buffers before we submit them for I/O. If we mark the | ||
344 | * buffers as we got, then we can end up with a page that only has buffers | ||
345 | * marked async write and I/O complete on can occur before we mark the other | ||
346 | * buffers async write. | ||
347 | * | ||
348 | * The end result of this is that we trip a bug in end_page_writeback() because | ||
349 | * we call it twice for the one page as the code in end_buffer_async_write() | ||
350 | * assumes that all buffers on the page are started at the same time. | ||
351 | * | ||
352 | * The fix is two passes across the ioend list - one to start writeback on the | ||
353 | * bufferheads, and then the second one submit them for I/O. | ||
341 | */ | 354 | */ |
342 | STATIC void | 355 | STATIC void |
343 | xfs_submit_ioend( | 356 | xfs_submit_ioend( |
344 | xfs_ioend_t *ioend) | 357 | xfs_ioend_t *ioend) |
345 | { | 358 | { |
359 | xfs_ioend_t *head = ioend; | ||
346 | xfs_ioend_t *next; | 360 | xfs_ioend_t *next; |
347 | struct buffer_head *bh; | 361 | struct buffer_head *bh; |
348 | struct bio *bio; | 362 | struct bio *bio; |
349 | sector_t lastblock = 0; | 363 | sector_t lastblock = 0; |
350 | 364 | ||
365 | /* Pass 1 - start writeback */ | ||
366 | do { | ||
367 | next = ioend->io_list; | ||
368 | for (bh = ioend->io_buffer_head; bh; bh = bh->b_private) { | ||
369 | xfs_start_buffer_writeback(bh); | ||
370 | } | ||
371 | } while ((ioend = next) != NULL); | ||
372 | |||
373 | /* Pass 2 - submit I/O */ | ||
374 | ioend = head; | ||
351 | do { | 375 | do { |
352 | next = ioend->io_list; | 376 | next = ioend->io_list; |
353 | bio = NULL; | 377 | bio = NULL; |
354 | 378 | ||
355 | for (bh = ioend->io_buffer_head; bh; bh = bh->b_private) { | 379 | for (bh = ioend->io_buffer_head; bh; bh = bh->b_private) { |
356 | xfs_start_buffer_writeback(bh); | ||
357 | 380 | ||
358 | if (!bio) { | 381 | if (!bio) { |
359 | retry: | 382 | retry: |