diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 48958e2..c752880 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -823,6 +824,20 @@ static struct kproc_desc vnlru_kp = { SYSINIT(vnlru, SI_SUB_KTHREAD_UPDATE, SI_ORDER_FIRST, kproc_start, &vnlru_kp); +MALLOC_DEFINE(M_RECORD_REF, "recordref", "recordref"); +static void +v_record_ref(struct vnode *vp, int val, const char *op) +{ + struct debug_ref *r; + + r = malloc(sizeof(struct debug_ref), M_RECORD_REF, M_NOWAIT | + M_USE_RESERVE); + r->val = val; + r->op = op; + stack_save(&r->stack); + TAILQ_INSERT_TAIL(&vp->v_debug_ref, r, link); +} + /* * Routines having to do with the management of the vnode table. */ @@ -831,6 +846,7 @@ void vdestroy(struct vnode *vp) { struct bufobj *bo; + struct debug_ref *r, *r1; CTR2(KTR_VFS, "%s: vp %p", __func__, vp); mtx_lock(&vnode_free_list_mtx); @@ -865,6 +881,9 @@ vdestroy(struct vnode *vp) lockdestroy(vp->v_vnlock); mtx_destroy(&vp->v_interlock); mtx_destroy(BO_MTX(bo)); + TAILQ_FOREACH_SAFE(r, &vp->v_debug_ref, link, r1) { + free(r, M_RECORD_REF); + } uma_zfree(vnode_zone, vp); } @@ -1017,6 +1036,8 @@ alloc: vp->v_vflag |= VV_NOKNOTE; } rangelock_init(&vp->v_rl); + TAILQ_INIT(&vp->v_debug_ref); + v_record_ref(vp, 1, "getnewvnode"); *vpp = vp; return (0); @@ -2114,6 +2135,7 @@ vget(struct vnode *vp, int flags, struct thread *td) vp->v_iflag &= ~VI_OWEINACT; } VI_UNLOCK(vp); + v_record_ref(vp, 1, "vget"); return (0); } @@ -2128,6 +2150,7 @@ vref(struct vnode *vp) VI_LOCK(vp); v_incr_usecount(vp); VI_UNLOCK(vp); + v_record_ref(vp, 1, "vref"); } /* @@ -2164,6 +2187,7 @@ vrele(struct vnode *vp) KASSERT(vp != NULL, ("vrele: null vp")); VFS_ASSERT_GIANT(vp->v_mount); + v_record_ref(vp, -1, "vrele"); VI_LOCK(vp); /* Skip this v_writecount check if we're going to panic below. */ @@ -2225,6 +2249,7 @@ vput(struct vnode *vp) ASSERT_VOP_LOCKED(vp, "vput"); VFS_ASSERT_GIANT(vp->v_mount); CTR2(KTR_VFS, "%s: vp %p", __func__, vp); + v_record_ref(vp, -1, "vput"); VI_LOCK(vp); /* Skip this v_writecount check if we're going to panic below. */ VNASSERT(vp->v_writecount < vp->v_usecount || vp->v_usecount < 1, vp, @@ -2652,6 +2677,8 @@ vn_printf(struct vnode *vp, const char *fmt, ...) va_list ap; char buf[256], buf2[16]; u_long flags; + int ref; + struct debug_ref *r; va_start(ap, fmt); vprintf(fmt, ap); @@ -2720,6 +2747,12 @@ vn_printf(struct vnode *vp, const char *fmt, ...) lockmgr_printinfo(vp->v_vnlock); if (vp->v_data != NULL) VOP_PRINT(vp); + ref = 0; + TAILQ_FOREACH(r, &vp->v_debug_ref, link) { + ref += r->val; + printf("REF %d %s\n", ref, r->op); + stack_print(&r->stack); + } } #ifdef DDB diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index e6ce792..48c912c 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -97,6 +97,13 @@ struct vpollinfo { #if defined(_KERNEL) || defined(_KVM_VNODE) +struct debug_ref { + TAILQ_ENTRY(debug_ref) link; + int val; + const char *op; + struct stack stack; +}; + struct vnode { /* * Fields which define the identity of the vnode. These fields are @@ -171,6 +178,7 @@ struct vnode { struct label *v_label; /* MAC label for vnode */ struct lockf *v_lockf; /* Byte-level adv lock list */ struct rangelock v_rl; /* Byte-range lock */ + TAILQ_HEAD(, debug_ref) v_debug_ref; }; #endif /* defined(_KERNEL) || defined(_KVM_VNODE) */