diff --git a/sys/vfs/hammer2/hammer2_ondisk.c b/sys/vfs/hammer2/hammer2_ondisk.c --- a/sys/vfs/hammer2/hammer2_ondisk.c +++ b/sys/vfs/hammer2/hammer2_ondisk.c @@ -222,8 +222,14 @@ TAILQ_REMOVE(devvpl, e, entry); /* devvp */ KKASSERT(e->devvp); - if (e->devvp->v_rdev) - e->devvp->v_rdev->si_mountpoint = NULL; + /* + * NOTE: Do not clear v_rdev->si_mountpoint here. This list + * may reference devices owned by a still-live hammer2 mount + * or by another filesystem entirely (see the secondary-mount + * and failed-mount paths in hammer2_vfsops.c). The marker + * lifecycle is managed by hammer2_devvp_mount() / + * hammer2_devvp_unmount() instead. + */ vrele(e->devvp); e->devvp = NULL; /* path */ @@ -234,6 +240,59 @@ } } +/* + * Set the mountpoint marker on all devices belonging to hmp. Called when + * a mount is established on the device(s), repointing the marker at the + * most recent mount so it never references a freed struct mount. + */ +void +hammer2_devvp_mount(hammer2_dev_t *hmp, struct mount *mp) +{ + hammer2_devvp_t *e; + + TAILQ_FOREACH(e, &hmp->devvpl, entry) { + if (e->devvp && e->devvp->v_rdev) + e->devvp->v_rdev->si_mountpoint = mp; + } +} + +/* + * Clear the mountpoint marker on all devices belonging to hmp, but only + * where it still points at the specified mount (ownership check, same as + * hammer1's hammer_unload_volume()). This prevents cleanup code running + * against devices owned by other filesystems, or against devices backing + * a still-live hammer2 mount, from stripping their protection. + */ +void +hammer2_devvp_unmount(hammer2_dev_t *hmp, struct mount *mp) +{ + hammer2_devvp_t *e; + + TAILQ_FOREACH(e, &hmp->devvpl, entry) { + if (e->devvp && e->devvp->v_rdev && + e->devvp->v_rdev->si_mountpoint == mp) + e->devvp->v_rdev->si_mountpoint = NULL; + } +} + +/* + * Unconditionally clear the mountpoint marker on all devices belonging to + * hmp. Only used at final device teardown (mount_count == 0), where no + * hammer2 mount remains and any marker left on these devices was placed + * there by hammer2 (possibly stale from an earlier mount). + */ +void +hammer2_devvp_unmount_all(hammer2_dev_t *hmp) +{ + hammer2_devvp_t *e; + + TAILQ_FOREACH(e, &hmp->devvpl, entry) { + if (e->devvp && e->devvp->v_rdev && + e->devvp->v_rdev->si_mountpoint != NULL) + e->devvp->v_rdev->si_mountpoint = NULL; + } +} + static int hammer2_verify_volumes_common(const hammer2_volume_t *volumes, const hammer2_volume_data_t *rootvoldata) @@ -702,6 +761,17 @@ if (!error) error = hammer2_verify_volumes(volumes, rootvoldata); } + if (error) { + /* + * On failure clear only the markers we placed ourselves; + * hammer2_cleanup_devvp() no longer touches si_mountpoint. + */ + TAILQ_FOREACH(e, devvpl, entry) { + if (e->devvp && e->devvp->v_rdev && + e->devvp->v_rdev->si_mountpoint == mp) + e->devvp->v_rdev->si_mountpoint = NULL; + } + } kfree(voldata, M_HAMMER2); return error; diff --git a/sys/vfs/hammer2/hammer2.h b/sys/vfs/hammer2/hammer2.h --- a/sys/vfs/hammer2/hammer2.h +++ b/sys/vfs/hammer2/hammer2.h @@ -1954,6 +1954,9 @@ hammer2_volume_data_t *rootvoldata, int *rootvolzone, struct vnode **rootvoldevvp); +void hammer2_devvp_mount(hammer2_dev_t *hmp, struct mount *mp); +void hammer2_devvp_unmount(hammer2_dev_t *hmp, struct mount *mp); +void hammer2_devvp_unmount_all(hammer2_dev_t *hmp); hammer2_volume_t *hammer2_get_volume(hammer2_dev_t *hmp, hammer2_off_t offset); /* diff --git a/sys/vfs/hammer2/hammer2_vfsops.c b/sys/vfs/hammer2/hammer2_vfsops.c --- a/sys/vfs/hammer2/hammer2_vfsops.c +++ b/sys/vfs/hammer2/hammer2_vfsops.c @@ -502,8 +502,10 @@ * If the PFS is already mounted we must account * for the mount_count here. */ - if (pmp->mp) + if (pmp->mp) { ++chain->hmp->mount_count; + hammer2_devvp_mount(chain->hmp, pmp->mp); + } /* * May have to fixup dirty chain tracking. Previous @@ -1700,6 +1702,11 @@ if (rchain == NULL) continue; ++rchain->hmp->mount_count; + /* + * (Re)own the device marker for this mount, keeping it + * pointed at a live struct mount. + */ + hammer2_devvp_mount(rchain->hmp, mp); } /* @@ -1753,6 +1760,39 @@ continue; --rchain->hmp->mount_count; /* scrapping hmp now may invalidate the pmp */ + if (rchain->hmp->mount_count) { + /* + * Repoint the device marker at a mount that + * is still live so it never goes stale. + */ + hammer2_pfs_t *pmp2; + int j; + int found = 0; + + TAILQ_FOREACH(pmp2, &hammer2_pfslist, + mntentry) { + if (pmp2->mp == NULL) + continue; + for (j = 0; j < HAMMER2_MAXCLUSTER; + ++j) { + if (pmp2->pfs_hmps[j] == + rchain->hmp) { + hammer2_devvp_mount( + rchain->hmp, + pmp2->mp); + found = 1; + break; + } + } + if (found) + break; + } + if (!found) + hammer2_devvp_unmount(rchain->hmp, mp); + } else { + /* last mount on this device went away */ + hammer2_devvp_unmount(rchain->hmp, mp); + } } again: TAILQ_FOREACH(hmp, &hammer2_mntlist, mntentry) { @@ -1830,6 +1870,14 @@ KKASSERT(hmp->spmp == NULL); /* + * Final device teardown: no hammer2 mounts remain on these devices. + * Drop any (possibly stale) marker we placed on them. Markers owned + * by other filesystems cannot exist here because vfs_mountedon() + * would have blocked their mounts while ours was established. + */ + hammer2_devvp_unmount_all(hmp); + + /* * Finish up with the device vnode */ if (!TAILQ_EMPTY(&hmp->devvpl)) {