vfs: allow syncfs to return an error when inode writeback fails
Subsystems (40)
Jeff Layton
maintainerv2 cover vfs: allow syncfs to return an error when inode writeback fails
From: Jeff Layton <[email protected]>
v2: don't modify sync_fs op prototype, handle errors at vfs layer
The syncfs system call returns an int error code, but in general it does not return errors when an inode fails writeback. It will return an error if __sync_blockdev fails, but that's not useful on non-block based filesystems and is generally not useful on more modern filesystems that don't rely on that call.
This patchset aims to remedy that by adding some infrastructure to allow syncfs to return an error when an inode fails writeback. The basic idea is to add a new errseq_t to the super_block to record writeback errors.
Because we don't want to grow struct file for this purpose, this new behavior is only activated when the fd passed to syncfs is opened with O_PATH.
One of the items on the wishlist from the PostgreSQL developers at this year's LSF/MM was a way to tell whether there has been any writeback failure on a filesystem without forcing everything to be synced out.
The final patch in this series adds a new generic vfs ioctl that presents the new sb->s_wb_err field to userland applications that wish to see it.
Comments and suggestions welcome.
Jeff Layton (5):
vfs: push __sync_blockdev calls down into sync_fs routines
vfs: add an errseq_t pointer arg to sync_filesystem and
__sync_filesystem
vfs: track per-sb writeback errors and report them to syncfs
buffer: record blockdev write errors in super_block that backs them
vfs: add a new ioctl for fetching the superblock's errseq_t
drivers/staging/ncpfs/inode.c | 2 +- fs/adfs/super.c | 2 +- fs/affs/super.c | 4 +-- fs/befs/linuxvfs.c | 2 +- fs/block_dev.c | 3 ++- fs/btrfs/super.c | 4 +-- fs/buffer.c | 2 ++ fs/cachefiles/interface.c | 2 +- fs/cifs/cifsfs.c | 2 +- fs/coda/inode.c | 2 +- fs/cramfs/inode.c | 2 +- fs/debugfs/inode.c | 2 +- fs/efs/super.c | 2 +- fs/ext2/super.c | 4 +-- fs/ext4/super.c | 13 +++++----- fs/f2fs/super.c | 15 ++++++----- fs/fat/inode.c | 2 +- fs/freevxfs/vxfs_super.c | 2 +- fs/fuse/inode.c | 2 +- fs/gfs2/super.c | 6 +++-- fs/hfs/super.c | 4 +-- fs/hfsplus/super.c | 2 +- fs/hpfs/super.c | 2 +- fs/internal.h | 7 ------ fs/ioctl.c | 3 +++ fs/isofs/inode.c | 2 +- fs/jffs2/super.c | 2 +- fs/jfs/super.c | 5 ++-- fs/minix/inode.c | 2 +- fs/nfs/super.c | 2 +- fs/nilfs2/super.c | 7 +++--- fs/ntfs/super.c | 2 +- fs/ocfs2/super.c | 4 +-- fs/open.c | 6 ++--- fs/openpromfs/inode.c | 2 +- fs/overlayfs/super.c | 2 +- fs/proc/root.c | 2 +- fs/pstore/inode.c | 2 +- fs/qnx4/inode.c | 2 +- fs/qnx6/inode.c | 2 +- fs/quota/dquot.c | 11 +++----- fs/reiserfs/super.c | 4 +-- fs/romfs/super.c | 2 +- fs/squashfs/super.c | 2 +- fs/super.c | 4 +-- fs/sync.c | 47 +++++++++++++++++++++++++++-------- fs/sysv/inode.c | 5 ++-- fs/tracefs/inode.c | 2 +- fs/ubifs/super.c | 2 +- fs/udf/super.c | 2 +- fs/ufs/super.c | 2 +- fs/xfs/xfs_super.c | 2 +- include/linux/errseq.h | 1 + include/linux/fs.h | 13 +++++++++- include/linux/pagemap.h | 5 +++- include/uapi/linux/fs.h | 1 + lib/errseq.c | 33 ++++++++++++++++++++++-- 57 files changed, 173 insertions(+), 102 deletions(-)
Signature · 1 line
2.17.0
Jeff Layton
maintainerPATCH v2 1/5 vfs: push __sync_blockdev calls down into sync_fs routines
From: Jeff Layton <[email protected]>
Currently, we always call __sync_blockdev after sync_fs, though very few filesystems actually need it these days.
Note that many older filesystems still rely on flushing out the bd_inode cache to ensure that it's safely written to the backing store, so when sync_fs is not defined, we do still call __sync_blockdev to support them.
Export __sync_blockdev and push the calls to __sync_blockdev down into the sync_fs routines. Push those calls down into the filesystem ->sync_fs routines that actually need it, rather than calling it unconditionally.
This also means that we need to return the error from the ->sync_fs op to the caller as well, so add a vfs_sync_fs helper to encapsulate these details.
--- fs/affs/super.c | 2 +- fs/block_dev.c | 1 + fs/ext2/super.c | 2 +- fs/ext4/super.c | 9 +++++---- fs/f2fs/super.c | 15 +++++++++------ fs/gfs2/super.c | 4 +++- fs/hfs/super.c | 2 +- fs/internal.h | 7 ------- fs/jfs/super.c | 3 +-- fs/nilfs2/super.c | 5 +++-- fs/ocfs2/super.c | 2 +- fs/quota/dquot.c | 9 +++------ fs/reiserfs/super.c | 2 +- fs/sync.c | 21 ++++++++++++++++----- fs/sysv/inode.c | 3 +-- include/linux/fs.h | 8 ++++++++ 16 files changed, 55 insertions(+), 40 deletions(-)
fs/affs/super.c +1 -1
@@ -58,7 +58,7 @@ static int affs_sync_fs(struct super_block *sb, int wait) {affs_commit_super (sb, wait); -return 0 ; +return __sync_blockdev (sb->s_bdev, wait); }static void flush_superblock (struct work_struct *work)
fs/block_dev.c +1 -0
@@ -447,6 +447,7 @@ int __sync_blockdev(struct block_device *bdev, int wait)return filemap_flush (bdev->bd_inode->i_mapping);return filemap_write_and_wait (bdev->bd_inode->i_mapping); } +EXPORT_SYMBOL (__sync_blockdev);/* * Write out and wait upon all the dirty data associated with a block
fs/ext2/super.c +1 -1
@@ -1280,7 +1280,7 @@ static int ext2_sync_fs(struct super_block *sb, int wait) }spin_unlock (&sbi->s_lock);ext2_sync_super (sb, es, wait); -return 0 ; +return __sync_blockdev (sb->s_bdev, wait); }static int ext2_freeze (struct super_block *sb)
fs/ext4/super.c +5 -4
@@ -4857,13 +4857,13 @@ int ext4_force_commit(struct super_block *sb)static int ext4_sync_fs (struct super_block *sb,int wait) { -int ret =0 ; +int ret =0 , ret2;tid_t target;bool needs_barrier = false;struct ext4_sb_info *sbi =EXT4_SB (sb);if (unlikely (ext4_forced_shutdown (sbi))) -return 0 ; + goto out;trace_ext4_sync_fs (sb, wait);flush_workqueue (sbi->rsv_conversion_wq);
@@ -4896,8 +4896,9 @@ static int ext4_sync_fs(struct super_block *sb, int wait)if (!ret) ret = err; } - -return ret; +out: + ret2 =__sync_blockdev (sb->s_bdev, wait); +return ret ? ret : ret2; }/*
fs/f2fs/super.c +9 -6
@@ -1058,15 +1058,17 @@ static void f2fs_put_super(struct super_block *sb)int f2fs_sync_fs (struct super_block *sb,int sync) {struct f2fs_sb_info *sbi =F2FS_SB (sb); -int err =0 ; +int err =0 , err2;if (unlikely (f2fs_cp_error (sbi))) -return 0 ; + goto out;trace_f2fs_sync_fs (sb, sync); -if (unlikely (is_sbi_flag_set (sbi,SBI_POR_DOING ))) -return -EAGAIN ; +if (unlikely (is_sbi_flag_set (sbi,SBI_POR_DOING ))) { + err = -EAGAIN ; + goto out; + }if (sync) {struct cp_control cpc;
@@ -1078,8 +1080,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync)mutex_unlock (&sbi->gc_mutex); }f2fs_trace_ios (NULL ,1 ); - -return err; +out: + err2 =__sync_blockdev (sb->s_bdev, sync); +return err ? err : err2; }static int f2fs_freeze (struct super_block *sb)
fs/gfs2/super.c +3 -1
@@ -951,13 +951,15 @@ static void gfs2_put_super(struct super_block *sb)static int gfs2_sync_fs (struct super_block *sb,int wait) { +int bderr;struct gfs2_sbd *sdp = sb->s_fs_info;gfs2_quota_sync (sb,-1 );if (wait)gfs2_log_flush (sdp,NULL ,GFS2_LOG_HEAD_FLUSH_NORMAL |GFS2_LFC_SYNC_FS ); -return sdp->sd_log_error; + bderr =__sync_blockdev (sb->s_bdev, wait); +return sdp->sd_log_error ? sdp->sd_log_error : bderr; }void gfs2_freeze_func (struct work_struct *work)
fs/hfs/super.c +1 -1
@@ -33,7 +33,7 @@ MODULE_LICENSE("GPL");static int hfs_sync_fs (struct super_block *sb,int wait) {hfs_mdb_commit (sb); -return 0 ; +return __sync_blockdev (sb->s_bdev, wait); }/*
fs/internal.h +0 -7
@@ -24,17 +24,10 @@ struct shrink_control;#ifdef CONFIG_BLOCK extern void __init bdev_cache_init (void ); -extern int __sync_blockdev (struct block_device *bdev,int wait); -#else static inline void bdev_cache_init (void ) { } - -static inline int __sync_blockdev (struct block_device *bdev,int wait) -{ -return 0 ; -}#endif /*
fs/jfs/super.c +1 -2
@@ -717,8 +717,7 @@ static int jfs_sync_fs(struct super_block *sb, int wait)jfs_flush_journal (log, wait);jfs_syncpt (log,0 ); } - -return 0 ; +return __sync_blockdev (sb->s_bdev, wait); }static int jfs_show_options (struct seq_file *seq,struct dentry *root)
fs/nilfs2/super.c +3 -2
@@ -495,7 +495,7 @@ static int nilfs_sync_fs(struct super_block *sb, int wait) {struct the_nilfs *nilfs = sb->s_fs_info;struct nilfs_super_block **sbp; -int err =0 ; +int err =0 , bderr;/* This function is called when super block should be written back */ if (wait)
@@ -514,7 +514,8 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)if (!err) err =nilfs_flush_device (nilfs); -return err; + bderr =__sync_blockdev (sb->s_bdev, wait); +return err ? err : bderr; } int nilfs_attach_checkpoint(struct super_block *sb, __u64 cno, int curr_mnt,
fs/ocfs2/super.c +1 -1
@@ -429,7 +429,7 @@ static int ocfs2_sync_fs(struct super_block *sb, int wait)jbd2_log_wait_commit (osb->journal->j_journal, target); } -return 0 ; +return __sync_blockdev (sb->s_bdev, wait); }static int ocfs2_need_system_inode (struct ocfs2_super *osb,int ino)
fs/quota/dquot.c +3 -6
@@ -686,9 +686,7 @@ int dquot_quota_sync(struct super_block *sb, int type)/* This is not very clever (and fast) but currently I don't know about * any other simple way of getting quota data to disk and we must get * them there for userspace to be visible... */ -if (sb->s_op->sync_fs) - sb->s_op->sync_fs (sb,1 ); -sync_blockdev (sb->s_bdev); +vfs_sync_fs (sb,1 );/* * Now when everything is written we can discard the pagecache so
@@ -2245,9 +2243,8 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)/* Sync the superblock so that buffers with quota data are written to * disk (and so userspace sees correct data afterwards). */ -if (sb->s_op->sync_fs) - sb->s_op->sync_fs (sb,1 ); -sync_blockdev (sb->s_bdev); +vfs_sync_fs (sb,1 ); +/* Now the quota files are just ordinary files and we can set the * inode flags back. Moreover we discard the pagecache so that * userspace sees the writes we did bypassing the pagecache. We
fs/reiserfs/super.c +1 -1
@@ -78,7 +78,7 @@ static int reiserfs_sync_fs(struct super_block *s, int wait)if (!journal_end_sync (&th))reiserfs_flush_old_commits (s);reiserfs_write_unlock (s); -return 0 ; +return __sync_blockdev (s->s_bdev, wait); }static void flush_old_commits (struct work_struct *work)
fs/sync.c +16 -5
@@ -21,6 +21,18 @@#define VALID_FLAGS (SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE| \ SYNC_FILE_RANGE_WAIT_AFTER) +/* +* Many legacy filesystems don't have a sync_fs op. For them, we just flush +* the block device (if there is one). +*/ +int vfs_sync_fs (struct super_block *sb,int wait) +{ +if (sb->s_op->sync_fs) +return sb->s_op->sync_fs (sb, wait); +return __sync_blockdev (sb->s_bdev, wait); +} +EXPORT_SYMBOL (vfs_sync_fs); +/* * Do the filesystem syncing work. For simple filesystems * writeback_inodes_sb(sb) just dirties buffers with inodes so we have to
@@ -35,9 +47,7 @@ static int __sync_filesystem(struct super_block *sb, int wait)else writeback_inodes_sb (sb,WB_REASON_SYNC ); -if (sb->s_op->sync_fs) - sb->s_op->sync_fs (sb, wait); -return __sync_blockdev (sb->s_bdev, wait); +return vfs_sync_fs (sb, wait); }/*
@@ -78,8 +88,9 @@ static void sync_fs_one_sb(struct super_block *sb, void *arg) {int wait = arg ?1 :0 ; -if (!sb_rdonly (sb) && sb->s_op->sync_fs) - sb->s_op->sync_fs (sb, wait); +if (sb_rdonly (sb)) +return ; +vfs_sync_fs (sb, wait); }static void fdatawrite_one_bdev (struct block_device *bdev,void *arg)
fs/sysv/inode.c +1 -2
@@ -53,8 +53,7 @@ static int sysv_sync_fs(struct super_block *sb, int wait) }mutex_unlock (&sbi->s_lock); - -return 0 ; +return __sync_blockdev (sb->s_bdev, wait); }static int sysv_remount (struct super_block *sb,int *flags,char *data)
include/linux/fs.h +8 -0
@@ -2441,6 +2441,7 @@ extern void bd_forget(struct inode *inode);extern void bdput (struct block_device *);extern void invalidate_bdev (struct block_device *);extern void iterate_bdevs (void (*)(struct block_device *,void *),void *); +extern int __sync_blockdev (struct block_device *bdev,int wait);extern int sync_blockdev (struct block_device *bdev);extern void kill_bdev (struct block_device *);extern struct super_block *freeze_bdev (struct block_device *);
@@ -2461,6 +2462,11 @@ static inline int sync_blockdev(struct block_device *bdev) { return 0; }static inline void kill_bdev (struct block_device *bdev) {}static inline void invalidate_bdev (struct block_device *bdev) {} +static inline int __sync_blockdev (struct block_device *bdev,int wait) +{ +return 0 ; +} +static inline struct super_block *freeze_bdev (struct block_device *sb) {return NULL ;
@@ -2485,9 +2491,11 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb) return false; }#endif +int vfs_sync_fs (struct super_block *sb,int wait);extern int sync_filesystem (struct super_block *);extern const struct file_operations def_blk_fops;extern const struct file_operations def_chr_fops; +#ifdef CONFIG_BLOCK extern int ioctl_by_bdev (struct block_device *,unsigned ,unsigned long );extern int blkdev_ioctl (struct block_device *,fmode_t ,unsigned ,unsigned long );
Signature · 1 line
2.17.0
Jeff Layton
maintainerPATCH v2 2/5 vfs: add an errseq_t pointer arg to sync_filesystem and __sync_filesystem
In reply to Jeff LaytonFrom: Jeff Layton <[email protected]>
Allow sync_filesystem and __sync_filesystem to take an errseq_t pointer for the purposes of error reporting. For now, everything passes in NULL and the field is ignored, but a later patch will change that.
--- drivers/staging/ncpfs/inode.c | 2 +- fs/adfs/super.c | 2 +- fs/affs/super.c | 2 +- fs/befs/linuxvfs.c | 2 +- fs/block_dev.c | 2 +- fs/btrfs/super.c | 4 ++-- fs/cachefiles/interface.c | 2 +- fs/cifs/cifsfs.c | 2 +- fs/coda/inode.c | 2 +- fs/cramfs/inode.c | 2 +- fs/debugfs/inode.c | 2 +- fs/efs/super.c | 2 +- fs/ext2/super.c | 2 +- fs/ext4/super.c | 4 ++-- fs/fat/inode.c | 2 +- fs/freevxfs/vxfs_super.c | 2 +- fs/fuse/inode.c | 2 +- fs/gfs2/super.c | 2 +- fs/hfs/super.c | 2 +- fs/hfsplus/super.c | 2 +- fs/hpfs/super.c | 2 +- fs/isofs/inode.c | 2 +- fs/jffs2/super.c | 2 +- fs/jfs/super.c | 2 +- fs/minix/inode.c | 2 +- fs/nfs/super.c | 2 +- fs/nilfs2/super.c | 2 +- fs/ntfs/super.c | 2 +- fs/ocfs2/super.c | 2 +- fs/openpromfs/inode.c | 2 +- fs/overlayfs/super.c | 2 +- fs/proc/root.c | 2 +- fs/pstore/inode.c | 2 +- fs/qnx4/inode.c | 2 +- fs/qnx6/inode.c | 2 +- fs/quota/dquot.c | 2 +- fs/reiserfs/super.c | 2 +- fs/romfs/super.c | 2 +- fs/squashfs/super.c | 2 +- fs/super.c | 4 ++-- fs/sync.c | 10 +++++----- fs/sysv/inode.c | 2 +- fs/tracefs/inode.c | 2 +- fs/ubifs/super.c | 2 +- fs/udf/super.c | 2 +- fs/ufs/super.c | 2 +- fs/xfs/xfs_super.c | 2 +- include/linux/fs.h | 2 +- 48 files changed, 55 insertions(+), 55 deletions(-)
drivers/staging/ncpfs/inode.c +1 -1
@@ -103,7 +103,7 @@ static void destroy_inodecache(void)static int ncp_remount (struct super_block *sb,int *flags,char * data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_NODIRATIME ;return 0 ; }
fs/adfs/super.c +1 -1
@@ -212,7 +212,7 @@ static int parse_options(struct super_block *sb, char *options)static int adfs_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_NODIRATIME ;return parse_options (sb, data); }
fs/affs/super.c +1 -1
@@ -570,7 +570,7 @@ affs_remount(struct super_block *sb, int *flags, char *data)pr_debug ("%s(flags=0x%x,opts=\"%s\")\n" , __func__, *flags, data); -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_NODIRATIME ;memcpy (volume, sbi->s_volume,32 );
fs/befs/linuxvfs.c +1 -1
@@ -944,7 +944,7 @@ befs_fill_super(struct super_block *sb, void *data, int silent)static int befs_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL );if (!(*flags &SB_RDONLY ))return -EINVAL ;return 0 ;
fs/block_dev.c +1 -1
@@ -468,7 +468,7 @@ int fsync_bdev(struct block_device *bdev) {struct super_block *sb =get_super (bdev);if (sb) { -int res =sync_filesystem (sb); +int res =sync_filesystem (sb,NULL );drop_super (sb);return res; }
fs/btrfs/super.c +2 -2
@@ -1732,7 +1732,7 @@ static inline void btrfs_remount_begin(struct btrfs_fs_info *fs_info,wait_event (fs_info->transaction_wait, (atomic_read (&fs_info->defrag_running) ==0 ));if (flags &SB_RDONLY ) -sync_filesystem (fs_info->sb); +sync_filesystem (fs_info->sb,NULL ); } }
@@ -1763,7 +1763,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)u32 old_metadata_ratio = fs_info->metadata_ratio;int ret; -sync_filesystem (sb); +sync_filesystem (sb,NULL );btrfs_remount_prepare (fs_info);if (data) {
fs/cachefiles/interface.c +1 -1
@@ -391,7 +391,7 @@ static void cachefiles_sync_cache(struct fscache_cache *_cache)* written to disc */ cachefiles_begin_secure (cache, &saved_cred);down_read (&cache->mnt->mnt_sb->s_umount); - ret =sync_filesystem (cache->mnt->mnt_sb); + ret =sync_filesystem (cache->mnt->mnt_sb,NULL );up_read (&cache->mnt->mnt_sb->s_umount);cachefiles_end_secure (cache, saved_cred);
fs/cifs/cifsfs.c +1 -1
@@ -574,7 +574,7 @@ static int cifs_show_stats(struct seq_file *s, struct dentry *root)static int cifs_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_NODIRATIME ;return 0 ; }
fs/coda/inode.c +1 -1
@@ -95,7 +95,7 @@ void coda_destroy_inodecache(void)static int coda_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_NOATIME ;return 0 ; }
fs/cramfs/inode.c +1 -1
@@ -504,7 +504,7 @@ static void cramfs_kill_sb(struct super_block *sb)static int cramfs_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_RDONLY ;return 0 ; }
fs/debugfs/inode.c +1 -1
@@ -135,7 +135,7 @@ static int debugfs_remount(struct super_block *sb, int *flags, char *data)int err;struct debugfs_fs_info *fsi = sb->s_fs_info; -sync_filesystem (sb); +sync_filesystem (sb,NULL ); err =debugfs_parse_options (data, &fsi->mount_opts);if (err) goto fail;
fs/efs/super.c +1 -1
@@ -115,7 +115,7 @@ static void destroy_inodecache(void)static int efs_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_RDONLY ;return 0 ; }
fs/ext2/super.c +1 -1
@@ -1326,7 +1326,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)struct ext2_mount_options new_opts;int err; -sync_filesystem (sb); +sync_filesystem (sb,NULL );spin_lock (&sbi->s_lock); new_opts.s_mount_opt = sbi->s_mount_opt;
fs/ext4/super.c +2 -2
@@ -5094,7 +5094,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) }if (*flags &SB_RDONLY ) { - err =sync_filesystem (sb); + err =sync_filesystem (sb,NULL );if (err <0 ) goto restore_opts; err =dquot_suspend (sb,-1 );
@@ -5590,7 +5590,7 @@ static int ext4_quota_off(struct super_block *sb, int type)/* Force all delayed allocation blocks to be allocated. * Caller already holds s_umount sem */ if (test_opt (sb,DELALLOC )) -sync_filesystem (sb); +sync_filesystem (sb,NULL );if (!inode || !igrab (inode)) goto out;
fs/fat/inode.c +1 -1
@@ -784,7 +784,7 @@ static int fat_remount(struct super_block *sb, int *flags, char *data)struct msdos_sb_info *sbi =MSDOS_SB (sb); *flags |=SB_NODIRATIME | (sbi->options.isvfat ?0 :SB_NOATIME ); -sync_filesystem (sb); +sync_filesystem (sb,NULL );/* make sure we update state on remount. */ new_rdonly = *flags &SB_RDONLY ;
fs/freevxfs/vxfs_super.c +1 -1
@@ -115,7 +115,7 @@ vxfs_statfs(struct dentry *dentry, struct kstatfs *bufp)static int vxfs_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_RDONLY ;return 0 ; }
fs/fuse/inode.c +1 -1
@@ -140,7 +140,7 @@ static void fuse_evict_inode(struct inode *inode)static int fuse_remount_fs (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL );if (*flags &SB_MANDLOCK )return -EINVAL ;
fs/gfs2/super.c +1 -1
@@ -1241,7 +1241,7 @@ static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data)struct gfs2_tune *gt = &sdp->sd_tune;int error; -sync_filesystem (sb); +sync_filesystem (sb,NULL );spin_lock (>->gt_spin); args.ar_commit = gt->gt_logd_secs;
fs/hfs/super.c +1 -1
@@ -113,7 +113,7 @@ static int hfs_statfs(struct dentry *dentry, struct kstatfs *buf)static int hfs_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_NODIRATIME ;if ((bool )(*flags &SB_RDONLY ) ==sb_rdonly (sb))return 0 ;
fs/hfsplus/super.c +1 -1
@@ -328,7 +328,7 @@ static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)static int hfsplus_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL );if ((bool )(*flags &SB_RDONLY ) ==sb_rdonly (sb))return 0 ;if (!(*flags &SB_RDONLY )) {
fs/hpfs/super.c +1 -1
@@ -454,7 +454,7 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)int o;struct hpfs_sb_info *sbi =hpfs_sb (s); -sync_filesystem (s); +sync_filesystem (s,NULL ); *flags |=SB_NOATIME ;
fs/isofs/inode.c +1 -1
@@ -113,7 +113,7 @@ static void destroy_inodecache(void)static int isofs_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL );if (!(*flags &SB_RDONLY ))return -EROFS ;return 0 ;
fs/jffs2/super.c +1 -1
@@ -243,7 +243,7 @@ static int jffs2_remount_fs(struct super_block *sb, int *flags, char *data)struct jffs2_sb_info *c =JFFS2_SB_INFO (sb);int err; -sync_filesystem (sb); +sync_filesystem (sb,NULL ); err =jffs2_parse_options (c, data);if (err)return -EINVAL ;
fs/jfs/super.c +1 -1
@@ -463,7 +463,7 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data)int flag =JFS_SBI (sb)->flag;int ret; -sync_filesystem (sb); +sync_filesystem (sb,NULL );if (!parse_options (data, sb, &newLVSize, &flag))return -EINVAL ;
fs/minix/inode.c +1 -1
@@ -123,7 +123,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data)struct minix_sb_info * sbi =minix_sb (sb);struct minix_super_block * ms; -sync_filesystem (sb); +sync_filesystem (sb,NULL ); ms = sbi->s_ms;if ((bool )(*flags &SB_RDONLY ) ==sb_rdonly (sb))return 0 ;
fs/nfs/super.c +1 -1
@@ -2252,7 +2252,7 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)struct nfs4_mount_data *options4 = (struct nfs4_mount_data *)raw_data;u32 nfsvers = nfss->nfs_client->rpc_ops->version; -sync_filesystem (sb); +sync_filesystem (sb,NULL );/* * Userspace mount programs that send binary options generally send
fs/nilfs2/super.c +1 -1
@@ -1126,7 +1126,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)unsigned long old_mount_opt;int err; -sync_filesystem (sb); +sync_filesystem (sb,NULL ); old_sb_flags = sb->s_flags; old_mount_opt = nilfs->ns_mount_opt;
fs/ntfs/super.c +1 -1
@@ -469,7 +469,7 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)ntfs_debug ("Entering with remount options string: %s" , opt); -sync_filesystem (sb); +sync_filesystem (sb,NULL );#ifndef NTFS_RW /* For read-only compiled driver, enforce read-only flag. */
fs/ocfs2/super.c +1 -1
@@ -641,7 +641,7 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)struct ocfs2_super *osb =OCFS2_SB (sb);u32 tmp; -sync_filesystem (sb); +sync_filesystem (sb,NULL );if (!ocfs2_parse_options (sb, data, &parsed_options,1 ) || !ocfs2_check_set_options (sb, &parsed_options)) {
fs/openpromfs/inode.c +1 -1
@@ -368,7 +368,7 @@ static struct inode *openprom_iget(struct super_block *sb, ino_t ino)static int openprom_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_NOATIME ;return 0 ; }
fs/overlayfs/super.c +1 -1
@@ -289,7 +289,7 @@ static int ovl_sync_fs(struct super_block *sb, int wait) upper_sb = ofs->upper_mnt->mnt_sb;down_read (&upper_sb->s_umount); - ret =sync_filesystem (upper_sb); + ret =sync_filesystem (upper_sb,NULL );up_read (&upper_sb->s_umount);return ret;
fs/proc/root.c +1 -1
@@ -82,7 +82,7 @@ int proc_remount(struct super_block *sb, int *flags, char *data) {struct pid_namespace *pid = sb->s_fs_info; -sync_filesystem (sb); +sync_filesystem (sb,NULL );return !proc_parse_options (data, pid); }
fs/pstore/inode.c +1 -1
@@ -273,7 +273,7 @@ static int pstore_show_options(struct seq_file *m, struct dentry *root)static int pstore_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL );parse_options (data);return 0 ;
fs/qnx4/inode.c +1 -1
@@ -44,7 +44,7 @@ static int qnx4_remount(struct super_block *sb, int *flags, char *data) {struct qnx4_sb_info *qs; -sync_filesystem (sb); +sync_filesystem (sb,NULL ); qs =qnx4_sb (sb); qs->Version =QNX4_VERSION ; *flags |=SB_RDONLY ;
fs/qnx6/inode.c +1 -1
@@ -55,7 +55,7 @@ static int qnx6_show_options(struct seq_file *seq, struct dentry *root)static int qnx6_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_RDONLY ;return 0 ; }
fs/quota/dquot.c +1 -1
@@ -2340,7 +2340,7 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,* the quota file since if blocksize < pagesize, invalidation * of the cache could fail because of other unrelated dirty * data */ -sync_filesystem (sb); +sync_filesystem (sb,NULL );invalidate_bdev (sb->s_bdev); }
fs/reiserfs/super.c +1 -1
@@ -1454,7 +1454,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)if (arg && !new_opts)return -ENOMEM ; -sync_filesystem (s); +sync_filesystem (s,NULL );reiserfs_write_lock (s);#ifdef CONFIG_QUOTA
fs/romfs/super.c +1 -1
@@ -450,7 +450,7 @@ static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf)*/ static int romfs_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_RDONLY ;return 0 ; }
fs/squashfs/super.c +1 -1
@@ -372,7 +372,7 @@ static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)static int squashfs_remount (struct super_block *sb,int *flags,char *data) { -sync_filesystem (sb); +sync_filesystem (sb,NULL ); *flags |=SB_RDONLY ;return 0 ; }
fs/super.c +2 -2
@@ -436,7 +436,7 @@ void generic_shutdown_super(struct super_block *sb)if (sb->s_root) {shrink_dcache_for_umount (sb); -sync_filesystem (sb); +sync_filesystem (sb,NULL ); sb->s_flags &= ~SB_ACTIVE ;fsnotify_unmount_inodes (sb);
@@ -1484,7 +1484,7 @@ int freeze_super(struct super_block *sb)sb_wait_write (sb,SB_FREEZE_PAGEFAULT );/* All writers are done so after syncing there won't be dirty data */ -sync_filesystem (sb); +sync_filesystem (sb,NULL );/* Now wait for internal filesystem counter */ sb->s_writers.frozen =SB_FREEZE_FS ;
fs/sync.c +5 -5
@@ -40,7 +40,7 @@ EXPORT_SYMBOL(vfs_sync_fs);* wait == 1 case since in that case write_inode() functions do * sync_dirty_buffer() and thus effectively write one block at a time. */ -static int __sync_filesystem (struct super_block *sb,int wait) +static int __sync_filesystem (struct super_block *sb,int wait,errseq_t *since) {if (wait)sync_inodes_sb (sb);
@@ -55,7 +55,7 @@ static int __sync_filesystem(struct super_block *sb, int wait)* superblock. Filesystem data as well as the underlying block * device. Takes the superblock lock. */ -int sync_filesystem (struct super_block *sb) +int sync_filesystem (struct super_block *sb,errseq_t *since) {int ret;
@@ -71,10 +71,10 @@ int sync_filesystem(struct super_block *sb)if (sb_rdonly (sb))return 0 ; - ret =__sync_filesystem (sb,0 ); + ret =__sync_filesystem (sb,0 , since);if (ret <0 )return ret; -return __sync_filesystem (sb,1 ); +return __sync_filesystem (sb,1 , since); }EXPORT_SYMBOL (sync_filesystem);
@@ -181,7 +181,7 @@ SYSCALL_DEFINE1(syncfs, int, fd) sb = f.file->f_path.dentry->d_sb;down_read (&sb->s_umount); - ret =sync_filesystem (sb); + ret =sync_filesystem (sb,NULL );up_read (&sb->s_umount);fdput (f);
fs/sysv/inode.c +1 -1
@@ -60,7 +60,7 @@ static int sysv_remount(struct super_block *sb, int *flags, char *data) {struct sysv_sb_info *sbi =SYSV_SB (sb); -sync_filesystem (sb); +sync_filesystem (sb,NULL );if (sbi->s_forced_ro) *flags |=SB_RDONLY ;return 0 ;
fs/tracefs/inode.c +1 -1
@@ -230,7 +230,7 @@ static int tracefs_remount(struct super_block *sb, int *flags, char *data)int err;struct tracefs_fs_info *fsi = sb->s_fs_info; -sync_filesystem (sb); +sync_filesystem (sb,NULL ); err =tracefs_parse_options (data, &fsi->mount_opts);if (err) goto fail;
fs/ubifs/super.c +1 -1
@@ -1847,7 +1847,7 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)int err;struct ubifs_info *c = sb->s_fs_info; -sync_filesystem (sb); +sync_filesystem (sb,NULL );dbg_gen ("old flags %#lx, new flags %#x" , sb->s_flags, *flags); err =ubifs_parse_options (c, data,1 );
fs/udf/super.c +1 -1
@@ -617,7 +617,7 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)int error =0 ;struct logicalVolIntegrityDescImpUse *lvidiu =udf_sb_lvidiu (sb); -sync_filesystem (sb); +sync_filesystem (sb,NULL );if (lvidiu) {int write_rev =le16_to_cpu (lvidiu->minUDFWriteRev);if (write_rev >UDF_MAX_WRITE_VERSION && !(*flags &SB_RDONLY ))
fs/ufs/super.c +1 -1
@@ -1303,7 +1303,7 @@ static int ufs_remount (struct super_block *sb, int *mount_flags, char *data)unsigned new_mount_opt, ufstype;unsigned flags; -sync_filesystem (sb); +sync_filesystem (sb,NULL );mutex_lock (&UFS_SB (sb)->s_lock); uspi =UFS_SB (sb)->s_uspi; flags =UFS_SB (sb)->s_flags;
fs/xfs/xfs_super.c +1 -1
@@ -1281,7 +1281,7 @@ xfs_fs_remount(if (error)return error; -sync_filesystem (sb); +sync_filesystem (sb,NULL );while ((p =strsep (&options,"," )) !=NULL ) {int token;
include/linux/fs.h +1 -1
@@ -2492,7 +2492,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb) }#endif int vfs_sync_fs (struct super_block *sb,int wait); -extern int sync_filesystem (struct super_block *); +extern int sync_filesystem (struct super_block *,errseq_t *);extern const struct file_operations def_blk_fops;extern const struct file_operations def_chr_fops;
Signature · 1 line
2.17.0
Jeff Layton
maintainerPATCH v2 3/5 vfs: track per-sb writeback errors and report them to syncfs
In reply to Jeff LaytonFrom: Jeff Layton <[email protected]>
Usually we suggest that applications call fsync when they want to ensure that all data written to the file has made it to the backing store, but that can be inefficient when there are a lot of open files.
Calling syncfs on the filesystem can be more efficient in some situations, but the error reporting doesn't currently work the way most people expect. If a single inode on a filesystem reports a writeback error, syncfs won't necessarily return an error. syncfs only returns an error if __sync_blockdev fails, and on some filesystems that's a no-op.
It would be better if syncfs reported an error if there were any writeback failures. Then applications could call syncfs to see if there are any errors on any open files, and could then call fsync on all of the other descriptors to figure out which one failed.
This patch adds a new errseq_t to struct super_block, and has mapping_set_error also record writeback errors there.
To report those errors, we also need to keep an errseq_t for in struct file to act as a cursor, but growing struct file for this purpose is undesirable. We could just reuse f_wb_err, but someone could mix calls to fsync and syncfs and that would break things.
This patch implements an alternative suggested by Willy. When the file is opened with O_PATH, then we repurpose the f_wb_err cursor to track s_wb_err. Any file opened with O_PATH will not have an fsync file_operation, and attempts to fsync such a fd will return -EBADF.
Note that calling syncfs on an O_PATH descriptor today will also return -EBADF, so this scheme gives userland a way to tell whether this mechanism will work at runtime.
--- fs/open.c | 6 +++--- fs/quota/dquot.c | 4 ++-- fs/sync.c | 28 +++++++++++++++++++++------- include/linux/fs.h | 5 ++++- include/linux/pagemap.h | 5 ++++- 5 files changed, 34 insertions(+), 14 deletions(-)
fs/open.c +3 -3
@@ -739,15 +739,15 @@ static int do_dentry_open(struct file *f, f->f_inode = inode; f->f_mapping = inode->i_mapping; -/* Ensure that we skip any errors that predate opening of the file */ - f->f_wb_err =filemap_sample_wb_err (f->f_mapping); -if (unlikely (f->f_flags &O_PATH )) { f->f_mode =FMODE_PATH ; f->f_op = &empty_fops; + f->f_wb_err =errseq_sample (&f->f_path.dentry->d_sb->s_wb_err); goto done; } + f->f_wb_err =filemap_sample_wb_err (f->f_mapping); +if (f->f_mode &FMODE_WRITE && !special_file (inode->i_mode)) { error =get_write_access (inode);if (unlikely (error))
fs/quota/dquot.c +2 -2
@@ -686,7 +686,7 @@ int dquot_quota_sync(struct super_block *sb, int type)/* This is not very clever (and fast) but currently I don't know about * any other simple way of getting quota data to disk and we must get * them there for userspace to be visible... */ -vfs_sync_fs (sb,1 ); +vfs_sync_fs (sb,1 ,NULL );/* * Now when everything is written we can discard the pagecache so
@@ -2243,7 +2243,7 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)/* Sync the superblock so that buffers with quota data are written to * disk (and so userspace sees correct data afterwards). */ -vfs_sync_fs (sb,1 ); +vfs_sync_fs (sb,1 ,NULL );/* Now the quota files are just ordinary files and we can set the * inode flags back. Moreover we discard the pagecache so that
fs/sync.c +21 -7
@@ -25,11 +25,20 @@* Many legacy filesystems don't have a sync_fs op. For them, we just flush * the block device (if there is one). */ -int vfs_sync_fs (struct super_block *sb,int wait) +int vfs_sync_fs (struct super_block *sb,int wait,errseq_t *since) { +int ret; +if (sb->s_op->sync_fs) -return sb->s_op->sync_fs (sb, wait); -return __sync_blockdev (sb->s_bdev, wait); + ret = sb->s_op->sync_fs (sb, wait); + ret =__sync_blockdev (sb->s_bdev, wait); + +if (since) { +int ret2 =errseq_check_and_advance (&sb->s_wb_err, since); +if (ret ==0 ) + ret = ret2; + } +return ret; }EXPORT_SYMBOL (vfs_sync_fs);
@@ -47,7 +56,7 @@ static int __sync_filesystem(struct super_block *sb, int wait, errseq_t *since)else writeback_inodes_sb (sb,WB_REASON_SYNC ); -return vfs_sync_fs (sb, wait); +return vfs_sync_fs (sb, wait, since); }/*
@@ -90,7 +99,7 @@ static void sync_fs_one_sb(struct super_block *sb, void *arg)if (sb_rdonly (sb))return ; -vfs_sync_fs (sb, wait); +vfs_sync_fs (sb, wait,NULL ); }static void fdatawrite_one_bdev (struct block_device *bdev,void *arg)
@@ -172,16 +181,21 @@ void emergency_sync(void)*/ SYSCALL_DEFINE1(syncfs, int, fd) { -struct fd f =fdget (fd); +struct fd f =fdget_raw (fd);struct super_block *sb; +errseq_t *wberr =NULL ;int ret;if (!f.file)return -EBADF ; + sb = f.file->f_path.dentry->d_sb; +if (f.file->f_flags &O_PATH ) + wberr = &f.file->f_wb_err; +down_read (&sb->s_umount); - ret =sync_filesystem (sb,NULL ); + ret =sync_filesystem (sb, wberr);up_read (&sb->s_umount);fdput (f);
include/linux/fs.h +4 -1
@@ -1416,6 +1416,9 @@ struct super_block {/* Being remounted read-only */ int s_readonly_remount; +/* per-sb errseq_t for reporting writeback errors via syncfs */ +errseq_t s_wb_err; +/* AIO completions deferred from interrupt context */ struct workqueue_struct *s_dio_done_wq;struct hlist_head s_pins;
@@ -2491,7 +2494,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb) return false; }#endif -int vfs_sync_fs (struct super_block *sb,int wait); +int vfs_sync_fs (struct super_block *sb,int wait,errseq_t *since);extern int sync_filesystem (struct super_block *,errseq_t *);extern const struct file_operations def_blk_fops;extern const struct file_operations def_chr_fops;
include/linux/pagemap.h +4 -1
@@ -51,7 +51,10 @@ static inline void mapping_set_error(struct address_space *mapping, int error)return ;/* Record in wb_err for checkers using errseq_t based tracking */ -filemap_set_wb_err (mapping, error); +__filemap_set_wb_err (mapping, error); + +/* Record it in superblock */ +errseq_set (&mapping->host->i_sb->s_wb_err, error);/* Record it in flags for now, for legacy callers */ if (error == -ENOSPC )
Signature · 1 line
2.17.0
Jeff Layton
maintainerPATCH v2 4/5 buffer: record blockdev write errors in super_block that backs them
In reply to Jeff LaytonFrom: Jeff Layton <[email protected]>
When syncing out a block device (a'la __sync_blockdev), any error encountered will only be recorded in the bd_inode's mapping. When the blockdev contains a filesystem however, we'd like to also record the error in the super_block that's stored there.
Make mark_buffer_write_io_error also record the error in the corresponding super_block when a writeback error occurs and the block device contains a mounted superblock.
--- fs/buffer.c | 2 ++ 1 file changed, 2 insertions(+)
fs/buffer.c +2 -0
@@ -1117,6 +1117,8 @@ void mark_buffer_write_io_error(struct buffer_head *bh)mapping_set_error (bh->b_page->mapping, -EIO );if (bh->b_assoc_map)mapping_set_error (bh->b_assoc_map, -EIO ); +if (bh->b_bdev->bd_super) +errseq_set (&bh->b_bdev->bd_super->s_wb_err, -EIO ); }EXPORT_SYMBOL (mark_buffer_write_io_error);
Signature · 1 line
2.17.0
Jeff Layton
maintainerPATCH v2 5/5 vfs: add a new ioctl for fetching the superblock's errseq_t
In reply to Jeff LaytonFrom: Jeff Layton <[email protected]>
The postgres developers recently mentioned that they'd like a way to tell whether there have been any writeback errors on a given filesystem without having to forcibly sync out all buffered writes.
Now that we have a per-sb errseq_t that tracks whether any inode on the filesystem might have failed writeback, we can present that to userland applications via a new interface. Add a new generic fs ioctl for that purpose. This just reports the current state of the errseq_t counter with the SEEN bit masked off.
--- fs/ioctl.c | 3 +++ include/linux/errseq.h | 1 + include/uapi/linux/fs.h | 1 + lib/errseq.c | 33 +++++++++++++++++++++++++++++++-- 4 files changed, 36 insertions(+), 2 deletions(-)
fs/ioctl.c +3 -0
@@ -667,6 +667,9 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,case FS_IOC_FIEMAP :return ioctl_fiemap (filp, arg); +case FS_IOC_GETFSERR : +return put_user (errseq_scrape (&inode->i_sb->s_wb_err), argp); +case FIGETBSZ :return put_user (inode->i_sb->s_blocksize, argp);
include/linux/errseq.h +1 -0
@@ -9,6 +9,7 @@ typedef u32 errseq_t;errseq_t errseq_set (errseq_t *eseq,int err);errseq_t errseq_sample (errseq_t *eseq); +errseq_t errseq_scrape (errseq_t *eseq);int errseq_check (errseq_t *eseq,errseq_t since);int errseq_check_and_advance (errseq_t *eseq,errseq_t *since);#endif
include/uapi/linux/fs.h +1 -0
@@ -253,6 +253,7 @@ struct fsxattr {#define FS_IOC32_SETVERSION _IOW('v', 2, int)#define FS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr)#define FS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr) +#define FS_IOC_GETFSERR _IOR('e', 1, unsigned int)/* * File system encryption support
lib/errseq.c +31 -2
@@ -108,7 +108,7 @@ errseq_t errseq_set(errseq_t *eseq, int err)EXPORT_SYMBOL (errseq_set);/** -* errseq_sample() - Grab current errseq_t value. +* errseq_sample() - Grab current errseq_t value (or 0 if it hasn't been seen) * @eseq: Pointer to errseq_t to be sampled. * * This function allows callers to initialise their errseq_t variable.
@@ -117,7 +117,7 @@ EXPORT_SYMBOL(errseq_set);* see it the next time it checks for an error. * * Context: Any context. -* Return: The current errseq value. +* Return: The current errseq value or 0 if it wasn't previously seen */ errseq_t errseq_sample (errseq_t *eseq) {
@@ -130,6 +130,35 @@ errseq_t errseq_sample(errseq_t *eseq) }EXPORT_SYMBOL (errseq_sample); +/** +* errseq_scrape() - Grab current errseq_t value +* @eseq: Pointer to errseq_t to be sampled. +* +* This function allows callers to scrape the current value of an errseq_t. +* Unlike errseq_sample, this will always return the current value with +* the SEEN flag unset, even when the value has not yet been seen. +* +* Context: Any context. +* Return: The current errseq value with ERRSEQ_SEEN masked off +*/ +errseq_t errseq_scrape (errseq_t *eseq) +{ +errseq_t old =READ_ONCE (*eseq); + +/* +* For the common case of no errors ever having been set, we can skip +* marking the SEEN bit. Once an error has been set, the value will +* never go back to zero. +*/ +if (old !=0 ) { +errseq_t new = old |ERRSEQ_SEEN ; +if (old != new) +cmpxchg (eseq, old, new); + } +return old & ~ERRSEQ_SEEN ; +} +EXPORT_SYMBOL (errseq_scrape); +/** * errseq_check() - Has an error occurred since a particular sample point? * @eseq: Pointer to errseq_t value to be checked.
Signature · 1 line
2.17.0
Jeff Layton
maintainerPATCH 1/1 generic: test reporting of wb errors via syncfs
In reply to Jeff LaytonFrom: Jeff Layton <[email protected]>
Add a test for new syncfs error reporting behavior. When an inode fails to be written back, ensure that we report an error to a subsequent call to syncfs().
Because we don't want to grow struct file in order to support this, we only do this if the file was opened with O_PATH flag.
--- tests/generic/999 | 99 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/999.out | 8 ++++ tests/generic/group | 1 + 3 files changed, 108 insertions(+) create mode 100755 tests/generic/999 create mode 100644 tests/generic/999.out
This is the companion testcase patch to this series:
[PATCH v2 0/5] vfs: allow syncfs to return an error when inode writeback fails
We probably don't want to merge this until it's clear that treating syncfs differently on an O_PATH open is OK.
tests/generic/999 new file +99 -0
@@ -0,0 +1,99 @@ +#! /bin/bash +# FS QA Test No. 999 +# +# Open a file and write to it and fsync. Then, flip the data device to throw +# errors, write to it again and do an fdatasync. Then open an O_PATH fd on the +# same file and call syncfs against it and ensure that an error is reported, +# then call syncfs again and ensure that no error is reported. Finally, repeat +# the open and syncfs and ensure that there is no error reported. +# +#----------------------------------------------------------------------- +# Copyright (c) 2018, Jeff Layton <[email protected]> +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +#----------------------------------------------------------------------- + +seq=`basename $0` +seqres=$RESULT_DIR/$seq +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! +trap "_cleanup; exit \$status" 0 1 2 3 15 + +_cleanup() +{ + cd / + rm -f $tmp.* + _dmerror_cleanup +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter +. ./common/dmerror + +# real QA test starts here +_supported_os Linux +_require_xfs_io_command 'open' '-P' +_require_scratch_nocheck +# This test uses "dm" without taking into account the data could be on +# realtime subvolume, thus the test will fail with rtinherit=1 +_require_no_rtinherit +_require_dm_target error + +rm -f $seqres.full + +echo "Format and mount" +_scratch_mkfs > $seqres.full 2>&1 +_dmerror_init +_dmerror_mount + +datalen=65536 +_require_fs_space $SCRATCH_MNT $datalen + +# use fd 5 to hold file open +testfile=$SCRATCH_MNT/syncfs-reports-errors +exec 5>$testfile + +# write some data to file and fsync it out +$XFS_IO_PROG -c "pwrite -W -q 0 $datalen" $testfile + +# flip device to non-working mode +_dmerror_load_error_table + +# rewrite the data, and do fdatasync +$XFS_IO_PROG -c "pwrite -w -q 0 $datalen" $testfile + +# heal the device error +_dmerror_load_working_table + +# open again and call syncfs twice +echo "One of the following syncfs calls should fail with EIO:" +$XFS_IO_PROG -P -c syncfs -c syncfs $testfile +echo "done" + +echo "This syncfs call should succeed:" +$XFS_IO_PROG -P -c syncfs $testfile +echo "done" + +# close file +exec 5>&- + +# success, all done +_dmerror_cleanup + +status=0 +exit
tests/generic/999.out new file +8 -0
@@ -0,0 +1,8 @@ +QA output created by 999 +Format and mount +fdatasync: Input/output error +One of the following syncfs calls should fail with EIO: +syncfs: Input/output error +done +This syncfs call should succeed: +done
tests/generic/group +1 -0
@@ -497,3 +497,4 @@ 493 auto quick swap 494 auto quick swap 495 auto quick swap +999 auto quick
Signature · 1 line
2.17.0
Jeff Layton
maintainerQuoted 86 lines · Jeff Layton
From: Jeff Layton <[email protected]>
v2: don't modify
sync_fsop prototype, handle errors at vfs layerThe syncfs system call returns an int error code, but in general it does not return errors when an inode fails writeback. It will return an error if
__sync_blockdevfails, but that's not useful on non-block based filesystems and is generally not useful on more modern filesystems that don't rely on that call.This patchset aims to remedy that by adding some infrastructure to allow syncfs to return an error when an inode fails writeback. The basic idea is to add a new
errseq_tto thesuper_blockto record writeback errors.Because we don't want to grow struct file for this purpose, this new behavior is only activated when the fd passed to syncfs is opened with
O_PATH.One of the items on the wishlist from the PostgreSQL developers at this year's LSF/MM was a way to tell whether there has been any writeback failure on a filesystem without forcing everything to be synced out.
The final patch in this series adds a new generic vfs ioctl that presents the new
sb->s_wb_errfield to userland applications that wish to see it.Comments and suggestions welcome.
Jeff Layton (5):
vfs: push__sync_blockdevcalls down intosync_fsroutines
vfs: add anerrseq_tpointer arg tosync_filesystemand
__sync_filesystem
vfs: track per-sb writeback errors and report them to syncfs
buffer: record blockdev write errors insuper_blockthat backs them
vfs: add a new ioctl for fetching the superblock'serrseq_tdrivers/staging/ncpfs/inode.c | 2 +- fs/adfs/super.c | 2 +- fs/affs/super.c | 4 +-- fs/befs/linuxvfs.c | 2 +- fs/block_dev.c | 3 ++- fs/btrfs/super.c | 4 +-- fs/buffer.c | 2 ++ fs/cachefiles/interface.c | 2 +- fs/cifs/cifsfs.c | 2 +- fs/coda/inode.c | 2 +- fs/cramfs/inode.c | 2 +- fs/debugfs/inode.c | 2 +- fs/efs/super.c | 2 +- fs/ext2/super.c | 4 +-- fs/ext4/super.c | 13 +++++----- fs/f2fs/super.c | 15 ++++++----- fs/fat/inode.c | 2 +- fs/freevxfs/vxfs_super.c | 2 +- fs/fuse/inode.c | 2 +- fs/gfs2/super.c | 6 +++-- fs/hfs/super.c | 4 +-- fs/hfsplus/super.c | 2 +- fs/hpfs/super.c | 2 +- fs/internal.h | 7 ------ fs/ioctl.c | 3 +++ fs/isofs/inode.c | 2 +- fs/jffs2/super.c | 2 +- fs/jfs/super.c | 5 ++-- fs/minix/inode.c | 2 +- fs/nfs/super.c | 2 +- fs/nilfs2/super.c | 7 +++--- fs/ntfs/super.c | 2 +- fs/ocfs2/super.c | 4 +-- fs/open.c | 6 ++--- fs/openpromfs/inode.c | 2 +- fs/overlayfs/super.c | 2 +- fs/proc/root.c | 2 +- fs/pstore/inode.c | 2 +- fs/qnx4/inode.c | 2 +- fs/qnx6/inode.c | 2 +- fs/quota/dquot.c | 11 +++----- fs/reiserfs/super.c | 4 +-- fs/romfs/super.c | 2 +- fs/squashfs/super.c | 2 +- fs/super.c | 4 +-- fs/sync.c | 47 +++++++++++++++++++++++++++-------- fs/sysv/inode.c | 5 ++-- fs/tracefs/inode.c | 2 +- fs/ubifs/super.c | 2 +- fs/udf/super.c | 2 +-
Quoted 9 lines
fs/ufs/super.c | 2 +- fs/xfs/xfs_super.c | 2 +- include/linux/errseq.h | 1 + include/linux/fs.h | 13 +++++++++- include/linux/pagemap.h | 5 +++- include/uapi/linux/fs.h | 1 + lib/errseq.c | 33 ++++++++++++++++++++++-- 57 files changed, 173 insertions(+), 102 deletions(-)
After chatting with David Howells about this, I think I'm going to withdraw this set and do one that only exposes s_wb_err via the new fsinfo syscall that's going in soon. We can always add the other interfaces later if it's
Let's just disregard this set for now and I'll look at respinning it around that as an interface.
Cheers,
Signature · 1 line
Jeff Layton <[email protected]>