Current fs_may_remount_ro() implementation is suboptimal. Sane implementation must have some sort of per-sb dirty counter. We may implement it exactly like mnt write counter. But this introduce additional code on fast path. Let do not reinvent the weel. And just caclulate sb write count as sum of write_count of it's vfsmnts. To achieve this task we have to link vfsmnt in to per-sb list. Later patch introduce actual dirty write counter implementation. NOTE: The problem with per-sb list is than external modules may not know about this new logic. Module may just forget to insert mnt in to sb's list. This condition is hard to guard by some sort of BUG_ON. So it's list will be empty and write_count will result incorrect result. Signed-off-by: Dmitry Monakhov <dmonakhov@xxxxxxxxxx> --- fs/btrfs/super.c | 2 +- fs/gfs2/ops_fstype.c | 4 ++-- fs/namespace.c | 23 +++++++++++++++++++++-- fs/nfs/super.c | 12 ++++++------ fs/super.c | 1 + include/linux/fs.h | 3 +++ include/linux/mount.h | 1 + 7 files changed, 35 insertions(+), 11 deletions(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 8a1ea6e..f82b0ad 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -567,7 +567,7 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags, } } - mnt->mnt_sb = s; + super_add_mnt(s, mnt); mnt->mnt_root = root; kfree(subvol_name); diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 8a102f7..f0d71a0 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1361,7 +1361,7 @@ static int gfs2_get_sb(struct file_system_type *fs_type, int flags, } sdp = s->s_fs_info; - mnt->mnt_sb = s; + super_add_mnt(sb, mnt); if (args.ar_meta) mnt->mnt_root = dget(sdp->sd_master_dir); else @@ -1406,7 +1406,7 @@ static int gfs2_get_sb_meta(struct file_system_type *fs_type, int flags, return -EBUSY; } sdp = s->s_fs_info; - mnt->mnt_sb = s; + super_add_mnt(s, mnt); mnt->mnt_root = dget(sdp->sd_master_dir); return 0; } diff --git a/fs/namespace.c b/fs/namespace.c index ffa3843..e816097 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -145,6 +145,7 @@ struct vfsmount *alloc_vfsmnt(const char *name) INIT_LIST_HEAD(&mnt->mnt_hash); INIT_LIST_HEAD(&mnt->mnt_child); INIT_LIST_HEAD(&mnt->mnt_mounts); + INIT_LIST_HEAD(&mnt->mnt_sb_list); INIT_LIST_HEAD(&mnt->mnt_list); INIT_LIST_HEAD(&mnt->mnt_expire); INIT_LIST_HEAD(&mnt->mnt_share); @@ -389,9 +390,26 @@ static void __mnt_unmake_readonly(struct vfsmount *mnt) spin_unlock(&vfsmount_lock); } -void simple_set_mnt(struct vfsmount *mnt, struct super_block *sb) +void super_add_mnt(struct super_block *sb, struct vfsmount *mnt) { + spin_lock(&vfsmount_lock); + list_add(&sb->s_vfsmount, &mnt->mnt_sb_list); mnt->mnt_sb = sb; + spin_unlock(&vfsmount_lock); +} +EXPORT_SYMBOL(super_add_mnt); + +void super_del_mnt(struct vfsmount *mnt) +{ + spin_lock(&vfsmount_lock); + list_del_init(&mnt->mnt_sb_list); + spin_unlock(&vfsmount_lock); +} +EXPORT_SYMBOL(super_del_mnt); + +void simple_set_mnt(struct vfsmount *mnt, struct super_block *sb) +{ + super_add_mnt(sb, mnt); mnt->mnt_root = dget(sb->s_root); } @@ -563,7 +581,7 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, mnt->mnt_flags = old->mnt_flags; atomic_inc(&sb->s_active); - mnt->mnt_sb = sb; + super_add_mnt(sb, mnt); mnt->mnt_root = dget(root); mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; @@ -997,6 +1015,7 @@ void release_mounts(struct list_head *head) while (!list_empty(head)) { mnt = list_first_entry(head, struct vfsmount, mnt_hash); list_del_init(&mnt->mnt_hash); + super_del_mnt(mnt); if (mnt->mnt_parent != mnt) { struct dentry *dentry; struct vfsmount *m; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index f1afee4..3470201 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2235,7 +2235,7 @@ static int nfs_get_sb(struct file_system_type *fs_type, goto error_splat_root; s->s_flags |= MS_ACTIVE; - mnt->mnt_sb = s; + super_add_mnt(s, mnt); mnt->mnt_root = mntroot; error = 0; @@ -2347,7 +2347,7 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags, } s->s_flags |= MS_ACTIVE; - mnt->mnt_sb = s; + super_add_mnt(s, mnt); mnt->mnt_root = mntroot; /* clone any lsm security options from the parent to the new sb */ @@ -2599,7 +2599,7 @@ static int nfs4_remote_get_sb(struct file_system_type *fs_type, goto error_splat_root; s->s_flags |= MS_ACTIVE; - mnt->mnt_sb = s; + super_add_mnt(s, mnt); mnt->mnt_root = mntroot; error = 0; @@ -2681,7 +2681,7 @@ static int nfs_follow_remote_path(struct vfsmount *root_mnt, s = nd.path.mnt->mnt_sb; atomic_inc(&s->s_active); - mnt_target->mnt_sb = s; + super_add_mnt(s, mnt); mnt_target->mnt_root = dget(nd.path.dentry); /* Correct the device pathname */ @@ -2832,7 +2832,7 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, } s->s_flags |= MS_ACTIVE; - mnt->mnt_sb = s; + super_add_mnt(s, mnt); mnt->mnt_root = mntroot; security_sb_clone_mnt_opts(data->sb, s); @@ -2914,7 +2914,7 @@ static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type, } s->s_flags |= MS_ACTIVE; - mnt->mnt_sb = s; + super_add_mnt(s, mnt); mnt->mnt_root = mntroot; security_sb_clone_mnt_opts(data->sb, s); diff --git a/fs/super.c b/fs/super.c index f35ac60..d3e0083 100644 --- a/fs/super.c +++ b/fs/super.c @@ -151,6 +151,7 @@ int __put_super_and_need_restart(struct super_block *sb) { /* check for race with generic_shutdown_super() */ if (list_empty(&sb->s_list)) { + WARN_ON(!list_empty(&sb->s_vfsmount)); /* super block is removed, need to restart... */ __put_super(sb); return 1; diff --git a/include/linux/fs.h b/include/linux/fs.h index 66369f6..75f057d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1339,6 +1339,7 @@ struct super_block { #endif struct xattr_handler **s_xattr; + struct list_head s_vfsmount; /* All vfsmounts */ struct list_head s_inodes; /* all inodes */ struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ struct list_head s_files; @@ -1779,6 +1780,8 @@ extern int get_sb_pseudo(struct file_system_type *, char *, const struct super_operations *ops, unsigned long, struct vfsmount *mnt); extern void simple_set_mnt(struct vfsmount *mnt, struct super_block *sb); +extern void super_add_mnt(struct super_block *sb, struct vfsmount *mnt); +extern void super_del_mnt(struct vfsmount *mnt); int __put_super_and_need_restart(struct super_block *sb); void put_super(struct super_block *sb); diff --git a/include/linux/mount.h b/include/linux/mount.h index ca726eb..751784c 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -59,6 +59,7 @@ struct vfsmount { /* 4 bytes hole on 64bits arches */ const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; + struct list_head mnt_sb_list; /* link sb-speciffic mounts */ struct list_head mnt_expire; /* link in fs-specific expiry list */ struct list_head mnt_share; /* circular list of shared mounts */ struct list_head mnt_slave_list;/* list of slave mounts */ -- 1.6.6 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html