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
This commit is contained in:
Ryan Stone 2011-07-17 21:53:42 +00:00
parent 1d9b2fdb21
commit 05a4755ef6

View File

@ -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;
#ifdef HWPMC_HOOKS
pkm.pm_file = lf->filename;
pkm.pm_address = (uintptr_t) lf->address;
PMC_CALL_HOOK(td, PMC_FN_KLD_LOAD, (void *) &pkm);
#endif
if (error) {
KLD_UNLOCK();
goto done;
}
lf->userrefs++;
if (fileid != NULL)
*fileid = lf->id;
unlock:
#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);
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?"));