{
zfsvfs_t *zfsvfs = s->s_fs_info;
objset_t *os = data;
- int match;
-
/*
* If the os doesn't match the z_os in the super_block, assume it is
* not a match. Matching would imply a multimount of a dataset. It is
* that changes the z_os, e.g., rollback, where the match will be
* missed, but in that case the user will get an EBUSY.
*/
- if (zfsvfs == NULL || os != zfsvfs->z_os)
- return (0);
-
- /*
- * If they do match, recheck with the lock held to prevent mounting the
- * wrong dataset since z_os can be stale when the teardown lock is held.
- */
- if (zpl_enter(zfsvfs, FTAG) != 0)
- return (0);
- match = (os == zfsvfs->z_os);
- zpl_exit(zfsvfs, FTAG);
-
- return (match);
+ return (zfsvfs != NULL && os == zfsvfs->z_os);
}
static struct super_block *
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);
+ 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) {