diff -urNp x-ref/drivers/block/ll_rw_blk.c x/drivers/block/ll_rw_blk.c --- x-ref/drivers/block/ll_rw_blk.c 2003-03-01 05:05:31.000000000 +0100 +++ x/drivers/block/ll_rw_blk.c 2003-03-01 05:06:18.000000000 +0100 @@ -1419,6 +1419,8 @@ void __submit_bh(int rw, struct buffer_h set_bit(BH_Req, &bh->b_state); set_bit(BH_Launder, &bh->b_state); + if (rw == WRITE) + clear_bit(BH_Write_EIO, &bh->b_state); /* * First step, 'identity mapping' - RAID or LVM might @@ -1515,10 +1517,10 @@ void ll_rw_block(int rw, int nr, struct /* We have the buffer lock */ atomic_inc(&bh->b_count); - bh->b_end_io = end_buffer_io_sync; switch(rw) { case WRITE: + bh->b_end_io = end_buffer_write_sync; if (!atomic_set_buffer_clean(bh)) /* Hmmph! Nothing to write */ goto end_io; @@ -1527,6 +1529,7 @@ void ll_rw_block(int rw, int nr, struct case READA: case READ: + bh->b_end_io = end_buffer_read_sync; if (buffer_uptodate(bh)) /* Hmmph! Already have it */ goto end_io; diff -urNp x-ref/fs/buffer.c x/fs/buffer.c --- x-ref/fs/buffer.c 2003-03-01 05:05:31.000000000 +0100 +++ x/fs/buffer.c 2003-03-01 05:06:18.000000000 +0100 @@ -187,13 +187,28 @@ void __wait_on_buffer(struct buffer_head * 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) { mark_buffer_uptodate(bh, uptodate); unlock_buffer(bh); put_bh(bh); } +void end_buffer_write_sync(struct buffer_head *bh, int uptodate) +{ + mark_buffer_uptodate(bh, uptodate); + + /* Flag failed writes for later fsync/msync */ + if (unlikely(!uptodate)) { + printk(KERN_WARNING "lost page write due to I/O error on %s\n", + kdevname(bh->b_dev)); + set_bit(BH_Write_EIO, &bh->b_state); + } + + unlock_buffer(bh); + put_bh(bh); +} + /* * The buffers have been marked clean and locked. Just submit the dang * things.. @@ -202,7 +217,7 @@ static void write_locked_buffers(struct { do { struct buffer_head * bh = *array++; - bh->b_end_io = end_buffer_io_sync; + bh->b_end_io = end_buffer_write_sync; submit_bh(WRITE, bh); } while (--count); } @@ -795,8 +810,12 @@ static void end_buffer_io_async(struct b /* This is a temporary buffer used for page I/O. */ page = bh->b_page; - if (!uptodate) + if (unlikely(!uptodate)) { + printk(KERN_WARNING "lost async page write due to I/O error on %s\n", + kdevname(bh->b_dev)); + page->mapping->error = -EIO; SetPageError(page); + } /* * Be _very_ careful from here on. Bad things can happen if @@ -2711,7 +2730,7 @@ static int sync_page_buffers(struct buff __mark_buffer_clean(bh); get_bh(bh); - bh->b_end_io = end_buffer_io_sync; + bh->b_end_io = end_buffer_write_sync; submit_bh(WRITE, bh); tryagain = 0; } while ((bh = bh->b_this_page) != head); @@ -2746,6 +2765,8 @@ cleaned_buffers_try_again: write_lock(&hash_table_lock); tmp = bh; do { + if (unlikely(buffer_write_io_error(tmp))) + page->mapping->error = -EIO; if (buffer_busy(tmp)) goto busy_buffer_page; tmp = tmp->b_this_page; diff -urNp x-ref/fs/inode.c x/fs/inode.c --- x-ref/fs/inode.c 2003-03-01 05:05:26.000000000 +0100 +++ x/fs/inode.c 2003-03-01 05:06:18.000000000 +0100 @@ -117,6 +117,7 @@ static struct inode *alloc_inode(struct mapping->a_ops = &empty_aops; mapping->host = inode; mapping->gfp_mask = GFP_HIGHUSER; + mapping->error = 0; inode->i_mapping = mapping; } return inode; diff -urNp x-ref/fs/open.c x/fs/open.c --- x-ref/fs/open.c 2003-03-01 05:05:31.000000000 +0100 +++ x/fs/open.c 2003-03-01 05:06:18.000000000 +0100 @@ -824,21 +824,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; - if (!file_count(filp)) { + /* Report and clear outstanding errors */ + err = filp->f_error; + if (unlikely(err)) { + filp->f_error = 0; + retval = err; + } + + err = mapping->error; + if (unlikely(err && !retval)) { + mapping->error = 0; + retval = err; + } + + if (unlikely(!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 (unlikely(err && !retval)) + retval = err; } dnotify_flush(filp, id); locks_remove_posix(filp, id); fput(filp); + return retval; } diff -urNp x-ref/include/linux/fs.h x/include/linux/fs.h --- x-ref/include/linux/fs.h 2003-03-01 05:05:31.000000000 +0100 +++ x/include/linux/fs.h 2003-03-01 05:06:18.000000000 +0100 @@ -224,6 +224,7 @@ enum bh_state_bits { BH_Wait_IO, /* 1 if we should write out this buffer */ BH_Launder, /* 1 if we can throttle on this buffer */ BH_Attached, /* 1 if b_inode_buffers is linked into a list */ + BH_Write_EIO, /* 1 if encountered I/O error on write */ BH_JBD, /* 1 if it has an attached journal_head */ BH_Delay, /* 1 if the buffer is delayed allocate */ @@ -290,6 +291,7 @@ void init_buffer(struct buffer_head *, b #define buffer_async(bh) __buffer_state(bh,Async) #define buffer_launder(bh) __buffer_state(bh,Launder) #define buffer_delay(bh) __buffer_state(bh,Delay) +#define buffer_write_io_error(bh) __buffer_state(bh,Write_EIO) #define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK) @@ -420,6 +422,7 @@ struct address_space { struct vm_area_struct *i_mmap_shared; /* list of shared mappings */ spinlock_t i_shared_lock; /* and spinlock protecting it */ int gfp_mask; /* how to allocate the pages */ + int error; /* write error for fsync */ }; struct char_device { @@ -1235,7 +1238,8 @@ extern int fs_may_remount_ro(struct supe extern int FASTCALL(try_to_free_buffers(struct page *, unsigned int)); extern void refile_buffer(struct buffer_head * buf); extern void create_empty_buffers(struct page *, kdev_t, unsigned long); -extern void end_buffer_io_sync(struct buffer_head *bh, int uptodate); +extern void end_buffer_read_sync(struct buffer_head *bh, int uptodate); +extern void end_buffer_write_sync(struct buffer_head *bh, int uptodate); /* reiserfs_writepage needs this */ extern void set_buffer_async_io(struct buffer_head *bh) ; diff -urNp x-ref/kernel/ksyms.c x/kernel/ksyms.c --- x-ref/kernel/ksyms.c 2003-03-01 05:05:29.000000000 +0100 +++ x/kernel/ksyms.c 2003-03-01 05:07:22.000000000 +0100 @@ -186,6 +186,8 @@ EXPORT_SYMBOL(d_alloc); EXPORT_SYMBOL(d_lookup); EXPORT_SYMBOL(__d_path); EXPORT_SYMBOL(mark_buffer_dirty); +EXPORT_SYMBOL(end_buffer_read_sync); +EXPORT_SYMBOL(end_buffer_write_sync); EXPORT_SYMBOL(set_buffer_async_io); /* for reiserfs_writepage */ EXPORT_SYMBOL(__mark_buffer_dirty); EXPORT_SYMBOL(__mark_inode_dirty); diff -urNp x-ref/mm/filemap.c x/mm/filemap.c --- x-ref/mm/filemap.c 2003-03-01 05:05:31.000000000 +0100 +++ x/mm/filemap.c 2003-03-01 05:06:18.000000000 +0100 @@ -619,6 +619,13 @@ int filemap_fdatawait(struct address_spa spin_lock(&pagecache_lock); } spin_unlock(&pagecache_lock); + + /* Check for outstanding write errors */ + if (unlikely(mapping->error)) { + ret = mapping->error; + mapping->error = 0; + } + return ret; } diff -urNp x-ref/mm/vmscan.c x/mm/vmscan.c --- x-ref/mm/vmscan.c 2003-03-01 05:05:26.000000000 +0100 +++ x/mm/vmscan.c 2003-03-01 05:06:18.000000000 +0100 @@ -495,15 +495,21 @@ static int shrink_cache(int nr_pages, zo * so the direct writes to the page cannot get lost. */ int (*writepage)(struct page *); + struct address_space *mapping = page->mapping; - writepage = page->mapping->a_ops->writepage; + writepage = mapping->a_ops->writepage; if ((gfp_mask & __GFP_FS) && writepage) { + int err; + ClearPageDirty(page); SetPageLaunder(page); page_cache_get(page); spin_unlock(&pagemap_lru_lock); - writepage(page); + err = writepage(page); + if (unlikely(err)) + mapping->error = err; + page_cache_release(page); spin_lock(&pagemap_lru_lock);