diff options
author | rstone <rstone@FreeBSD.org> | 2011-07-17 21:53:42 +0000 |
---|---|---|
committer | rstone <rstone@FreeBSD.org> | 2011-07-17 21:53:42 +0000 |
commit | 83ed8794e4fd7b29c3fba10fbbac291a66ad688b (patch) | |
tree | e58330870a6cda57b0f6514ebf9d5ea6a14f9f64 | |
parent | 3ea31a73ff82044669e975939fa1a08c13dd93c0 (diff) | |
download | FreeBSD-src-83ed8794e4fd7b29c3fba10fbbac291a66ad688b.zip FreeBSD-src-83ed8794e4fd7b29c3fba10fbbac291a66ad688b.tar.gz |
Fix a LOR between hwpmc and the kernel linker. When a system-wide
sampling mode PMC is allocated, hwpmc calls linker_hwpmc_list_objects()
while already holding an exclusive lock on pmc-sx lock. list_objects()
tries to acquire an exclusive lock on the kld_sx lock. When a KLD module
is loaded or unloaded successfully, kern_kld(un)load calls into the pmc
hook while already holding an exclusive lock on the kld_sx lock. Calling
the pmc hook requires acquiring a shared lock on the pmc-sx lock.
Fix this by only acquiring a shared lock on the kld_sx lock in
linker_hwpmc_list_objects(), and also downgrading to a shared lock on the
kld_sx lock in kern_kld(un)load before calling into the pmc hook. In
kern_kldload this required moving some modifications of the linker_file_t
to happen before calling into the pmc hook.
This fixes the deadlock by ensuring that the hwpmc -> list_objects() case
is always able to proceed. Without this patch, I was able to deadlock a
multicore system within minutes by constantly loading and unloading an KLD
module while I simultaneously started a sampling mode PMC in a loop.
MFC after: 1 month
-rw-r--r-- | sys/kern/kern_linker.c | 35 |
1 files changed, 24 insertions, 11 deletions
diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c index 86a14ae..f919254 100644 --- a/sys/kern/kern_linker.c +++ b/sys/kern/kern_linker.c @@ -70,6 +70,9 @@ SYSCTL_INT(_debug, OID_AUTO, kld_debug, CTLFLAG_RW, #define KLD_LOCK() sx_xlock(&kld_sx) #define KLD_UNLOCK() sx_xunlock(&kld_sx) +#define KLD_DOWNGRADE() sx_downgrade(&kld_sx) +#define KLD_LOCK_READ() sx_slock(&kld_sx) +#define KLD_UNLOCK_READ() sx_sunlock(&kld_sx) #define KLD_LOCKED() sx_xlocked(&kld_sx) #define KLD_LOCK_ASSERT() do { \ if (!cold) \ @@ -1019,18 +1022,24 @@ kern_kldload(struct thread *td, const char *file, int *fileid) KLD_LOCK(); error = linker_load_module(kldname, modname, NULL, NULL, &lf); - if (error) - goto unlock; + if (error) { + KLD_UNLOCK(); + goto done; + } + lf->userrefs++; + if (fileid != NULL) + *fileid = lf->id; #ifdef HWPMC_HOOKS + KLD_DOWNGRADE(); pkm.pm_file = lf->filename; pkm.pm_address = (uintptr_t) lf->address; PMC_CALL_HOOK(td, PMC_FN_KLD_LOAD, (void *) &pkm); -#endif - lf->userrefs++; - if (fileid != NULL) - *fileid = lf->id; -unlock: + KLD_UNLOCK_READ(); +#else KLD_UNLOCK(); +#endif + +done: CURVNET_RESTORE(); return (error); } @@ -1102,10 +1111,14 @@ kern_kldunload(struct thread *td, int fileid, int flags) error = ENOENT; #ifdef HWPMC_HOOKS - if (error == 0) + if (error == 0) { + KLD_DOWNGRADE(); PMC_CALL_HOOK(td, PMC_FN_KLD_UNLOAD, (void *) &pkm); + KLD_UNLOCK_READ(); + } else +#else + KLD_UNLOCK(); #endif - KLD_UNLOCK(); CURVNET_RESTORE(); return (error); } @@ -1932,7 +1945,7 @@ linker_hwpmc_list_objects(void) int i, nmappings; nmappings = 0; - KLD_LOCK(); + KLD_LOCK_READ(); TAILQ_FOREACH(lf, &linker_files, link) nmappings++; @@ -1947,7 +1960,7 @@ linker_hwpmc_list_objects(void) kobase[i].pm_address = (uintptr_t)lf->address; i++; } - KLD_UNLOCK(); + KLD_UNLOCK_READ(); KASSERT(i > 0, ("linker_hpwmc_list_objects: no kernel objects?")); |