Index: sys/contrib/openzfs/module/os/freebsd/zfs/zfs_znode.c =================================================================== --- sys/contrib/openzfs/module/os/freebsd/zfs/zfs_znode.c +++ sys/contrib/openzfs/module/os/freebsd/zfs/zfs_znode.c @@ -540,6 +540,7 @@ * Acquire vnode lock before making it available to the world. */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + vn_set_state(vp, VSTATE_CONSTRUCTED); VN_LOCK_AREC(vp); if (vp->v_type != VFIFO) VN_LOCK_ASHARE(vp); Index: sys/fs/autofs/autofs_vnops.c =================================================================== --- sys/fs/autofs/autofs_vnops.c +++ sys/fs/autofs/autofs_vnops.c @@ -704,6 +704,7 @@ sx_xunlock(&anp->an_vnode_lock); + vn_set_state(vp, VSTATE_CONSTRUCTED); *vpp = vp; return (0); } Index: sys/fs/cd9660/cd9660_vfsops.c =================================================================== --- sys/fs/cd9660/cd9660_vfsops.c +++ sys/fs/cd9660/cd9660_vfsops.c @@ -822,6 +822,7 @@ * XXX need generation number? */ + vn_set_state(vp, VSTATE_CONSTRUCTED); *vpp = vp; return (0); } Index: sys/fs/devfs/devfs_vnops.c =================================================================== --- sys/fs/devfs/devfs_vnops.c +++ sys/fs/devfs/devfs_vnops.c @@ -613,6 +613,7 @@ return (error); } if (devfs_allocv_drop_refs(0, dmp, de)) { + vgone(vp); vput(vp); return (ENOENT); } @@ -620,6 +621,7 @@ mac_devfs_vnode_associate(mp, de, vp); #endif sx_xunlock(&dmp->dm_lock); + vn_set_state(vp, VSTATE_CONSTRUCTED); *vpp = vp; return (0); } Index: sys/fs/ext2fs/ext2_alloc.c =================================================================== --- sys/fs/ext2fs/ext2_alloc.c +++ sys/fs/ext2fs/ext2_alloc.c @@ -480,6 +480,7 @@ ip->i_birthtime = ts.tv_sec; ip->i_birthnsec = ts.tv_nsec; + vn_set_state(vp, VSTATE_CONSTRUCTED); *vpp = vp; return (0); Index: sys/fs/ext2fs/ext2_vfsops.c =================================================================== --- sys/fs/ext2fs/ext2_vfsops.c +++ sys/fs/ext2fs/ext2_vfsops.c @@ -1308,6 +1308,7 @@ * Finish inode initialization. */ + vn_set_state(vp, VSTATE_CONSTRUCTED); *vpp = vp; return (0); } Index: sys/fs/fdescfs/fdesc_vnops.c =================================================================== --- sys/fs/fdescfs/fdesc_vnops.c +++ sys/fs/fdescfs/fdesc_vnops.c @@ -235,6 +235,7 @@ /* If we came here, we can insert it safely. */ LIST_INSERT_HEAD(fc, fd, fd_hash); mtx_unlock(&fdesc_hashmtx); + vn_set_state(vp, VSTATE_CONSTRUCTED); *vpp = vp; return (0); } Index: sys/fs/fuse/fuse_node.c =================================================================== --- sys/fs/fuse/fuse_node.c +++ sys/fs/fuse/fuse_node.c @@ -263,6 +263,7 @@ if (data->dataflags & FSESS_ASYNC_READ && vtyp != VFIFO) VN_LOCK_ASHARE(*vpp); + vn_set_state(*vpp, VSTATE_CONSTRUCTED); err = vfs_hash_insert(*vpp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, td, &vp2, fuse_vnode_cmp, &nodeid); if (err) { Index: sys/fs/mntfs/mntfs_vnops.c =================================================================== --- sys/fs/mntfs/mntfs_vnops.c +++ sys/fs/mntfs/mntfs_vnops.c @@ -83,6 +83,9 @@ dev_ref(dev); vp->v_rdev = dev; + VOP_UNLOCK(ovp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + vn_set_state(vp, VSTATE_CONSTRUCTED); return (vp); } Index: sys/fs/msdosfs/msdosfs_denode.c =================================================================== --- sys/fs/msdosfs/msdosfs_denode.c +++ sys/fs/msdosfs/msdosfs_denode.c @@ -299,6 +299,7 @@ } } else nvp->v_type = VREG; + vn_set_state(nvp, VSTATE_CONSTRUCTED); ldep->de_modrev = init_va_filerev(); *depp = ldep; return (0); Index: sys/fs/msdosfs/msdosfs_vfsops.c =================================================================== --- sys/fs/msdosfs/msdosfs_vfsops.c +++ sys/fs/msdosfs/msdosfs_vfsops.c @@ -431,8 +431,6 @@ ronly = (mp->mnt_flag & MNT_RDONLY) != 0; devvp = mntfs_allocvp(mp, odevvp); - VOP_UNLOCK(odevvp); - vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); dev = devvp->v_rdev; if (atomic_cmpset_acq_ptr((uintptr_t *)&dev->si_mountpt, 0, (uintptr_t)mp) == 0) { Index: sys/fs/nfsclient/nfs_clnode.c =================================================================== --- sys/fs/nfsclient/nfs_clnode.c +++ sys/fs/nfsclient/nfs_clnode.c @@ -178,6 +178,7 @@ uma_zfree(newnfsnode_zone, np); return (error); } + vn_set_state(vp, VSTATE_CONSTRUCTED); error = vfs_hash_insert(vp, hash, lkflags, td, &nvp, newnfs_vncmpf, np->n_fhp); if (error) Index: sys/fs/nfsclient/nfs_clport.c =================================================================== --- sys/fs/nfsclient/nfs_clport.c +++ sys/fs/nfsclient/nfs_clport.c @@ -286,6 +286,7 @@ uma_zfree(newnfsnode_zone, np); return (error); } + vn_set_state(vp, VSTATE_CONSTRUCTED); error = vfs_hash_insert(vp, hash, lkflags, td, &nvp, newnfs_vncmpf, nfhp); if (error) Index: sys/fs/nullfs/null_subr.c =================================================================== --- sys/fs/nullfs/null_subr.c +++ sys/fs/nullfs/null_subr.c @@ -262,6 +262,7 @@ } null_hashins(mp, xp); + vn_set_state(vp, VSTATE_CONSTRUCTED); rw_wunlock(&null_hash_lock); *vpp = vp; Index: sys/fs/pseudofs/pseudofs_vncache.c =================================================================== --- sys/fs/pseudofs/pseudofs_vncache.c +++ sys/fs/pseudofs/pseudofs_vncache.c @@ -207,6 +207,7 @@ *vpp = NULLVP; return (error); } + vn_set_state(*vpp, VSTATE_CONSTRUCTED); retry2: mtx_lock(&pfs_vncache_mutex); /* Index: sys/fs/smbfs/smbfs_node.c =================================================================== --- sys/fs/smbfs/smbfs_node.c +++ sys/fs/smbfs/smbfs_node.c @@ -217,6 +217,7 @@ free(np, M_SMBNODE); return (error); } + vn_set_state(vp, VSTATE_CONSTRUCTED); error = vfs_hash_insert(vp, smbfs_hash(name, nmlen), LK_EXCLUSIVE, td, &vp2, smbfs_vnode_cmp, &sc); if (error) Index: sys/fs/tmpfs/tmpfs_subr.c =================================================================== --- sys/fs/tmpfs/tmpfs_subr.c +++ sys/fs/tmpfs/tmpfs_subr.c @@ -1080,6 +1080,8 @@ vgone(vp); vput(vp); vp = NULL; + } else { + vn_set_state(vp, VSTATE_CONSTRUCTED); } unlock: Index: sys/fs/udf/udf_vfsops.c =================================================================== --- sys/fs/udf/udf_vfsops.c +++ sys/fs/udf/udf_vfsops.c @@ -717,6 +717,7 @@ if (ino == udf_getid(&udfmp->root_icb)) vp->v_vflag |= VV_ROOT; + vn_set_state(vp, VSTATE_CONSTRUCTED); *vpp = vp; return (0); Index: sys/fs/unionfs/union_subr.c =================================================================== --- sys/fs/unionfs/union_subr.c +++ sys/fs/unionfs/union_subr.c @@ -407,6 +407,8 @@ return (ENOENT); } + vn_set_state(vp, VSTATE_CONSTRUCTED); + if (dvp != NULLVP && vt == VDIR) *vpp = unionfs_ins_cached_vnode(unp, dvp); if (*vpp != NULLVP) { Index: sys/kern/vfs_lookup.c =================================================================== --- sys/kern/vfs_lookup.c +++ sys/kern/vfs_lookup.c @@ -161,6 +161,7 @@ UMA_ALIGN_PTR, 0); vfs_vector_op_register(&crossmp_vnodeops); getnewvnode("crossmp", NULL, &crossmp_vnodeops, &vp_crossmp); + vp_crossmp->v_state = VSTATE_CONSTRUCTED; } SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_SECOND, nameiinit, NULL); Index: sys/kern/vfs_subr.c =================================================================== --- sys/kern/vfs_subr.c +++ sys/kern/vfs_subr.c @@ -603,6 +603,8 @@ vp->v_dbatchcpu = NOCPU; + vp->v_state = VSTATE_DEAD; + /* * Check vhold_recycle_free for an explanation. */ @@ -1792,6 +1794,9 @@ vp = vn_alloc(mp); } counter_u64_add(vnodes_created, 1); + + vn_set_state(vp, VSTATE_UNINITIALIZED); + /* * Locks are given the generic name "vnode" when created. * Follow the historic practice of using the filesystem @@ -4015,14 +4020,18 @@ /* * Don't vgonel if we're already doomed. */ - if (VN_IS_DOOMED(vp)) + if (VN_IS_DOOMED(vp)) { + VNPASS(vn_get_state(vp) == VSTATE_DESTROYING || \ + vn_get_state(vp) == VSTATE_DEAD, vp); return; + } /* * Paired with freevnode. */ vn_seqc_write_begin_locked(vp); vunlazy_gone(vp); vn_irflag_set_locked(vp, VIRF_DOOMED); + vn_set_state(vp, VSTATE_DESTROYING); /* * Check to see if the vnode is in use. If so, we have to @@ -4140,14 +4149,28 @@ vp->v_vnlock = &vp->v_lock; vp->v_op = &dead_vnodeops; vp->v_type = VBAD; + vn_set_state(vp, VSTATE_DEAD); } /* * Print out a description of a vnode. */ -static const char * const typename[] = -{"VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VFIFO", "VBAD", - "VMARKER"}; +static const char *const vtypename[] = { + [VNON] = "VNON", [VREG] = "VREG", [VDIR] = "VDIR", [VBLK] = "VBLK", + [VCHR] = "VCHR", [VLNK] = "VLNK", [VSOCK] = "VSOCK", [VFIFO] = "VFIFO", + [VBAD] = "VBAD", [VMARKER] = "VMARKER" +}; +_Static_assert(nitems(vtypename) == VLASTTYPE + 1, + "vnode type name not added to vtypename"); + +static const char *const vstatename[] = { + [VSTATE_UNINITIALIZED] = "VSTATE_UNINITIALIZED", + [VSTATE_CONSTRUCTED] = "VSTATE_CONSTRUCTED", + [VSTATE_DESTROYING] = "VSTATE_DESTROYING", + [VSTATE_DEAD] = "VSTATE_DEAD", +}; +_Static_assert(nitems(vstatename) == VLASTSTATE + 1, + "vnode state name not added to vstatename"); _Static_assert((VHOLD_ALL_FLAGS & ~VHOLD_NO_SMR) == 0, "new hold count flag not added to vn_printf"); @@ -4165,7 +4188,7 @@ vprintf(fmt, ap); va_end(ap); printf("%p: ", (void *)vp); - printf("type %s\n", typename[vp->v_type]); + printf("type %s state %s\n", vtypename[vp->v_type], vstatename[vp->v_state]); holdcnt = atomic_load_int(&vp->v_holdcnt); printf(" usecount %d, writecount %d, refcount %d seqc users %d", vp->v_usecount, vp->v_writecount, holdcnt & ~VHOLD_ALL_FLAGS, @@ -5063,6 +5086,7 @@ if (error != 0) panic("vfs_allocate_syncvnode: insmntque() failed"); vp->v_vflag &= ~VV_FORCEINSMQ; + vn_set_state(vp, VSTATE_CONSTRUCTED); VOP_UNLOCK(vp); /* * Place the vnode onto the syncer worklist. We attempt to @@ -5709,8 +5733,11 @@ vop_unlock_debugpre(void *ap) { struct vop_unlock_args *a = ap; + struct vnode *vp = a->a_vp; + + VNPASS(vn_get_state(vp) != VSTATE_UNINITIALIZED, vp); - ASSERT_VOP_LOCKED(a->a_vp, "VOP_UNLOCK"); + ASSERT_VOP_LOCKED(vp, "VOP_UNLOCK"); } void @@ -7082,3 +7109,51 @@ vn_irflag_unset_locked(vp, tounset); VI_UNLOCK(vp); } + +#ifdef INVARIANTS +void +vn_set_state_validate(struct vnode *vp, enum vstate state) +{ + + switch (vp->v_state) { + case VSTATE_UNINITIALIZED: + switch (state) { + case VSTATE_CONSTRUCTED: + case VSTATE_DESTROYING: + return; + default: + break; + } + break; + case VSTATE_CONSTRUCTED: + ASSERT_VOP_ELOCKED(vp, __func__); + switch (state) { + case VSTATE_DESTROYING: + return; + default: + break; + } + break; + case VSTATE_DESTROYING: + ASSERT_VOP_ELOCKED(vp, __func__); + switch (state) { + case VSTATE_DEAD: + return; + default: + break; + } + break; + case VSTATE_DEAD: + switch (state) { + case VSTATE_UNINITIALIZED: + return; + default: + break; + } + break; + } + + vn_printf(vp, "invalid state transition %d -> %d\n", vp->v_state, state); + panic("invalid state transition %d -> %d\n", vp->v_state, state); +} +#endif Index: sys/sys/vnode.h =================================================================== --- sys/sys/vnode.h +++ sys/sys/vnode.h @@ -58,6 +58,11 @@ */ enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD, VMARKER }; +#define VLASTTYPE VMARKER + +enum vstate { VSTATE_UNINITIALIZED, VSTATE_CONSTRUCTED, VSTATE_DESTROYING, + VSTATE_DEAD }; +#define VLASTSTATE VSTATE_DEAD enum vgetstate { VGET_NONE, VGET_HOLDCNT, VGET_USECOUNT }; /* @@ -106,6 +111,7 @@ * owned by the filesystem (XXX: and vgone() ?) */ enum vtype v_type:8; /* u vnode type */ + enum vstate v_state:8; /* u vnode state */ short v_irflag; /* i frequently read flags */ seqc_t v_seqc; /* i modification count */ uint32_t v_nchash; /* u namecache hash */ @@ -1135,6 +1141,25 @@ int vn_dir_check_exec(struct vnode *vp, struct componentname *cnp); int vn_lktype_write(struct mount *mp, struct vnode *vp); +#ifdef INVARIANTS +void vn_set_state_validate(struct vnode *vp, enum vstate state); +#endif + +static inline void +vn_set_state(struct vnode *vp, enum vstate state) +{ +#ifdef INVARIANTS + vn_set_state_validate(vp, state); +#endif + vp->v_state = state; +} + +static inline enum vstate +vn_get_state(struct vnode *vp) +{ + return (vp->v_state); +} + #define VOP_UNLOCK_FLAGS(vp, flags) ({ \ struct vnode *_vp = (vp); \ int _flags = (flags); \ Index: sys/ufs/ffs/ffs_vfsops.c =================================================================== --- sys/ufs/ffs/ffs_vfsops.c +++ sys/ufs/ffs/ffs_vfsops.c @@ -925,8 +925,6 @@ ronly = (mp->mnt_flag & MNT_RDONLY) != 0; devvp = mntfs_allocvp(mp, odevvp); - VOP_UNLOCK(odevvp); - vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); KASSERT(devvp->v_type == VCHR, ("reclaimed devvp")); dev = devvp->v_rdev; KASSERT(dev->si_snapdata == NULL, ("non-NULL snapshot data")); @@ -2028,6 +2026,7 @@ } #endif + vn_set_state(vp, VSTATE_CONSTRUCTED); *vpp = vp; return (0); }