]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] BUG on error handlings in Ext3 under I/O failure condition
authorHisashi Hifumi <hifumi.hisashi@lab.ntt.co.jp>
Tue, 11 Jan 2005 11:30:37 +0000 (03:30 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 11 Jan 2005 11:30:37 +0000 (03:30 -0800)
I found bugs on error handlings in the functions arround the ext3 file
system, which cause inadequate completions of synchronous write I/O
operations when disk I/O failures occur.  Both 2.4 and 2.6 have this
problem.

I carried out following experiment:

1.  Mount a ext3 file system on a SCSI disk with ordered mode.
2.  Open a file on the file system with O_SYNC|O_RDWR|O_TRUNC|O_CREAT flag.
3.  Write 512 bytes data to the file by calling write() every 5 seconds, and
     examine return values from the syscall.
     from write().
4.  Disconnect the SCSI cable,  and examine messages from the kernel.

After the SCSI cable is disconnected, write() must fail.  But the result
was different: write() succeeded for a while even though messages of the
kernel notified SCSI I/O error.

By applying following modifications, the above problem was solved.

Signed-off-by: Hisashi Hifumi <hifumi.hisashi@lab.ntt.co.jp>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/buffer.c
fs/fs-writeback.c
fs/jbd/commit.c
include/linux/fs.h

index 7a31850892cbb40cb703e9dbf7a25d456d7b875f..341ba4c58a1825106e2427e571a29b2de05ccdb6 100644 (file)
@@ -311,10 +311,10 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync)
 {
        struct inode * inode = dentry->d_inode;
        struct super_block * sb;
-       int ret;
+       int ret, err;
 
        /* sync the inode to buffers */
-       write_inode_now(inode, 0);
+       ret = write_inode_now(inode, 0);
 
        /* sync the superblock to buffers */
        sb = inode->i_sb;
@@ -324,7 +324,9 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync)
        unlock_super(sb);
 
        /* .. finally sync the buffers to disk */
-       ret = sync_blockdev(sb->s_bdev);
+       err = sync_blockdev(sb->s_bdev);
+       if (!ret)
+               ret = err;
        return ret;
 }
 
index 26f234a0da93d1fcbc1c58767b040ffb6937f8a0..6d0e70efd39905328a9663b5b376bf781a6e7daa 100644 (file)
@@ -558,22 +558,24 @@ void sync_inodes(int wait)
  *     dirty. This is primarily needed by knfsd.
  */
  
-void write_inode_now(struct inode *inode, int sync)
+int write_inode_now(struct inode *inode, int sync)
 {
+       int ret;
        struct writeback_control wbc = {
                .nr_to_write = LONG_MAX,
                .sync_mode = WB_SYNC_ALL,
        };
 
        if (inode->i_mapping->backing_dev_info->memory_backed)
-               return;
+               return 0;
 
        might_sleep();
        spin_lock(&inode_lock);
-       __writeback_single_inode(inode, &wbc);
+       ret = __writeback_single_inode(inode, &wbc);
        spin_unlock(&inode_lock);
        if (sync)
                wait_on_inode(inode);
+       return ret;
 }
 EXPORT_SYMBOL(write_inode_now);
 
@@ -642,8 +644,11 @@ int generic_osync_inode(struct inode *inode, struct address_space *mapping, int
                need_write_inode_now = 1;
        spin_unlock(&inode_lock);
 
-       if (need_write_inode_now)
-               write_inode_now(inode, 1);
+       if (need_write_inode_now) {
+               err2 = write_inode_now(inode, 1);
+               if (!err)
+                       err = err2;
+       }
        else
                wait_on_inode(inode);
 
index c5c9983e3a6069a7b49569323362d3a27b0d8659..aa5f22435d0c27b2882ad1fc5c8259980160cf30 100644 (file)
@@ -337,6 +337,9 @@ write_out_data:
        }
        spin_unlock(&journal->j_list_lock);
 
+       if (err)
+               __journal_abort_hard(journal);
+
        journal_write_revoke_records(journal, commit_transaction);
 
        jbd_debug(3, "JBD: commit phase 2\n");
index aa7abb112dbc685679b3ecbcd8adac3e5ca3ca6f..4d26f55c129962403ca7358740dd6adf3b7004be 100644 (file)
@@ -1344,7 +1344,7 @@ static inline void invalidate_remote_inode(struct inode *inode)
                invalidate_inode_pages(inode->i_mapping);
 }
 extern int invalidate_inode_pages2(struct address_space *mapping);
-extern void write_inode_now(struct inode *, int);
+extern int write_inode_now(struct inode *, int);
 extern int filemap_fdatawrite(struct address_space *);
 extern int filemap_flush(struct address_space *);
 extern int filemap_fdatawait(struct address_space *);