Patch from Oliver Xymoron buffer.c | 40 ++++++++++++++++++++++++++++++---------- inode.c | 1 + mpage.c | 13 +++++++++++++ ntfs/compress.c | 2 +- open.c | 25 +++++++++++++++++++++---- linux/buffer_head.h | 10 +++++++--- linux/fs.h | 1 + ksyms.c | 3 ++- filemap.c | 8 ++++++++ vmscan.c | 3 +++ 10 files changed, 87 insertions(+), 19 deletions(-) diff -puN fs/buffer.c~handle-async-write-errors fs/buffer.c --- 25/fs/buffer.c~handle-async-write-errors 2003-02-13 18:39:21.000000000 -0800 +++ 25-akpm/fs/buffer.c 2003-02-13 18:39:21.000000000 -0800 @@ -164,15 +164,27 @@ static void buffer_io_error(struct buffe * Default synchronous end-of-IO handler.. Just mark it up-to-date and * unlock the buffer. This is what ll_rw_block uses too. */ -void end_buffer_io_sync(struct buffer_head *bh, int uptodate) +void end_buffer_read_sync(struct buffer_head *bh, int uptodate) { if (uptodate) { set_buffer_uptodate(bh); } else { - /* - * This happens, due to failed READA attempts. - * buffer_io_error(bh); - */ + /* This happens, due to failed READA attempts. */ + clear_buffer_uptodate(bh); + } + unlock_buffer(bh); + put_bh(bh); +} + +void end_buffer_write_sync(struct buffer_head *bh, int uptodate) +{ + if (uptodate) { + set_buffer_uptodate(bh); + } else { + buffer_io_error(bh); + printk(KERN_WARNING "lost page write due to I/O error on %s\n", + bdevname(bh->b_bdev)); + set_buffer_write_io_error(bh); clear_buffer_uptodate(bh); } unlock_buffer(bh); @@ -549,6 +561,9 @@ static void end_buffer_async_write(struc set_buffer_uptodate(bh); } else { buffer_io_error(bh); + printk(KERN_WARNING "lost page write due to I/O error on %s\n", + bdevname(bh->b_bdev)); + page->mapping->error = -EIO; clear_buffer_uptodate(bh); SetPageError(page); } @@ -1200,7 +1215,7 @@ static struct buffer_head *__bread_slow( if (buffer_dirty(bh)) buffer_error(); get_bh(bh); - bh->b_end_io = end_buffer_io_sync; + bh->b_end_io = end_buffer_read_sync; submit_bh(READ, bh); wait_on_buffer(bh); if (buffer_uptodate(bh)) @@ -2548,8 +2563,10 @@ int submit_bh(int rw, struct buffer_head buffer_error(); if (rw == READ && buffer_dirty(bh)) buffer_error(); - - set_buffer_req(bh); + + /* Only clear out a write error when rewriting */ + if (test_set_buffer_req(bh) && rw == WRITE) + clear_buffer_write_io_error(bh); /* * from here on down, it's all bio -- do the initial mapping, @@ -2609,13 +2626,14 @@ void ll_rw_block(int rw, int nr, struct continue; get_bh(bh); - bh->b_end_io = end_buffer_io_sync; if (rw == WRITE) { + bh->b_end_io = end_buffer_write_sync; if (test_clear_buffer_dirty(bh)) { submit_bh(WRITE, bh); continue; } } else { + bh->b_end_io = end_buffer_read_sync; if (!buffer_uptodate(bh)) { submit_bh(rw, bh); continue; @@ -2636,7 +2654,7 @@ void sync_dirty_buffer(struct buffer_hea lock_buffer(bh); if (test_clear_buffer_dirty(bh)) { get_bh(bh); - bh->b_end_io = end_buffer_io_sync; + bh->b_end_io = end_buffer_write_sync; submit_bh(WRITE, bh); wait_on_buffer(bh); } else { @@ -2695,6 +2713,8 @@ drop_buffers(struct page *page, struct b bh = head; do { check_ttfb_buffer(page, bh); + if (buffer_write_io_error(bh)) + page->mapping->error = -EIO; if (buffer_busy(bh)) goto failed; if (!buffer_uptodate(bh) && !buffer_req(bh)) diff -puN fs/inode.c~handle-async-write-errors fs/inode.c --- 25/fs/inode.c~handle-async-write-errors 2003-02-13 18:39:21.000000000 -0800 +++ 25-akpm/fs/inode.c 2003-02-13 18:39:21.000000000 -0800 @@ -134,6 +134,7 @@ static struct inode *alloc_inode(struct mapping->dirtied_when = 0; mapping->assoc_mapping = NULL; mapping->backing_dev_info = &default_backing_dev_info; + mapping->error = 0; if (sb->s_bdev) inode->i_data.backing_dev_info = sb->s_bdev->bd_inode->i_mapping->backing_dev_info; memset(&inode->u, 0, sizeof(inode->u)); diff -puN fs/mpage.c~handle-async-write-errors fs/mpage.c --- 25/fs/mpage.c~handle-async-write-errors 2003-02-13 18:39:21.000000000 -0800 +++ 25-akpm/fs/mpage.c 2003-02-13 18:39:21.000000000 -0800 @@ -562,6 +562,8 @@ confused: if (bio) bio = mpage_bio_submit(WRITE, bio); *ret = page->mapping->a_ops->writepage(page, wbc); + if (*ret < 0) + page->mapping->error = *ret; out: return bio; } @@ -665,6 +667,17 @@ mpage_writepages(struct address_space *m test_clear_page_dirty(page)) { if (writepage) { ret = (*writepage)(page, wbc); + if (ret < 0) { + /* + * lock the page to keep truncate away + * then check that it is still on the + * mapping. + */ + lock_page(page); + if (page->mapping == mapping) + page->mapping->error = ret; + unlock_page(page); + } } else { bio = mpage_writepage(bio, page, get_block, &last_block_in_bio, &ret, wbc); diff -puN fs/ntfs/compress.c~handle-async-write-errors fs/ntfs/compress.c --- 25/fs/ntfs/compress.c~handle-async-write-errors 2003-02-13 18:39:21.000000000 -0800 +++ 25-akpm/fs/ntfs/compress.c 2003-02-13 18:39:21.000000000 -0800 @@ -598,7 +598,7 @@ lock_retry_remap: continue; } atomic_inc(&tbh->b_count); - tbh->b_end_io = end_buffer_io_sync; + tbh->b_end_io = end_buffer_read_sync; submit_bh(READ, tbh); } diff -puN fs/open.c~handle-async-write-errors fs/open.c --- 25/fs/open.c~handle-async-write-errors 2003-02-13 18:39:21.000000000 -0800 +++ 25-akpm/fs/open.c 2003-02-13 18:39:21.000000000 -0800 @@ -843,21 +843,38 @@ asmlinkage long sys_creat(const char * p */ int filp_close(struct file *filp, fl_owner_t id) { - int retval; + struct address_space *mapping = filp->f_dentry->d_inode->i_mapping; + int retval = 0, err; + + /* Report and clear outstanding errors */ + err = filp->f_error; + if (err) { + filp->f_error = 0; + retval = err; + } + + err = mapping->error; + if (err && !retval) { + mapping->error = 0; + retval = err; + } if (!file_count(filp)) { printk(KERN_ERR "VFS: Close: file count is 0\n"); - return 0; + return retval; } - retval = 0; + if (filp->f_op && filp->f_op->flush) { lock_kernel(); - retval = filp->f_op->flush(filp); + err = filp->f_op->flush(filp); unlock_kernel(); + if (err && !retval) + retval = err; } dnotify_flush(filp, id); locks_remove_posix(filp, id); fput(filp); + return retval; } diff -puN include/linux/buffer_head.h~handle-async-write-errors include/linux/buffer_head.h --- 25/include/linux/buffer_head.h~handle-async-write-errors 2003-02-13 18:39:21.000000000 -0800 +++ 25-akpm/include/linux/buffer_head.h 2003-02-13 18:39:21.000000000 -0800 @@ -24,8 +24,9 @@ enum bh_state_bits { BH_Async_Read, /* Is under end_buffer_async_read I/O */ BH_Async_Write, /* Is under end_buffer_async_write I/O */ BH_Delay, /* Buffer is not yet allocated on disk */ - BH_Boundary, /* Block is followed by a discontiguity */ + BH_Write_EIO, /* I/O error on write */ + BH_PrivateStart,/* not a state bit, but the first bit available * for private allocation by other entities */ @@ -103,12 +104,14 @@ TAS_BUFFER_FNS(Dirty, dirty) BUFFER_FNS(Lock, locked) TAS_BUFFER_FNS(Lock, locked) BUFFER_FNS(Req, req) +TAS_BUFFER_FNS(Req, req) BUFFER_FNS(Mapped, mapped) BUFFER_FNS(New, new) BUFFER_FNS(Async_Read, async_read) BUFFER_FNS(Async_Write, async_write) -BUFFER_FNS(Delay, delay); +BUFFER_FNS(Delay, delay) BUFFER_FNS(Boundary, boundary) +BUFFER_FNS(Write_EIO,write_io_error) #define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK) #define touch_buffer(bh) mark_page_accessed(bh->b_page) @@ -136,7 +139,8 @@ void set_bh_page(struct buffer_head *bh, int try_to_free_buffers(struct page *); void create_empty_buffers(struct page *, unsigned long, unsigned long b_state); -void end_buffer_io_sync(struct buffer_head *bh, int uptodate); +void end_buffer_read_sync(struct buffer_head *bh, int uptodate); +void end_buffer_write_sync(struct buffer_head *bh, int uptodate); /* Things to do with buffers at mapping->private_list */ void buffer_insert_list(spinlock_t *lock, diff -puN include/linux/fs.h~handle-async-write-errors include/linux/fs.h --- 25/include/linux/fs.h~handle-async-write-errors 2003-02-13 18:39:21.000000000 -0800 +++ 25-akpm/include/linux/fs.h 2003-02-13 18:39:21.000000000 -0800 @@ -326,6 +326,7 @@ struct address_space { spinlock_t private_lock; /* for use by the address_space */ struct list_head private_list; /* ditto */ struct address_space *assoc_mapping; /* ditto */ + int error; /* write error for fsync */ }; struct char_device { diff -puN kernel/ksyms.c~handle-async-write-errors kernel/ksyms.c --- 25/kernel/ksyms.c~handle-async-write-errors 2003-02-13 18:39:21.000000000 -0800 +++ 25-akpm/kernel/ksyms.c 2003-02-13 18:39:21.000000000 -0800 @@ -170,7 +170,8 @@ EXPORT_SYMBOL(d_splice_alias); EXPORT_SYMBOL(d_lookup); EXPORT_SYMBOL(d_path); EXPORT_SYMBOL(mark_buffer_dirty); -EXPORT_SYMBOL(end_buffer_io_sync); +EXPORT_SYMBOL(end_buffer_read_sync); +EXPORT_SYMBOL(end_buffer_write_sync); EXPORT_SYMBOL(__mark_inode_dirty); EXPORT_SYMBOL(get_empty_filp); EXPORT_SYMBOL(init_private_file); diff -puN mm/filemap.c~handle-async-write-errors mm/filemap.c --- 25/mm/filemap.c~handle-async-write-errors 2003-02-13 18:39:21.000000000 -0800 +++ 25-akpm/mm/filemap.c 2003-02-13 18:39:21.000000000 -0800 @@ -185,6 +185,14 @@ restart: write_lock(&mapping->page_lock); } write_unlock(&mapping->page_lock); + + /* Check for outstanding write errors */ + if (mapping->error) + { + ret = mapping->error; + mapping->error = 0; + } + return ret; } diff -puN mm/vmscan.c~handle-async-write-errors mm/vmscan.c --- 25/mm/vmscan.c~handle-async-write-errors 2003-02-13 18:39:21.000000000 -0800 +++ 25-akpm/mm/vmscan.c 2003-02-13 18:39:21.000000000 -0800 @@ -342,6 +342,9 @@ shrink_list(struct list_head *page_list, SetPageReclaim(page); res = mapping->a_ops->writepage(page, &wbc); + if (res < 0) { + mapping->error = res; + } if (res == WRITEPAGE_ACTIVATE) { ClearPageReclaim(page); goto activate_locked; _