Skip to content

Commit

Permalink
[PATCH] block_read_full_page() get_block() error handling fix
Browse files Browse the repository at this point in the history
If block_read_full_page() detects an error when running get_block() it will
run SetPageError(), then it will zero out the block in pagecache and will mark
the buffer_head uptodate.

So at the end of readahead we end up with a non-uptodate pagecache page which
is marked PageError.  But it has uptodate buffers.

The pagefault code will run ClearPageError, will launch readpage a second time
and block_read_full_page() will notice the uptodate buffers and will mark the
page uptodate as well.  We end up with an uptodate, !PageError page full of
zeros and the error is lost.

(It seems a little odd that filemap_nopage() runs ClearPageError().  I guess
all of this adds up to meaning that for each attempted access to the page, the
pagefault handler will retry the I/O.  Which is good and bad.  If the app is
ignoring SIGBUS for some reason we could get a lot of back-to-back I/O
errors.)

Fix it by not marking the pagecache buffer_head as uptodate if the attempt to
map that buffer to a disk block failed.

Credit-to: Qu Fuping <fs@ercist.iscas.ac.cn>

  For reporting the bug and identifying its source.

Signed-off-by: Qu Fuping <fs@ercist.iscas.ac.cn>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
  • Loading branch information
Andrew Morton authored and Linus Torvalds committed May 17, 2005
1 parent 3c0547b commit c64610b
Showing 1 changed file with 6 additions and 2 deletions.
8 changes: 6 additions & 2 deletions fs/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -2094,17 +2094,21 @@ int block_read_full_page(struct page *page, get_block_t *get_block)
continue;

if (!buffer_mapped(bh)) {
int err = 0;

fully_mapped = 0;
if (iblock < lblock) {
if (get_block(inode, iblock, bh, 0))
err = get_block(inode, iblock, bh, 0);
if (err)
SetPageError(page);
}
if (!buffer_mapped(bh)) {
void *kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr + i * blocksize, 0, blocksize);
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
set_buffer_uptodate(bh);
if (!err)
set_buffer_uptodate(bh);
continue;
}
/*
Expand Down

0 comments on commit c64610b

Please sign in to comment.