Rename the kld_unload event handler to kld_unload_try, and add a new

kld_unload event handler which gets invoked after a linker file has been
successfully unloaded. The kld_unload and kld_load event handlers are now
invoked with the shared linker lock held, while kld_unload_try is invoked
with the lock exclusively held.

Convert hwpmc(4) to use these event handlers instead of having
kern_kldload() and kern_kldunload() invoke hwpmc(4) hooks whenever files are
loaded or unloaded. This has no functional effect, but simplifes the linker
code somewhat.

Reviewed by:	jhb
This commit is contained in:
Mark Johnston 2013-08-24 21:13:38 +00:00
parent ce6169e715
commit 29f4e216f2
9 changed files with 84 additions and 98 deletions

View file

@ -202,6 +202,8 @@ Callbacks invoked when a BPF listener attaches to/detaches from network interfac
.It Vt kld_load
Callbacks invoked after a linker file has been loaded.
.It Vt kld_unload
Callbacks invoked after a linker file has been successfully unloaded.
.It Vt kld_unload_try
Callbacks invoked before a linker file is about to be unloaded.
These callbacks may be used to return an error and prevent the unload from
proceeding.

View file

@ -242,7 +242,7 @@ int dtrace_in_probe; /* non-zero if executing a probe */
uintptr_t dtrace_in_probe_addr; /* Address of invop when already in probe */
#endif
static eventhandler_tag dtrace_kld_load_tag;
static eventhandler_tag dtrace_kld_unload_tag;
static eventhandler_tag dtrace_kld_unload_try_tag;
#endif
/*
@ -15351,7 +15351,7 @@ dtrace_kld_load(void *arg __unused, linker_file_t lf)
}
static void
dtrace_kld_unload(void *arg __unused, linker_file_t lf, int *error)
dtrace_kld_unload_try(void *arg __unused, linker_file_t lf, int *error)
{
if (*error != 0)

View file

@ -59,8 +59,8 @@ dtrace_load(void *dummy)
/* Register callbacks for linker file load and unload events. */
dtrace_kld_load_tag = EVENTHANDLER_REGISTER(kld_load,
dtrace_kld_load, NULL, EVENTHANDLER_PRI_ANY);
dtrace_kld_unload_tag = EVENTHANDLER_REGISTER(kld_unload,
dtrace_kld_unload, NULL, EVENTHANDLER_PRI_ANY);
dtrace_kld_unload_try_tag = EVENTHANDLER_REGISTER(kld_unload_try,
dtrace_kld_unload_try, NULL, EVENTHANDLER_PRI_ANY);
/*
* Initialise the mutexes without 'witness' because the dtrace

View file

@ -68,7 +68,7 @@ dtrace_unload()
dtrace_provider = NULL;
EVENTHANDLER_DEREGISTER(kld_load, dtrace_kld_load_tag);
EVENTHANDLER_DEREGISTER(kld_unload, dtrace_kld_unload_tag);
EVENTHANDLER_DEREGISTER(kld_unload_try, dtrace_kld_unload_try_tag);
if ((state = dtrace_anon_grab()) != NULL) {
/*

View file

@ -59,7 +59,7 @@ static int sdt_unload(void *);
static void sdt_create_provider(struct sdt_provider *);
static void sdt_create_probe(struct sdt_probe *);
static void sdt_kld_load(void *, struct linker_file *);
static void sdt_kld_unload(void *, struct linker_file *, int *);
static void sdt_kld_unload_try(void *, struct linker_file *, int *);
static MALLOC_DEFINE(M_SDT, "SDT", "DTrace SDT providers");
@ -95,7 +95,7 @@ static struct cdev *sdt_cdev;
static TAILQ_HEAD(, sdt_provider) sdt_prov_list;
eventhandler_tag sdt_kld_load_tag;
eventhandler_tag sdt_kld_unload_tag;
eventhandler_tag sdt_kld_unload_try_tag;
static void
sdt_create_provider(struct sdt_provider *prov)
@ -264,7 +264,7 @@ sdt_kld_load(void *arg __unused, struct linker_file *lf)
}
static void
sdt_kld_unload(void *arg __unused, struct linker_file *lf, int *error __unused)
sdt_kld_unload_try(void *arg __unused, struct linker_file *lf, int *error __unused)
{
struct sdt_provider *prov, **curr, **begin, **end, *tmp;
@ -319,8 +319,8 @@ sdt_load(void *arg __unused)
sdt_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, sdt_kld_load, NULL,
EVENTHANDLER_PRI_ANY);
sdt_kld_unload_tag = EVENTHANDLER_REGISTER(kld_unload, sdt_kld_unload,
NULL, EVENTHANDLER_PRI_ANY);
sdt_kld_unload_try_tag = EVENTHANDLER_REGISTER(kld_unload_try,
sdt_kld_unload_try, NULL, EVENTHANDLER_PRI_ANY);
/* Pick up probes from the kernel and already-loaded linker files. */
linker_file_foreach(sdt_linker_file_cb, NULL);
@ -332,7 +332,7 @@ sdt_unload(void *arg __unused)
struct sdt_provider *prov, *tmp;
EVENTHANDLER_DEREGISTER(kld_load, sdt_kld_load_tag);
EVENTHANDLER_DEREGISTER(kld_unload, sdt_kld_unload_tag);
EVENTHANDLER_DEREGISTER(kld_unload_try, sdt_kld_unload_try_tag);
sdt_probe_func = sdt_probe_stub;

View file

@ -132,7 +132,8 @@ static int *pmc_pmcdisp; /* PMC row dispositions */
/* various event handlers */
static eventhandler_tag pmc_exit_tag, pmc_fork_tag;
static eventhandler_tag pmc_exit_tag, pmc_fork_tag, pmc_kld_load_tag,
pmc_kld_unload_tag;
/* Module statistics */
struct pmc_op_getdriverstats pmc_stats;
@ -1475,50 +1476,6 @@ pmc_process_csw_out(struct thread *td)
critical_exit();
}
/*
* Log a KLD operation.
*/
static void
pmc_process_kld_load(struct pmckern_map_in *pkm)
{
struct pmc_owner *po;
sx_assert(&pmc_sx, SX_LOCKED);
/*
* Notify owners of system sampling PMCs about KLD operations.
*/
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
pmclog_process_map_in(po, (pid_t) -1, pkm->pm_address,
(char *) pkm->pm_file);
/*
* TODO: Notify owners of (all) process-sampling PMCs too.
*/
return;
}
static void
pmc_process_kld_unload(struct pmckern_map_out *pkm)
{
struct pmc_owner *po;
sx_assert(&pmc_sx, SX_LOCKED);
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
pmclog_process_map_out(po, (pid_t) -1,
pkm->pm_address, pkm->pm_address + pkm->pm_size);
/*
* TODO: Notify owners of process-sampling PMCs.
*/
}
/*
* A mapping change for a process.
*/
@ -1833,8 +1790,8 @@ const char *pmc_hooknames[] = {
"CSW-IN",
"CSW-OUT",
"SAMPLE",
"KLDLOAD",
"KLDUNLOAD",
"UNUSED1",
"UNUSED2",
"MMAP",
"MUNMAP",
"CALLCHAIN-NMI",
@ -2002,17 +1959,6 @@ pmc_hook_handler(struct thread *td, int function, void *arg)
pmc_process_samples(PCPU_GET(cpuid), PMC_SR);
break;
case PMC_FN_KLD_LOAD:
sx_assert(&pmc_sx, SX_LOCKED);
pmc_process_kld_load((struct pmckern_map_in *) arg);
break;
case PMC_FN_KLD_UNLOAD:
sx_assert(&pmc_sx, SX_LOCKED);
pmc_process_kld_unload((struct pmckern_map_out *) arg);
break;
case PMC_FN_MMAP:
sx_assert(&pmc_sx, SX_LOCKED);
pmc_process_mmap(td, (struct pmckern_map_in *) arg);
@ -4644,6 +4590,47 @@ pmc_process_fork(void *arg __unused, struct proc *p1, struct proc *newproc,
sx_xunlock(&pmc_sx);
}
static void
pmc_kld_load(void *arg __unused, linker_file_t lf)
{
struct pmc_owner *po;
sx_slock(&pmc_sx);
/*
* Notify owners of system sampling PMCs about KLD operations.
*/
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
pmclog_process_map_in(po, (pid_t) -1,
(uintfptr_t) lf->address, lf->filename);
/*
* TODO: Notify owners of (all) process-sampling PMCs too.
*/
sx_sunlock(&pmc_sx);
}
static void
pmc_kld_unload(void *arg __unused, const char *filename __unused,
caddr_t address, size_t size)
{
struct pmc_owner *po;
sx_slock(&pmc_sx);
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
pmclog_process_map_out(po, (pid_t) -1,
(uintfptr_t) address, (uintfptr_t) address + size);
/*
* TODO: Notify owners of process-sampling PMCs.
*/
sx_sunlock(&pmc_sx);
}
/*
* initialization
@ -4913,6 +4900,12 @@ pmc_initialize(void)
pmc_fork_tag = EVENTHANDLER_REGISTER(process_fork,
pmc_process_fork, NULL, EVENTHANDLER_PRI_ANY);
/* register kld event handlers */
pmc_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, pmc_kld_load,
NULL, EVENTHANDLER_PRI_ANY);
pmc_kld_unload_tag = EVENTHANDLER_REGISTER(kld_unload, pmc_kld_unload,
NULL, EVENTHANDLER_PRI_ANY);
/* initialize logging */
pmclog_initialize();
@ -4970,6 +4963,8 @@ pmc_cleanup(void)
/* deregister event handlers */
EVENTHANDLER_DEREGISTER(process_fork, pmc_fork_tag);
EVENTHANDLER_DEREGISTER(process_exit, pmc_exit_tag);
EVENTHANDLER_DEREGISTER(kld_load, pmc_kld_load_tag);
EVENTHANDLER_DEREGISTER(kld_unload, pmc_kld_unload_tag);
/* send SIGBUS to all owner threads, free up allocations */
if (pmc_ownerhash)

View file

@ -995,9 +995,6 @@ linker_search_symbol_name(caddr_t value, char *buf, u_int buflen,
int
kern_kldload(struct thread *td, const char *file, int *fileid)
{
#ifdef HWPMC_HOOKS
struct pmckern_map_in pkm;
#endif
const char *kldname, *modname;
linker_file_t lf;
int error;
@ -1037,17 +1034,9 @@ kern_kldload(struct thread *td, const char *file, int *fileid)
if (fileid != NULL)
*fileid = lf->id;
EVENTHANDLER_INVOKE(kld_load, lf);
#ifdef HWPMC_HOOKS
sx_downgrade(&kld_sx);
pkm.pm_file = lf->filename;
pkm.pm_address = (uintptr_t) lf->address;
PMC_CALL_HOOK(td, PMC_FN_KLD_LOAD, (void *) &pkm);
EVENTHANDLER_INVOKE(kld_load, lf);
sx_sunlock(&kld_sx);
#else
sx_xunlock(&kld_sx);
#endif
done:
CURVNET_RESTORE();
@ -1076,10 +1065,10 @@ sys_kldload(struct thread *td, struct kldload_args *uap)
int
kern_kldunload(struct thread *td, int fileid, int flags)
{
#ifdef HWPMC_HOOKS
struct pmckern_map_out pkm;
#endif
linker_file_t lf;
char *filename = NULL;
caddr_t address;
size_t size;
int error = 0;
if ((error = securelevel_gt(td->td_ucred, 0)) != 0)
@ -1094,7 +1083,7 @@ kern_kldunload(struct thread *td, int fileid, int flags)
if (lf) {
KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
EVENTHANDLER_INVOKE(kld_unload, lf, &error);
EVENTHANDLER_INVOKE(kld_unload_try, lf, &error);
if (error != 0)
error = EBUSY;
else if (lf->userrefs == 0) {
@ -1105,11 +1094,11 @@ kern_kldunload(struct thread *td, int fileid, int flags)
" loaded by the kernel\n");
error = EBUSY;
} else {
#ifdef HWPMC_HOOKS
/* Save data needed by hwpmc(4) before unloading. */
pkm.pm_address = (uintptr_t) lf->address;
pkm.pm_size = lf->size;
#endif
/* Save data needed for the kld_unload callbacks. */
filename = strdup(lf->filename, M_TEMP);
address = lf->address;
size = lf->size;
lf->userrefs--;
error = linker_file_unload(lf, flags);
if (error)
@ -1118,16 +1107,14 @@ kern_kldunload(struct thread *td, int fileid, int flags)
} else
error = ENOENT;
#ifdef HWPMC_HOOKS
if (error == 0) {
sx_downgrade(&kld_sx);
PMC_CALL_HOOK(td, PMC_FN_KLD_UNLOAD, (void *) &pkm);
EVENTHANDLER_INVOKE(kld_unload, filename, address, size);
sx_sunlock(&kld_sx);
} else
sx_xunlock(&kld_sx);
#else
sx_xunlock(&kld_sx);
#endif
free(filename, M_TEMP);
CURVNET_RESTORE();
return (error);
}

View file

@ -269,8 +269,10 @@ EVENTHANDLER_DECLARE(maxsockets_change, uma_zone_chfn);
/* Kernel linker file load and unload events */
struct linker_file;
typedef void (*kld_load_fn)(void *, struct linker_file *);
typedef void (*kld_unload_fn)(void *, struct linker_file *, int *);
typedef void (*kld_unload_fn)(void *, const char *, caddr_t, size_t);
typedef void (*kld_unload_try_fn)(void *, struct linker_file *, int *);
EVENTHANDLER_DECLARE(kld_load, kld_load_fn);
EVENTHANDLER_DECLARE(kld_unload, kld_unload_fn);
EVENTHANDLER_DECLARE(kld_unload_try, kld_unload_try_fn);
#endif /* SYS_EVENTHANDLER_H */

View file

@ -51,8 +51,8 @@
#define PMC_FN_CSW_IN 2
#define PMC_FN_CSW_OUT 3
#define PMC_FN_DO_SAMPLES 4
#define PMC_FN_KLD_LOAD 5
#define PMC_FN_KLD_UNLOAD 6
#define PMC_FN_UNUSED1 5
#define PMC_FN_UNUSED2 6
#define PMC_FN_MMAP 7
#define PMC_FN_MUNMAP 8
#define PMC_FN_USER_CALLCHAIN 9