[PATCH v5 14/17] ext4: convert to errseq_t based error tracking
From: Jeff Layton <hidden>
Date: 2017-05-31 12:46:00
Also in:
linux-ext4, linux-fsdevel, lkml
Subsystem:
ext4 file system, filesystems (vfs and infrastructure), the rest · Maintainers:
"Theodore Ts'o", Alexander Viro, Christian Brauner, Linus Torvalds
Sample the block device inode's errseq_t when opening a file, so we can catch metadata writeback errors at fsync time. Change ext4_sync_file to check for data errors first, and then check the blockdev for metadata errors afterward. There are also several internal callers of filemap_write_and_wait_* that check the error code afterward. Convert them to the "_since" variants, using the file->f_wb_err value as the "since" value. This means passing file pointers to several functions instead of inode pointers. Note that because metadata writeback errors are only tracked on a per-device level, this does mean that we'll end up reporting an error on all open file descriptors when there is a metadata writeback failure. Signed-off-by: Jeff Layton <redacted> --- fs/ext4/dir.c | 8 ++++++-- fs/ext4/ext4.h | 8 ++++---- fs/ext4/extents.c | 24 ++++++++++++++---------- fs/ext4/file.c | 5 ++++- fs/ext4/fsync.c | 23 ++++++++++++++++++----- fs/ext4/inode.c | 19 ++++++++++++------- fs/ext4/ioctl.c | 9 +++++---- fs/ext4/super.c | 9 +++++---- 8 files changed, 68 insertions(+), 37 deletions(-)
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index e8b365000d73..6bbb19510f74 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c@@ -611,9 +611,13 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx) static int ext4_dir_open(struct inode * inode, struct file * filp) { + int ret = 0; + if (ext4_encrypted_inode(inode)) - return fscrypt_get_encryption_info(inode) ? -EACCES : 0; - return 0; + ret = fscrypt_get_encryption_info(inode) ? -EACCES : 0; + if (!ret) + filp->f_md_wb_err = filemap_sample_wb_err(inode->i_sb->s_bdev->bd_inode->i_mapping); + return ret; } static int ext4_release_dir(struct inode *inode, struct file *filp)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 8e8046104f4d..e3ab27db43d0 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h@@ -2468,12 +2468,12 @@ extern void ext4_clear_inode(struct inode *); extern int ext4_file_getattr(const struct path *, struct kstat *, u32, unsigned int); extern int ext4_sync_inode(handle_t *, struct inode *); extern void ext4_dirty_inode(struct inode *, int); -extern int ext4_change_inode_journal_flag(struct inode *, int); +extern int ext4_change_inode_journal_flag(struct file *, int); extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *); extern int ext4_inode_attach_jinode(struct inode *inode); extern int ext4_can_truncate(struct inode *inode); extern int ext4_truncate(struct inode *); -extern int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length); +extern int ext4_punch_hole(struct file *file, loff_t offset, loff_t length); extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks); extern void ext4_set_inode_flags(struct inode *); extern int ext4_alloc_da_blocks(struct inode *inode);
@@ -3143,8 +3143,8 @@ extern ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_ext_path *path); extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len); extern int ext4_ext_precache(struct inode *inode); -extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len); -extern int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len); +extern int ext4_collapse_range(struct file *file, loff_t offset, loff_t len); +extern int ext4_insert_range(struct file *file, loff_t offset, loff_t len); extern int ext4_swap_extents(handle_t *handle, struct inode *inode1, struct inode *inode2, ext4_lblk_t lblk1, ext4_lblk_t lblk2, ext4_lblk_t count,
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 2a97dff87b96..7e108fda9ae9 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c@@ -4934,17 +4934,17 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) return -EOPNOTSUPP; if (mode & FALLOC_FL_PUNCH_HOLE) - return ext4_punch_hole(inode, offset, len); + return ext4_punch_hole(file, offset, len); ret = ext4_convert_inline_data(inode); if (ret) return ret; if (mode & FALLOC_FL_COLLAPSE_RANGE) - return ext4_collapse_range(inode, offset, len); + return ext4_collapse_range(file, offset, len); if (mode & FALLOC_FL_INSERT_RANGE) - return ext4_insert_range(inode, offset, len); + return ext4_insert_range(file, offset, len); if (mode & FALLOC_FL_ZERO_RANGE) return ext4_zero_range(file, offset, len, mode);
@@ -5444,14 +5444,16 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle, * This implements the fallocate's collapse range functionality for ext4 * Returns: 0 and non-zero on error. */ -int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len) +int ext4_collapse_range(struct file *file, loff_t offset, loff_t len) { + struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; ext4_lblk_t punch_start, punch_stop; handle_t *handle; unsigned int credits; loff_t new_size, ioffset; int ret; + errseq_t since = READ_ONCE(file->f_wb_err); /* * We need to test this early because xfstests assumes that a
@@ -5515,7 +5517,7 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len) * Write tail of the last page before removed range since it will get * removed from the page cache below. */ - ret = filemap_write_and_wait_range(inode->i_mapping, ioffset, offset); + ret = filemap_write_and_wait_range_since(inode->i_mapping, ioffset, offset, since); if (ret) goto out_mmap; /*
@@ -5523,8 +5525,8 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len) * page cache below. We are also protected from pages becoming dirty * by i_mmap_sem. */ - ret = filemap_write_and_wait_range(inode->i_mapping, offset + len, - LLONG_MAX); + ret = filemap_write_and_wait_range_since(inode->i_mapping, offset + len, + LLONG_MAX, since); if (ret) goto out_mmap; truncate_pagecache(inode, ioffset);
@@ -5588,8 +5590,9 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len) * by len bytes. * Returns 0 on success, error otherwise. */ -int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len) +int ext4_insert_range(struct file *file, loff_t offset, loff_t len) { + struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; handle_t *handle; struct ext4_ext_path *path;
@@ -5598,6 +5601,7 @@ int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len) unsigned int credits, ee_len; int ret = 0, depth, split_flag = 0; loff_t ioffset; + errseq_t since = READ_ONCE(file->f_wb_err); /* * We need to test this early because xfstests assumes that an
@@ -5661,8 +5665,8 @@ int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len) */ ioffset = round_down(offset, PAGE_SIZE); /* Write out all dirty pages */ - ret = filemap_write_and_wait_range(inode->i_mapping, ioffset, - LLONG_MAX); + ret = filemap_write_and_wait_range_since(inode->i_mapping, ioffset, + LLONG_MAX, since); if (ret) goto out_mmap; truncate_pagecache(inode, ioffset);
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 831fd6beebf0..fe0d6e01c4b7 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c@@ -435,7 +435,10 @@ static int ext4_file_open(struct inode * inode, struct file * filp) if (ret < 0) return ret; } - return dquot_file_open(inode, filp); + ret = dquot_file_open(inode, filp); + if (!ret) + filp->f_md_wb_err = filemap_sample_wb_err(sb->s_bdev->bd_inode->i_mapping); + return ret; } /*
diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c
index 9d549608fd30..ba474de2dadb 100644
--- a/fs/ext4/fsync.c
+++ b/fs/ext4/fsync.c@@ -99,9 +99,12 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync) int ret = 0, err; tid_t commit_tid; bool needs_barrier = false; + errseq_t since = READ_ONCE(file->f_wb_err); - if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) - return -EIO; + if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) { + ret = -EIO; + goto out; + } J_ASSERT(ext4_journal_current_handle() == NULL);
@@ -124,9 +127,11 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync) goto out; } - ret = filemap_write_and_wait_range(inode->i_mapping, start, end); + ret = filemap_write_and_wait_range_since(inode->i_mapping, start, + end, since); if (ret) - return ret; + goto out; + /* * data=writeback,ordered: * The caller's filemap_fdatawrite()/wait will sync the data.
@@ -152,12 +157,20 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync) needs_barrier = true; ret = jbd2_complete_transaction(journal, commit_tid); if (needs_barrier) { - issue_flush: +issue_flush: err = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); if (!ret) ret = err; } out: + err = filemap_report_wb_err(file); + if (!ret) + ret = err; + + err = filemap_report_md_wb_err(file, + inode->i_sb->s_bdev->bd_inode->i_mapping); + if (!ret) + ret = err; trace_ext4_sync_file_exit(inode, ret); return ret; }
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 1bd0bfa547f6..df3b6f62dcbb 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c@@ -3705,6 +3705,7 @@ static ssize_t ext4_direct_IO_read(struct kiocb *iocb, struct iov_iter *iter) struct inode *inode = mapping->host; size_t count = iov_iter_count(iter); ssize_t ret; + errseq_t since = READ_ONCE(iocb->ki_filp->f_wb_err); /* * Shared inode_lock is enough for us - it protects against concurrent
@@ -3712,8 +3713,8 @@ static ssize_t ext4_direct_IO_read(struct kiocb *iocb, struct iov_iter *iter) * we are protected against page writeback as well. */ inode_lock_shared(inode); - ret = filemap_write_and_wait_range(mapping, iocb->ki_pos, - iocb->ki_pos + count); + ret = filemap_write_and_wait_range_since(mapping, iocb->ki_pos, + iocb->ki_pos + count, since); if (ret) goto out_unlock; ret = __blockdev_direct_IO(iocb, inode, inode->i_sb->s_bdev,
@@ -4085,8 +4086,9 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset, * Returns: 0 on success or negative on failure */ -int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length) +int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) { + struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; ext4_lblk_t first_block, stop_block; struct address_space *mapping = inode->i_mapping;
@@ -4094,6 +4096,7 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length) handle_t *handle; unsigned int credits; int ret = 0; + errseq_t since = READ_ONCE(file->f_wb_err); if (!S_ISREG(inode->i_mode)) return -EOPNOTSUPP;
@@ -4105,8 +4108,8 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length) * Then release them. */ if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) { - ret = filemap_write_and_wait_range(mapping, offset, - offset + length - 1); + ret = filemap_write_and_wait_range_since(mapping, offset, + offset + length - 1, since); if (ret) return ret; }
@@ -5771,12 +5774,14 @@ static int ext4_pin_inode(handle_t *handle, struct inode *inode) } #endif -int ext4_change_inode_journal_flag(struct inode *inode, int val) +int ext4_change_inode_journal_flag(struct file *file, int val) { + struct inode *inode = file_inode(file); journal_t *journal; handle_t *handle; int err; struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + errseq_t since = READ_ONCE(file->f_wb_err); /* * We have to be very careful here: changing a data block's
@@ -5808,7 +5813,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) */ if (val) { down_write(&EXT4_I(inode)->i_mmap_sem); - err = filemap_write_and_wait(inode->i_mapping); + err = filemap_write_and_wait_since(inode->i_mapping, since); if (err < 0) { up_write(&EXT4_I(inode)->i_mmap_sem); ext4_inode_resume_unlocked_dio(inode);
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 0c21e22acd74..888a4533d078 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c@@ -207,9 +207,10 @@ static int uuid_is_zero(__u8 u[16]) } #endif -static int ext4_ioctl_setflags(struct inode *inode, +static int ext4_ioctl_setflags(struct file *file, unsigned int flags) { + struct inode *inode = file_inode(file); struct ext4_inode_info *ei = EXT4_I(inode); handle_t *handle = NULL; int err = -EPERM, migrate = 0;
@@ -293,7 +294,7 @@ static int ext4_ioctl_setflags(struct inode *inode, goto flags_out; if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) - err = ext4_change_inode_journal_flag(inode, jflag); + err = ext4_change_inode_journal_flag(file, jflag); if (err) goto flags_out; if (migrate) {
@@ -617,7 +618,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return err; inode_lock(inode); - err = ext4_ioctl_setflags(inode, flags); + err = ext4_ioctl_setflags(filp, flags); inode_unlock(inode); mnt_drop_write_file(filp); return err;
@@ -1015,7 +1016,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) inode_lock(inode); flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) | (flags & EXT4_FL_XFLAG_VISIBLE); - err = ext4_ioctl_setflags(inode, flags); + err = ext4_ioctl_setflags(filp, flags); inode_unlock(inode); mnt_drop_write_file(filp); if (err)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 0b177da9ea82..9ce0b6e63abb 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c@@ -119,7 +119,7 @@ static struct file_system_type ext2_fs_type = { .name = "ext2", .mount = ext4_mount, .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, + .fs_flags = FS_REQUIRES_DEV|FS_WB_ERRSEQ, }; MODULE_ALIAS_FS("ext2"); MODULE_ALIAS("ext2");
@@ -134,7 +134,7 @@ static struct file_system_type ext3_fs_type = { .name = "ext3", .mount = ext4_mount, .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, + .fs_flags = FS_REQUIRES_DEV|FS_WB_ERRSEQ, }; MODULE_ALIAS_FS("ext3"); MODULE_ALIAS("ext3");
@@ -4887,6 +4887,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) ext4_group_t g; unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO; int err = 0; + errseq_t since = filemap_sample_wb_err(sb->s_bdev->bd_inode->i_mapping); #ifdef CONFIG_QUOTA int i, j; #endif
@@ -4988,7 +4989,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) } if (*flags & MS_RDONLY) { - err = sync_filesystem(sb); + err = sync_filesystem_since(sb, since); if (err < 0) goto restore_opts; err = dquot_suspend(sb, -1);
@@ -5690,7 +5691,7 @@ static struct file_system_type ext4_fs_type = { .name = "ext4", .mount = ext4_mount, .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, + .fs_flags = FS_REQUIRES_DEV|FS_WB_ERRSEQ, }; MODULE_ALIAS_FS("ext4");
--
2.9.4