commit a08705c1cbdf518609be8f1fcd648aae76141ca2 Author: Ryota Ozaki Date: Wed Apr 17 18:39:54 2019 +0900 Implement an aggressive psref leak detector diff --git a/sys/conf/files b/sys/conf/files index 4aaf000fb8d..a60e54c74e4 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -315,6 +315,7 @@ defparam opt_kgdb.h KGDB_DEV KGDB_DEVNAME KGDB_DEVPORT defflag LOCKDEBUG defflag SYSCALL_DEBUG defflag opt_kstack.h KSTACK_CHECK_MAGIC +defflag PSREF_DEBUG # memory (ram) disk options # diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index f9a1bf384b7..d658c7adaca 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -195,6 +195,7 @@ extern void *_binary_splash_image_end; #include #include #include +#include #include #include @@ -393,6 +394,9 @@ main(void) procinit(); lwpinit(); + /* Must be called after lwpinit (lwpinit_specificdata) */ + psref_init(); + /* Initialize signal-related data structures. */ signal_init(); diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 93b34428a38..97217c9aa8c 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -105,6 +105,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_exit.c,v 1.274 2019/03/01 09:02:03 hannken Exp #include #include #include +#include #include diff --git a/sys/kern/kern_lwp.c b/sys/kern/kern_lwp.c index fa7a5e6972b..5a67a575d69 100644 --- a/sys/kern/kern_lwp.c +++ b/sys/kern/kern_lwp.c @@ -242,6 +242,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.200 2019/05/03 22:34:21 kamil Exp $") #include #include #include +#include #include #include @@ -878,6 +879,7 @@ lwp_create(lwp_t *l1, proc_t *p2, vaddr_t uaddr, int flags, cv_init(&l2->l_sigcv, "sigwait"); cv_init(&l2->l_waitcv, "vfork"); l2->l_syncobj = &sched_syncobj; + PSREF_DEBUG_INIT_LWP(l2); if (rnewlwpp != NULL) *rnewlwpp = l2; diff --git a/sys/kern/kern_softint.c b/sys/kern/kern_softint.c index bf18826920b..f4659cad459 100644 --- a/sys/kern/kern_softint.c +++ b/sys/kern/kern_softint.c @@ -604,6 +604,8 @@ softint_execute(softint_t *si, lwp_t *l, int s) sh->sh_flags ^= SOFTINT_ACTIVE; } + PSREF_DEBUG_BARRIER(); + if (havelock) { KERNEL_UNLOCK_ONE(l); } diff --git a/sys/kern/subr_lwp_specificdata.c b/sys/kern/subr_lwp_specificdata.c index 8f79f9b0d16..acf902dd86d 100644 --- a/sys/kern/subr_lwp_specificdata.c +++ b/sys/kern/subr_lwp_specificdata.c @@ -129,3 +129,11 @@ lwp_setspecific(specificdata_key_t key, void *data) specificdata_setspecific(lwp_specificdata_domain, &curlwp->l_specdataref, key, data); } + +void +lwp_setspecific_by_lwp(struct lwp *l, specificdata_key_t key, void *data) +{ + + specificdata_setspecific(lwp_specificdata_domain, + &l->l_specdataref, key, data); +} diff --git a/sys/kern/subr_psref.c b/sys/kern/subr_psref.c index 0fa7142371b..cedca4b1cd6 100644 --- a/sys/kern/subr_psref.c +++ b/sys/kern/subr_psref.c @@ -77,6 +77,7 @@ __KERNEL_RCSID(0, "$NetBSD: subr_psref.c,v 1.12 2019/04/19 01:52:55 ozaki-r Exp #include #include #include +#include SLIST_HEAD(psref_head, psref); @@ -107,6 +108,44 @@ struct psref_cpu { struct psref_head pcpu_head; }; +/* + * Data structures and functions for debugging. + */ +#ifndef PSREF_DEBUG_NITEMS +#define PSREF_DEBUG_NITEMS 16 +#endif + +struct psref_debug_item { + void *prdi_caller; + struct psref *prdi_psref; +}; + +struct psref_debug { + int prd_refs_peek; + struct psref_debug_item prd_items[PSREF_DEBUG_NITEMS]; +}; + +#ifdef PSREF_DEBUG +static void psref_debug_acquire(struct psref *); +static void psref_debug_release(struct psref *); + +static void psref_debug_lwp_free(void *); + +static specificdata_key_t psref_debug_lwp_key; +#endif + +/* + * psref_init() + */ +void +psref_init(void) +{ + +#ifdef PSREF_DEBUG + lwp_specific_key_create(&psref_debug_lwp_key, psref_debug_lwp_free); +#endif +} + /* * psref_class_create(name, ipl) * @@ -279,9 +318,12 @@ psref_acquire(struct psref *psref, const struct psref_target *target, percpu_putref(class->prc_percpu); splx(s); -#ifdef DIAGNOSTIC +#if defined(DIAGNOSTIC) || defined(PSREF_DEBUG) curlwp->l_psrefs++; #endif +#ifdef PSREF_DEBUG + psref_debug_acquire(psref); +#endif } /* @@ -336,10 +378,13 @@ psref_release(struct psref *psref, const struct psref_target *target, percpu_putref(class->prc_percpu); splx(s); -#ifdef DIAGNOSTIC +#if defined(DIAGNOSTIC) || defined(PSREF_DEBUG) KASSERT(curlwp->l_psrefs > 0); curlwp->l_psrefs--; #endif +#ifdef PSREF_DEBUG + psref_debug_release(psref); +#endif /* If someone is waiting for users to drain, notify 'em. */ if (__predict_false(target->prt_draining)) @@ -398,7 +443,7 @@ psref_copy(struct psref *pto, const struct psref *pfrom, percpu_putref(class->prc_percpu); splx(s); -#ifdef DIAGNOSTIC +#if defined(DIAGNOSTIC) || defined(PSREF_DEBUG) curlwp->l_psrefs++; #endif } @@ -565,3 +610,90 @@ psref_held(const struct psref_target *target, struct psref_class *class) return _psref_held(target, class, false); } + +#ifdef PSREF_DEBUG +void +psref_debug_init_lwp(struct lwp *l) +{ + struct psref_debug *prd; + + prd = kmem_zalloc(sizeof(*prd), KM_SLEEP); + lwp_setspecific_by_lwp(l, psref_debug_lwp_key, prd); +} + +static void +psref_debug_lwp_free(void *arg) +{ + struct psref_debug *prd = arg; + + kmem_free(prd, sizeof(*prd)); +} + +static void +psref_debug_acquire(struct psref *psref) +{ + struct psref_debug *prd; + struct lwp *l = curlwp; + int s, i; + + prd = lwp_getspecific(psref_debug_lwp_key); + if (__predict_false(prd == NULL)) { + psref->psref_debug = NULL; + return; + } + + s = splserial(); + if (l->l_psrefs > prd->prd_refs_peek) { + prd->prd_refs_peek = l->l_psrefs; + if (__predict_false(prd->prd_refs_peek > PSREF_DEBUG_NITEMS)) + panic("exceeded PSREF_DEBUG_NITEMS"); + } + for (i = 0; i < prd->prd_refs_peek; i++) { + struct psref_debug_item *prdi = &prd->prd_items[i]; + if (prdi->prdi_psref != NULL) + continue; + prdi->prdi_caller = psref->psref_debug; + prdi->prdi_psref = psref; + psref->psref_debug = prdi; + break; + } + if (__predict_false(i == prd->prd_refs_peek)) + panic("out of range: %d", i); + splx(s); +} + +static void +psref_debug_release(struct psref *psref) +{ + int s; + + s = splserial(); + if (__predict_true(psref->psref_debug != NULL)) { + struct psref_debug_item *prdi = psref->psref_debug; + prdi->prdi_psref = NULL; + } + splx(s); +} + +void +psref_debug_barrier(void) +{ + struct psref_debug *prd; + struct lwp *l = curlwp; + int s, i; + + prd = lwp_getspecific(psref_debug_lwp_key); + if (__predict_false(prd == NULL)) + return; + + s = splserial(); + for (i = 0; i < prd->prd_refs_peek; i++) { + struct psref_debug_item *prdi = &prd->prd_items[i]; + if (__predict_true(prdi->prdi_psref == NULL)) + continue; + panic("psref leaked: lwp(%p) acquired at %p", l, prdi->prdi_caller); + } + prd->prd_refs_peek = 0; /* Reset the counter */ + splx(s); +} +#endif /* PSREF_DEBUG */ diff --git a/sys/net/if.c b/sys/net/if.c index 4404e22c051..8456ad1c6e9 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1863,6 +1863,7 @@ void ifa_acquire(struct ifaddr *ifa, struct psref *psref) { + PSREF_DEBUG_FILL_RETURN_ADDRESS(psref); psref_acquire(psref, &ifa->ifa_psref, ifa_psref_class); } @@ -2739,6 +2740,7 @@ if_get(const char *name, struct psref *psref) if (if_is_deactivated(ifp)) continue; if (strcmp(ifp->if_xname, name) == 0) { + PSREF_DEBUG_FILL_RETURN_ADDRESS(psref); psref_acquire(psref, &ifp->if_psref, ifnet_psref_class); goto out; @@ -2802,8 +2804,10 @@ if_get_byindex(u_int idx, struct psref *psref) s = pserialize_read_enter(); ifp = if_byindex(idx); - if (__predict_true(ifp != NULL)) + if (__predict_true(ifp != NULL)) { + PSREF_DEBUG_FILL_RETURN_ADDRESS(psref); psref_acquire(psref, &ifp->if_psref, ifnet_psref_class); + } pserialize_read_exit(s); return ifp; diff --git a/sys/net/route.c b/sys/net/route.c index a64374d8d14..baa588f5aa9 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -2077,6 +2077,8 @@ rtcache_ref(struct rtentry *rt, struct route *ro) #ifdef NET_MPSAFE RTCACHE_PSREF_TRACE(rt, ro); ro->ro_bound = curlwp_bind(); + /* XXX Use a real caller's address */ + PSREF_DEBUG_FILL_RETURN_ADDRESS(&ro->ro_psref); psref_acquire(&ro->ro_psref, &rt->rt_psref, rt_psref_class); #endif } diff --git a/sys/rump/kern/lib/libsysproxy/sysproxy.c b/sys/rump/kern/lib/libsysproxy/sysproxy.c index c0264b58f14..9b5f5fe689a 100644 --- a/sys/rump/kern/lib/libsysproxy/sysproxy.c +++ b/sys/rump/kern/lib/libsysproxy/sysproxy.c @@ -36,6 +36,7 @@ __KERNEL_RCSID(0, "$NetBSD: sysproxy.c,v 1.6 2019/04/19 01:52:55 ozaki-r Exp $") #include #include #include +#include #define _RUMP_SYSPROXY #include @@ -77,6 +78,7 @@ hyp_syscall(int num, void *arg, long *retval) /* Sanity checks (from mi_userret) */ LOCKDEBUG_BARRIER(NULL, 0); KASSERT(l->l_nopreempt == 0); + PSREF_DEBUG_BARRIER(); KASSERT(l->l_psrefs == 0); return rv; diff --git a/sys/rump/librump/rumpkern/lwproc.c b/sys/rump/librump/rumpkern/lwproc.c index 224b499fd9f..1ac7f2e3745 100644 --- a/sys/rump/librump/rumpkern/lwproc.c +++ b/sys/rump/librump/rumpkern/lwproc.c @@ -43,6 +43,7 @@ __KERNEL_RCSID(0, "$NetBSD: lwproc.c,v 1.41 2019/03/09 09:02:38 hannken Exp $"); #include #include #include +#include #include @@ -372,6 +373,7 @@ lwproc_makelwp(struct proc *p, struct lwp *l, bool doswitch, bool procmake) lwp_update_creds(l); lwp_initspecific(l); + PSREF_DEBUG_INIT_LWP(l); membar_enter(); lwproc_curlwpop(RUMPUSER_LWP_CREATE, l); diff --git a/sys/rump/librump/rumpkern/rump.c b/sys/rump/librump/rumpkern/rump.c index 17a91a777c8..074fadf4539 100644 --- a/sys/rump/librump/rumpkern/rump.c +++ b/sys/rump/librump/rumpkern/rump.c @@ -74,6 +74,7 @@ __KERNEL_RCSID(0, "$NetBSD: rump.c,v 1.333 2019/03/29 02:09:14 christos Exp $"); #include #include #include +#include #include #include @@ -344,6 +345,9 @@ rump_init(void) lwpinit_specificdata(); lwp_initspecific(&lwp0); + /* Must be called after lwpinit_specificdata */ + psref_init(); + threadpools_init(); loginit(); diff --git a/sys/sys/lwp.h b/sys/sys/lwp.h index ef0f02aaeed..218cfeb7b7c 100644 --- a/sys/sys/lwp.h +++ b/sys/sys/lwp.h @@ -357,6 +357,7 @@ void *lwp_getspecific(specificdata_key_t); void *_lwp_getspecific_by_lwp(lwp_t *, specificdata_key_t); #endif void lwp_setspecific(specificdata_key_t, void *); +void lwp_setspecific_by_lwp(lwp_t *, specificdata_key_t, void *); /* Syscalls. */ int lwp_park(clockid_t, int, struct timespec *, const void *); diff --git a/sys/sys/psref.h b/sys/sys/psref.h index 7257587777f..45acda1e761 100644 --- a/sys/sys/psref.h +++ b/sys/sys/psref.h @@ -32,6 +32,10 @@ #ifndef _SYS_PSREF_H #define _SYS_PSREF_H +#ifdef _KERNEL_OPT +#include "opt_psref_debug.h" +#endif + #include #include @@ -70,14 +74,15 @@ struct psref_target { */ struct psref { SLIST_ENTRY(psref) psref_entry; - /* To keep ABI with LIST_ENTRY(psref) version. */ - void *psref_unused0; + void *psref_debug; /* For debugging */ const struct psref_target *psref_target; struct lwp *psref_lwp; struct cpu_info *psref_cpu; }; #ifdef _KERNEL +void psref_init(void); + struct psref_class * psref_class_create(const char *, int); void psref_class_destroy(struct psref_class *); @@ -94,6 +99,30 @@ void psref_copy(struct psref *, const struct psref *, /* For use only in assertions. */ bool psref_held(const struct psref_target *, struct psref_class *); -#endif + + +#ifdef PSREF_DEBUG +void psref_debug_barrier(void); +void psref_debug_init_lwp(struct lwp *); + +#define PSREF_DEBUG_BARRIER() psref_debug_barrier() +#define PSREF_DEBUG_INIT_LWP(l) psref_debug_init_lwp((l)) +#define PSREF_DEBUG_FILL_RETURN_ADDRESS0(psref, addr) do { \ + (psref)->psref_debug = (addr); \ +} while (0) +#define PSREF_DEBUG_FILL_RETURN_ADDRESS(psref) do { \ + PSREF_DEBUG_FILL_RETURN_ADDRESS0(psref, __builtin_return_address(0));\ +} while (0) + +#else + +#define PSREF_DEBUG_BARRIER() __nothing +#define PSREF_DEBUG_INIT_LWP(l) __nothing +#define PSREF_DEBUG_FILL_RETURN_ADDRESS0(psref, addr) __nothing +#define PSREF_DEBUG_FILL_RETURN_ADDRESS(psref) __nothing + +#endif /* PSREF_DEBUG */ + +#endif /* _KERNEL */ #endif /* _SYS_PSREF_H */ diff --git a/sys/sys/userret.h b/sys/sys/userret.h index f3a9991be6d..3bc2531b4ca 100644 --- a/sys/sys/userret.h +++ b/sys/sys/userret.h @@ -67,6 +67,7 @@ #include #include +#include /* * Define the MI code needed before returning to user mode, for @@ -114,6 +115,7 @@ mi_userret(struct lwp *l) LOCKDEBUG_BARRIER(NULL, 0); KASSERT(l->l_nopreempt == 0); + PSREF_DEBUG_BARRIER(); KASSERT(l->l_psrefs == 0); }