summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_linker.c
diff options
context:
space:
mode:
authorrstone <rstone@FreeBSD.org>2011-07-17 21:53:42 +0000
committerrstone <rstone@FreeBSD.org>2011-07-17 21:53:42 +0000
commit83ed8794e4fd7b29c3fba10fbbac291a66ad688b (patch)
treee58330870a6cda57b0f6514ebf9d5ea6a14f9f64 /sys/kern/kern_linker.c
parent3ea31a73ff82044669e975939fa1a08c13dd93c0 (diff)
downloadFreeBSD-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
Diffstat (limited to 'sys/kern/kern_linker.c')
-rw-r--r--sys/kern/kern_linker.c35
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?"));
OpenPOWER on IntegriCloud