diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 48958e2..587346a 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,22 @@ 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; + + if (vp->v_type != VREG && vp->v_type != VBAD) + return; + 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 +848,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 +883,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 +1038,7 @@ alloc: vp->v_vflag |= VV_NOKNOTE; } rangelock_init(&vp->v_rl); + TAILQ_INIT(&vp->v_debug_ref); *vpp = vp; return (0); @@ -2113,6 +2135,7 @@ vget(struct vnode *vp, int flags, struct thread *td) vinactive(vp, td); vp->v_iflag &= ~VI_OWEINACT; } + v_record_ref(vp, 1, "vget"); VI_UNLOCK(vp); return (0); } @@ -2127,6 +2150,7 @@ vref(struct vnode *vp) CTR2(KTR_VFS, "%s: vp %p", __func__, vp); VI_LOCK(vp); v_incr_usecount(vp); + v_record_ref(vp, 1, "vref"); VI_UNLOCK(vp); } @@ -2165,6 +2189,7 @@ vrele(struct vnode *vp) VFS_ASSERT_GIANT(vp->v_mount); VI_LOCK(vp); + v_record_ref(vp, -1, "vrele"); /* Skip this v_writecount check if we're going to panic below. */ VNASSERT(vp->v_writecount < vp->v_usecount || vp->v_usecount < 1, vp, @@ -2226,6 +2251,7 @@ vput(struct vnode *vp) VFS_ASSERT_GIANT(vp->v_mount); CTR2(KTR_VFS, "%s: vp %p", __func__, vp); VI_LOCK(vp); + v_record_ref(vp, -1, "vput"); /* Skip this v_writecount check if we're going to panic below. */ VNASSERT(vp->v_writecount < vp->v_usecount || vp->v_usecount < 1, vp, ("vput: missed vn_close")); @@ -2652,6 +2678,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 +2748,13 @@ vn_printf(struct vnode *vp, const char *fmt, ...) lockmgr_printinfo(vp->v_vnlock); if (vp->v_data != NULL) VOP_PRINT(vp); + /* Getnewvnode() initial reference is not recorded due to VNON */ + ref = 1; + 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) */