diff --git a/module/os/linux/zfs/zpl_super.c b/module/os/linux/zfs/zpl_super.c index f5e7f3696..e404e05ef 100644 --- a/module/os/linux/zfs/zpl_super.c +++ b/module/os/linux/zfs/zpl_super.c @@ -370,79 +370,6 @@ zpl_test_super(struct super_block *s, void *data) return (zfsvfs != NULL && os == zfsvfs->z_os); } -static struct super_block * -zpl_mount_impl(struct file_system_type *fs_type, int flags, zfs_mnt_t *zm) -{ - struct super_block *s; - objset_t *os; - boolean_t issnap = B_FALSE; - int err; - - err = dmu_objset_hold(zm->mnt_osname, FTAG, &os); - if (err) - return (ERR_PTR(-err)); - - /* - * The dsl pool lock must be released prior to calling sget(). - * It is possible sget() may block on the lock in grab_super() - * while deactivate_super() holds that same lock and waits for - * a txg sync. If the dsl_pool lock is held over sget() - * this can prevent the pool sync and cause a deadlock. - */ - dsl_dataset_long_hold(dmu_objset_ds(os), FTAG); - dsl_pool_rele(dmu_objset_pool(os), FTAG); - - s = sget(fs_type, zpl_test_super, set_anon_super, flags, os); - - /* - * Recheck with the lock held to prevent mounting the wrong dataset - * since z_os can be stale when the teardown lock is held. - * - * We can't do this in zpl_test_super in since it's under spinlock and - * also s_umount lock is not held there so it would race with - * zfs_umount and zfsvfs can be freed. - */ - if (!IS_ERR(s) && s->s_fs_info != NULL) { - zfsvfs_t *zfsvfs = s->s_fs_info; - if (zpl_enter(zfsvfs, FTAG) == 0) { - if (os != zfsvfs->z_os) - err = -SET_ERROR(EBUSY); - issnap = zfsvfs->z_issnap; - zpl_exit(zfsvfs, FTAG); - } else { - err = -SET_ERROR(EBUSY); - } - } - dsl_dataset_long_rele(dmu_objset_ds(os), FTAG); - dsl_dataset_rele(dmu_objset_ds(os), FTAG); - - if (IS_ERR(s)) - return (ERR_CAST(s)); - - if (err) { - deactivate_locked_super(s); - return (ERR_PTR(err)); - } - - if (s->s_root == NULL) { - err = zpl_fill_super(s, zm, flags & SB_SILENT ? 1 : 0); - if (err) { - deactivate_locked_super(s); - return (ERR_PTR(err)); - } - s->s_flags |= SB_ACTIVE; - } else if (!issnap && ((flags ^ s->s_flags) & SB_RDONLY)) { - /* - * Skip ro check for snap since snap is always ro regardless - * ro flag is passed by mount or not. - */ - deactivate_locked_super(s); - return (ERR_PTR(-EBUSY)); - } - - return (s); -} - static void zpl_kill_sb(struct super_block *sb) { @@ -492,12 +419,79 @@ zpl_parse_monolithic(struct fs_context *fc, void *data) static int zpl_get_tree(struct fs_context *fc) { - zfs_mnt_t zm = { .mnt_osname = fc->source, .mnt_data = fc->fs_private }; + struct super_block *sb; + objset_t *os; + boolean_t issnap = B_FALSE; + int err; + + err = dmu_objset_hold(fc->source, FTAG, &os); + if (err) + return (-err); + + /* + * The dsl pool lock must be released prior to calling sget(). + * It is possible sget() may block on the lock in grab_super() + * while deactivate_super() holds that same lock and waits for + * a txg sync. If the dsl_pool lock is held over sget() + * this can prevent the pool sync and cause a deadlock. + */ + dsl_dataset_long_hold(dmu_objset_ds(os), FTAG); + dsl_pool_rele(dmu_objset_pool(os), FTAG); + + sb = sget(fc->fs_type, zpl_test_super, set_anon_super, + fc->sb_flags, os); + + /* + * Recheck with the lock held to prevent mounting the wrong dataset + * since z_os can be stale when the teardown lock is held. + * + * We can't do this in zpl_test_super in since it's under spinlock and + * also s_umount lock is not held there so it would race with + * zfs_umount and zfsvfs can be freed. + */ + if (!IS_ERR(sb) && sb->s_fs_info != NULL) { + zfsvfs_t *zfsvfs = sb->s_fs_info; + if (zpl_enter(zfsvfs, FTAG) == 0) { + if (os != zfsvfs->z_os) + err = SET_ERROR(EBUSY); + issnap = zfsvfs->z_issnap; + zpl_exit(zfsvfs, FTAG); + } else { + err = SET_ERROR(EBUSY); + } + } + dsl_dataset_long_rele(dmu_objset_ds(os), FTAG); + dsl_dataset_rele(dmu_objset_ds(os), FTAG); - struct super_block *sb = zpl_mount_impl(fc->fs_type, fc->sb_flags, &zm); if (IS_ERR(sb)) return (PTR_ERR(sb)); + if (err) { + deactivate_locked_super(sb); + return (-err); + } + + if (sb->s_root == NULL) { + zfs_mnt_t zm = { + .mnt_osname = fc->source, + .mnt_data = fc->fs_private, + }; + err = zpl_fill_super(sb, &zm, + fc->sb_flags & SB_SILENT ? 1 : 0); + if (err) { + deactivate_locked_super(sb); + return (-err); + } + sb->s_flags |= SB_ACTIVE; + } else if (!issnap && ((fc->sb_flags ^ sb->s_flags) & SB_RDONLY)) { + /* + * Skip ro check for snap since snap is always ro regardless + * ro flag is passed by mount or not. + */ + deactivate_locked_super(sb); + return (-SET_ERROR(EBUSY)); + } + struct dentry *root = dget(sb->s_root); if (IS_ERR(root)) return (PTR_ERR(root));