Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/hwpmc/hwpmc_mod.c
Show First 20 Lines • Show All 136 Lines • ▼ Show 20 Lines | |||||
#define PMC_UNMARK_ROW_THREAD(R) do { \ | #define PMC_UNMARK_ROW_THREAD(R) do { \ | ||||
atomic_add_int(&pmc_pmcdisp[(R)], -1); \ | atomic_add_int(&pmc_pmcdisp[(R)], -1); \ | ||||
KASSERT(pmc_pmcdisp[(R)] >= 0, ("[pmc,%d] row disposition error", \ | KASSERT(pmc_pmcdisp[(R)] >= 0, ("[pmc,%d] row disposition error", \ | ||||
__LINE__)); \ | __LINE__)); \ | ||||
} while (0) | } while (0) | ||||
/* various event handlers */ | /* various event handlers */ | ||||
static eventhandler_tag pmc_exit_tag, pmc_fork_tag, pmc_kld_load_tag, | static eventhandler_tag pmc_kld_load_tag, pmc_kld_unload_tag; | ||||
pmc_kld_unload_tag; | |||||
/* Module statistics */ | /* Module statistics */ | ||||
struct pmc_driverstats pmc_stats; | struct pmc_driverstats pmc_stats; | ||||
/* Machine/processor dependent operations */ | /* Machine/processor dependent operations */ | ||||
static struct pmc_mdep *md; | static struct pmc_mdep *md; | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | |||||
static void pmc_link_target_process(struct pmc *pm, | static void pmc_link_target_process(struct pmc *pm, | ||||
struct pmc_process *pp); | struct pmc_process *pp); | ||||
static void pmc_log_all_process_mappings(struct pmc_owner *po); | static void pmc_log_all_process_mappings(struct pmc_owner *po); | ||||
static void pmc_log_kernel_mappings(struct pmc *pm); | static void pmc_log_kernel_mappings(struct pmc *pm); | ||||
static void pmc_log_process_mappings(struct pmc_owner *po, struct proc *p); | static void pmc_log_process_mappings(struct pmc_owner *po, struct proc *p); | ||||
static void pmc_maybe_remove_owner(struct pmc_owner *po); | static void pmc_maybe_remove_owner(struct pmc_owner *po); | ||||
static void pmc_process_csw_in(struct thread *td); | static void pmc_process_csw_in(struct thread *td); | ||||
static void pmc_process_csw_out(struct thread *td); | static void pmc_process_csw_out(struct thread *td); | ||||
static void pmc_process_exit(void *arg, struct proc *p); | static void pmc_process_exit(struct proc *p); | ||||
static void pmc_process_fork(void *arg, struct proc *p1, | static void pmc_process_fork(struct proc *p1, struct proc *p2); | ||||
struct proc *p2, int n); | |||||
static void pmc_process_samples(int cpu, ring_type_t soft); | static void pmc_process_samples(int cpu, ring_type_t soft); | ||||
static void pmc_release_pmc_descriptor(struct pmc *pmc); | static void pmc_release_pmc_descriptor(struct pmc *pmc); | ||||
static void pmc_process_thread_add(struct thread *td); | static void pmc_process_thread_add(struct thread *td); | ||||
static void pmc_process_thread_delete(struct thread *td); | static void pmc_process_thread_delete(struct thread *td); | ||||
static void pmc_process_thread_userret(struct thread *td); | static void pmc_process_thread_userret(struct thread *td); | ||||
static void pmc_remove_owner(struct pmc_owner *po); | static void pmc_remove_owner(struct pmc_owner *po); | ||||
static void pmc_remove_process_descriptor(struct pmc_process *pp); | static void pmc_remove_process_descriptor(struct pmc_process *pp); | ||||
static void pmc_restore_cpu_binding(struct pmc_binding *pb); | static void pmc_restore_cpu_binding(struct pmc_binding *pb); | ||||
▲ Show 20 Lines • Show All 1,833 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
int cpu; | int cpu; | ||||
PMCDBG4(MOD,PMH,1, "hook td=%p func=%d \"%s\" arg=%p", td, function, | PMCDBG4(MOD,PMH,1, "hook td=%p func=%d \"%s\" arg=%p", td, function, | ||||
pmc_hooknames[function], arg); | pmc_hooknames[function], arg); | ||||
switch (function) | switch (function) | ||||
{ | { | ||||
case PMC_FN_PROCESS_EXIT: | |||||
pmc_process_exit(td->td_proc); | |||||
break; | |||||
/* | case PMC_FN_PROCESS_FORK: | ||||
* Process exec() | pmc_process_fork(td->td_proc, (struct proc *)arg); | ||||
*/ | break; | ||||
case PMC_FN_PROCESS_EXEC: | case PMC_FN_PROCESS_EXEC: | ||||
{ | { | ||||
char *fullpath, *freepath; | char *fullpath, *freepath; | ||||
unsigned int ri; | unsigned int ri; | ||||
int is_using_hwpmcs; | int is_using_hwpmcs; | ||||
struct pmc *pm; | struct pmc *pm; | ||||
struct proc *p; | struct proc *p; | ||||
▲ Show 20 Lines • Show All 2,946 Lines • ▼ Show 20 Lines | |||||
* XXX This eventhandler gets called early in the exit process. | * XXX This eventhandler gets called early in the exit process. | ||||
* Consider using a 'hook' invocation from thread_exit() or equivalent | * Consider using a 'hook' invocation from thread_exit() or equivalent | ||||
* spot. Another negative is that kse_exit doesn't seem to call | * spot. Another negative is that kse_exit doesn't seem to call | ||||
* exit1() [??]. | * exit1() [??]. | ||||
* | * | ||||
*/ | */ | ||||
static void | static void | ||||
pmc_process_exit(void *arg __unused, struct proc *p) | pmc_process_exit(struct proc *p) | ||||
{ | { | ||||
struct pmc *pm; | struct pmc *pm; | ||||
int adjri, cpu; | int adjri, cpu; | ||||
unsigned int ri; | unsigned int ri; | ||||
int is_using_hwpmcs; | int is_using_hwpmcs; | ||||
struct pmc_owner *po; | struct pmc_owner *po; | ||||
struct pmc_process *pp; | struct pmc_process *pp; | ||||
struct pmc_classdep *pcd; | struct pmc_classdep *pcd; | ||||
▲ Show 20 Lines • Show All 166 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Handle a process fork. | * Handle a process fork. | ||||
* | * | ||||
* If the parent process 'p1' is under HWPMC monitoring, then copy | * If the parent process 'p1' is under HWPMC monitoring, then copy | ||||
* over any attached PMCs that have 'do_descendants' semantics. | * over any attached PMCs that have 'do_descendants' semantics. | ||||
*/ | */ | ||||
static void | static void | ||||
pmc_process_fork(void *arg __unused, struct proc *p1, struct proc *newproc, | pmc_process_fork(struct proc *p1, struct proc *newproc) | ||||
int flags) | |||||
{ | { | ||||
int is_using_hwpmcs; | int is_using_hwpmcs; | ||||
unsigned int ri; | unsigned int ri; | ||||
uint32_t do_descendants; | uint32_t do_descendants; | ||||
struct pmc *pm; | struct pmc *pm; | ||||
struct pmc_owner *po; | struct pmc_owner *po; | ||||
struct pmc_process *ppnew, *ppold; | struct pmc_process *ppnew, *ppold; | ||||
(void) flags; /* unused parameter */ | |||||
PROC_LOCK(p1); | PROC_LOCK(p1); | ||||
is_using_hwpmcs = p1->p_flag & P_HWPMC; | is_using_hwpmcs = p1->p_flag & P_HWPMC; | ||||
PROC_UNLOCK(p1); | PROC_UNLOCK(p1); | ||||
/* | /* | ||||
* If there are system-wide sampling PMCs active, we need to | * If there are system-wide sampling PMCs active, we need to | ||||
* log all fork events to their owner's logs. | * log all fork events to their owner's logs. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 452 Lines • ▼ Show 20 Lines | #endif | ||||
/* Initialize a spin mutex for the thread free list. */ | /* Initialize a spin mutex for the thread free list. */ | ||||
mtx_init(&pmc_threadfreelist_mtx, "pmc-threadfreelist", "pmc-leaf", | mtx_init(&pmc_threadfreelist_mtx, "pmc-threadfreelist", "pmc-leaf", | ||||
MTX_SPIN); | MTX_SPIN); | ||||
/* Initialize the task to prune the thread free list. */ | /* Initialize the task to prune the thread free list. */ | ||||
TASK_INIT(&free_task, 0, pmc_thread_descriptor_pool_free_task, NULL); | TASK_INIT(&free_task, 0, pmc_thread_descriptor_pool_free_task, NULL); | ||||
/* register process {exit,fork,exec} handlers */ | |||||
pmc_exit_tag = EVENTHANDLER_REGISTER(process_exit, | |||||
pmc_process_exit, NULL, EVENTHANDLER_PRI_ANY); | |||||
pmc_fork_tag = EVENTHANDLER_REGISTER(process_fork, | |||||
pmc_process_fork, NULL, EVENTHANDLER_PRI_ANY); | |||||
/* register kld event handlers */ | /* register kld event handlers */ | ||||
pmc_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, pmc_kld_load, | pmc_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, pmc_kld_load, | ||||
NULL, EVENTHANDLER_PRI_ANY); | NULL, EVENTHANDLER_PRI_ANY); | ||||
pmc_kld_unload_tag = EVENTHANDLER_REGISTER(kld_unload, pmc_kld_unload, | pmc_kld_unload_tag = EVENTHANDLER_REGISTER(kld_unload, pmc_kld_unload, | ||||
NULL, EVENTHANDLER_PRI_ANY); | NULL, EVENTHANDLER_PRI_ANY); | ||||
/* initialize logging */ | /* initialize logging */ | ||||
pmclog_initialize(); | pmclog_initialize(); | ||||
Show All 27 Lines | |||||
static void | static void | ||||
pmc_cleanup(void) | pmc_cleanup(void) | ||||
{ | { | ||||
int c, cpu; | int c, cpu; | ||||
unsigned int maxcpu; | unsigned int maxcpu; | ||||
struct pmc_ownerhash *ph; | struct pmc_ownerhash *ph; | ||||
struct pmc_owner *po, *tmp; | struct pmc_owner *po, *tmp; | ||||
struct pmc_binding pb; | struct pmc_binding pb; | ||||
struct proc *p, *phead; | |||||
#ifdef HWPMC_DEBUG | #ifdef HWPMC_DEBUG | ||||
struct pmc_processhash *prh; | struct pmc_processhash *prh; | ||||
#endif | #endif | ||||
bool draining; | |||||
PMCDBG0(MOD,INI,0, "cleanup"); | PMCDBG0(MOD,INI,0, "cleanup"); | ||||
/* switch off sampling */ | /* switch off sampling */ | ||||
CPU_FOREACH(cpu) | CPU_FOREACH(cpu) | ||||
DPCPU_ID_SET(cpu, pmc_sampled, 0); | DPCPU_ID_SET(cpu, pmc_sampled, 0); | ||||
pmc_intr = NULL; | pmc_intr = NULL; | ||||
sx_xlock(&pmc_sx); | sx_xlock(&pmc_sx); | ||||
if (pmc_hook == NULL) { /* being unloaded already */ | if (pmc_hook == NULL) { /* being unloaded already */ | ||||
sx_xunlock(&pmc_sx); | sx_xunlock(&pmc_sx); | ||||
return; | return; | ||||
} | } | ||||
/* | |||||
* Ensure that fork and exit hooks are not called. | |||||
* New forked processes are added at the head of allproc p_list. | |||||
* There is no need to drain processes before phead in allproc, | |||||
* they were forked after pmc_hook was cleared. | |||||
*/ | |||||
sx_slock(&allproc_lock); | |||||
pmc_hook = NULL; /* prevent new threads from entering module */ | pmc_hook = NULL; /* prevent new threads from entering module */ | ||||
phead = LIST_FIRST(&allproc); | |||||
for (;;) { | |||||
draining = false; | |||||
p = phead; | |||||
LIST_FOREACH_FROM(p, &allproc, p_list) { | |||||
PROC_LOCK(p); | |||||
if (p->p_state == PRS_NEW || | |||||
((p->p_flag & P_WEXIT) != 0 && | |||||
p->p_state != PRS_ZOMBIE)) | |||||
draining = true; | |||||
markj: Isn't it quite likely that this will fail to make progress? | |||||
PROC_UNLOCK(p); | |||||
if (draining) | |||||
break; | |||||
} | |||||
if (draining) { | |||||
sx_sunlock(&allproc_lock); | |||||
pause("hwpmcd", 1); | |||||
sx_slock(&allproc_lock); | |||||
} | |||||
else | |||||
markjUnsubmitted Not Done Inline ActionsStyle markj: Style | |||||
break; | |||||
} | |||||
sx_sunlock(&allproc_lock); | |||||
/* deregister event handlers */ | /* 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_load, pmc_kld_load_tag); | ||||
EVENTHANDLER_DEREGISTER(kld_unload, pmc_kld_unload_tag); | EVENTHANDLER_DEREGISTER(kld_unload, pmc_kld_unload_tag); | ||||
/* send SIGBUS to all owner threads, free up allocations */ | /* send SIGBUS to all owner threads, free up allocations */ | ||||
if (pmc_ownerhash) | if (pmc_ownerhash) | ||||
for (ph = pmc_ownerhash; | for (ph = pmc_ownerhash; | ||||
ph <= &pmc_ownerhash[pmc_ownerhashmask]; | ph <= &pmc_ownerhash[pmc_ownerhashmask]; | ||||
ph++) { | ph++) { | ||||
▲ Show 20 Lines • Show All 168 Lines • Show Last 20 Lines |
Isn't it quite likely that this will fail to make progress?