Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHisashi Hifumi <hifumi.hisashi@lab.ntt.co.jp>2005-01-11 03:30:37 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-01-11 03:30:37 -0800
commit66d272434a1b009b94bab07bae98dec39e0a9706 (patch)
tree43761b410c49c5351cd49204c5a117c17de65d79
parentb0c7ad6670d714b474f87f7ea6fd9e0396a0f0d5 (diff)
[PATCH] BUG on error handlings in Ext3 under I/O failure condition
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>
-rw-r--r--fs/buffer.c8
-rw-r--r--fs/fs-writeback.c15
-rw-r--r--fs/jbd/commit.c3
-rw-r--r--include/linux/fs.h2
4 files changed, 19 insertions, 9 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index 7a31850892cb..341ba4c58a18 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -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;
}
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 26f234a0da93..6d0e70efd399 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -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);
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c
index c5c9983e3a60..aa5f22435d0c 100644
--- a/fs/jbd/commit.c
+++ b/fs/jbd/commit.c
@@ -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");
diff --git a/include/linux/fs.h b/include/linux/fs.h
index aa7abb112dbc..4d26f55c1299 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -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 *);