vfs: make vfscount refcounting handle possible kld unload

Make the refcounting occur outside any code that might deref a pointer
to struct vfsconf.

Increment vfc_refcount in vfs_byname(), under vfsconf_lock.
Unref either on early mount failure, before struct mount is
instantiated, or in vfs_mount_destroy().

Reported and reviewed by:	kevans
Tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2025-08-28 01:24:01 +03:00
parent 959806e0a8
commit be8bae6700
3 changed files with 20 additions and 4 deletions

View file

@ -103,6 +103,16 @@ struct vattr va_null;
* Routines having to do with the management of the vnode table.
*/
void
vfs_unref_vfsconf(struct vfsconf *vfsp)
{
vfsconf_lock();
KASSERT(vfsp->vfc_refcount > 0,
("vfs %p refcount underflow %d", vfsp, vfsp->vfc_refcount));
vfsp->vfc_refcount--;
vfsconf_unlock();
}
static struct vfsconf *
vfs_byname_locked(const char *name)
{
@ -123,9 +133,11 @@ vfs_byname(const char *name)
{
struct vfsconf *vfsp;
vfsconf_slock();
vfsconf_lock();
vfsp = vfs_byname_locked(name);
vfsconf_sunlock();
if (vfsp != NULL)
vfsp->vfc_refcount++;
vfsconf_unlock();
return (vfsp);
}

View file

@ -683,7 +683,6 @@ vfs_mount_alloc(struct vnode *vp, struct vfsconf *vfsp, const char *fspath,
MPASSERT(mp->mnt_vfs_ops == 1, mp,
("vfs_ops should be 1 but %d found", mp->mnt_vfs_ops));
(void) vfs_busy(mp, MBF_NOWAIT);
atomic_add_acq_int(&vfsp->vfc_refcount, 1);
mp->mnt_op = vfsp->vfc_vfsops;
mp->mnt_vfc = vfsp;
mp->mnt_stat.f_type = vfsp->vfc_typenum;
@ -731,7 +730,6 @@ vfs_mount_destroy(struct mount *mp)
__FILE__, __LINE__));
MPPASS(mp->mnt_writeopcount == 0, mp);
MPPASS(mp->mnt_secondary_writes == 0, mp);
atomic_subtract_rel_int(&mp->mnt_vfc->vfc_refcount, 1);
if (!TAILQ_EMPTY(&mp->mnt_nvnodelist)) {
struct vnode *vp;
@ -769,6 +767,9 @@ vfs_mount_destroy(struct mount *mp)
vfs_free_addrlist(mp->mnt_export);
free(mp->mnt_export, M_MOUNT);
}
vfsconf_lock();
mp->mnt_vfc->vfc_refcount--;
vfsconf_unlock();
crfree(mp->mnt_cred);
uma_zfree(mount_zone, mp);
}
@ -1133,6 +1134,7 @@ vfs_domount_first(
if (jailed(td->td_ucred) && (!prison_allow(td->td_ucred,
vfsp->vfc_prison_flag) || vp == td->td_ucred->cr_prison->pr_root)) {
vput(vp);
vfs_unref_vfsconf(vfsp);
return (EPERM);
}
@ -1169,6 +1171,7 @@ vfs_domount_first(
}
if (error != 0) {
vput(vp);
vfs_unref_vfsconf(vfsp);
return (error);
}
vn_seqc_write_begin(vp);

View file

@ -1007,6 +1007,7 @@ struct mntarg *mount_argsu(struct mntarg *ma, const char *name, const void *val,
void statfs_scale_blocks(struct statfs *sf, long max_size);
struct vfsconf *vfs_byname(const char *);
struct vfsconf *vfs_byname_kld(const char *, struct thread *td, int *);
void vfs_unref_vfsconf(struct vfsconf *vfsp);
void vfs_mount_destroy(struct mount *);
void vfs_event_signal(fsid_t *, u_int32_t, intptr_t);
void vfs_freeopts(struct vfsoptlist *opts);