From: Andrew Morton Date: Tue, 17 May 2005 04:53:49 +0000 (-0700) Subject: [PATCH] block_read_full_page() get_block() error handling fix X-Git-Tag: v2.6.12-rc5~125 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c64610ba585fabb36be78782868277f3d9741a2e;p=linux-2.6 [PATCH] block_read_full_page() get_block() error handling fix 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 For reporting the bug and identifying its source. Signed-off-by: Qu Fuping Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- diff --git a/fs/buffer.c b/fs/buffer.c index 6f88dcc6d0..7e9e409fea 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2094,9 +2094,12 @@ 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)) { @@ -2104,7 +2107,8 @@ int block_read_full_page(struct page *page, get_block_t *get_block) 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; } /*