Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/hwpmc/hwpmc_mod.c
Show First 20 Lines • Show All 645 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* save the cpu binding of the current kthread | * save the cpu binding of the current kthread | ||||
*/ | */ | ||||
static void | static void | ||||
pmc_save_cpu_binding(struct pmc_binding *pb) | pmc_save_cpu_binding(struct pmc_binding *pb) | ||||
{ | { | ||||
PMCDBG(CPU,BND,2, "%s", "save-cpu"); | PMCDBG0(CPU,BND,2, "save-cpu"); | ||||
thread_lock(curthread); | thread_lock(curthread); | ||||
pb->pb_bound = sched_is_bound(curthread); | pb->pb_bound = sched_is_bound(curthread); | ||||
pb->pb_cpu = curthread->td_oncpu; | pb->pb_cpu = curthread->td_oncpu; | ||||
thread_unlock(curthread); | thread_unlock(curthread); | ||||
PMCDBG(CPU,BND,2, "save-cpu cpu=%d", pb->pb_cpu); | PMCDBG1(CPU,BND,2, "save-cpu cpu=%d", pb->pb_cpu); | ||||
} | } | ||||
/* | /* | ||||
* restore the cpu binding of the current thread | * restore the cpu binding of the current thread | ||||
*/ | */ | ||||
static void | static void | ||||
pmc_restore_cpu_binding(struct pmc_binding *pb) | pmc_restore_cpu_binding(struct pmc_binding *pb) | ||||
{ | { | ||||
PMCDBG(CPU,BND,2, "restore-cpu curcpu=%d restore=%d", | PMCDBG2(CPU,BND,2, "restore-cpu curcpu=%d restore=%d", | ||||
curthread->td_oncpu, pb->pb_cpu); | curthread->td_oncpu, pb->pb_cpu); | ||||
thread_lock(curthread); | thread_lock(curthread); | ||||
if (pb->pb_bound) | if (pb->pb_bound) | ||||
sched_bind(curthread, pb->pb_cpu); | sched_bind(curthread, pb->pb_cpu); | ||||
else | else | ||||
sched_unbind(curthread); | sched_unbind(curthread); | ||||
thread_unlock(curthread); | thread_unlock(curthread); | ||||
PMCDBG(CPU,BND,2, "%s", "restore-cpu done"); | PMCDBG0(CPU,BND,2, "restore-cpu done"); | ||||
} | } | ||||
/* | /* | ||||
* move execution over the specified cpu and bind it there. | * move execution over the specified cpu and bind it there. | ||||
*/ | */ | ||||
static void | static void | ||||
pmc_select_cpu(int cpu) | pmc_select_cpu(int cpu) | ||||
{ | { | ||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), | KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), | ||||
("[pmc,%d] bad cpu number %d", __LINE__, cpu)); | ("[pmc,%d] bad cpu number %d", __LINE__, cpu)); | ||||
/* Never move to an inactive CPU. */ | /* Never move to an inactive CPU. */ | ||||
KASSERT(pmc_cpu_is_active(cpu), ("[pmc,%d] selecting inactive " | KASSERT(pmc_cpu_is_active(cpu), ("[pmc,%d] selecting inactive " | ||||
"CPU %d", __LINE__, cpu)); | "CPU %d", __LINE__, cpu)); | ||||
PMCDBG(CPU,SEL,2, "select-cpu cpu=%d", cpu); | PMCDBG1(CPU,SEL,2, "select-cpu cpu=%d", cpu); | ||||
thread_lock(curthread); | thread_lock(curthread); | ||||
sched_bind(curthread, cpu); | sched_bind(curthread, cpu); | ||||
thread_unlock(curthread); | thread_unlock(curthread); | ||||
KASSERT(curthread->td_oncpu == cpu, | KASSERT(curthread->td_oncpu == cpu, | ||||
("[pmc,%d] CPU not bound [cpu=%d, curr=%d]", __LINE__, | ("[pmc,%d] CPU not bound [cpu=%d, curr=%d]", __LINE__, | ||||
cpu, curthread->td_oncpu)); | cpu, curthread->td_oncpu)); | ||||
PMCDBG(CPU,SEL,2, "select-cpu cpu=%d ok", cpu); | PMCDBG1(CPU,SEL,2, "select-cpu cpu=%d ok", cpu); | ||||
} | } | ||||
/* | /* | ||||
* Force a context switch. | * Force a context switch. | ||||
* | * | ||||
* We do this by pause'ing for 1 tick -- invoking mi_switch() is not | * We do this by pause'ing for 1 tick -- invoking mi_switch() is not | ||||
* guaranteed to force a context switch. | * guaranteed to force a context switch. | ||||
*/ | */ | ||||
Show All 25 Lines | |||||
void | void | ||||
pmc_remove_owner(struct pmc_owner *po) | pmc_remove_owner(struct pmc_owner *po) | ||||
{ | { | ||||
struct pmc *pm, *tmp; | struct pmc *pm, *tmp; | ||||
sx_assert(&pmc_sx, SX_XLOCKED); | sx_assert(&pmc_sx, SX_XLOCKED); | ||||
PMCDBG(OWN,ORM,1, "remove-owner po=%p", po); | PMCDBG1(OWN,ORM,1, "remove-owner po=%p", po); | ||||
/* Remove descriptor from the owner hash table */ | /* Remove descriptor from the owner hash table */ | ||||
LIST_REMOVE(po, po_next); | LIST_REMOVE(po, po_next); | ||||
/* release all owned PMC descriptors */ | /* release all owned PMC descriptors */ | ||||
LIST_FOREACH_SAFE(pm, &po->po_pmcs, pm_next, tmp) { | LIST_FOREACH_SAFE(pm, &po->po_pmcs, pm_next, tmp) { | ||||
PMCDBG(OWN,ORM,2, "pmc=%p", pm); | PMCDBG1(OWN,ORM,2, "pmc=%p", pm); | ||||
KASSERT(pm->pm_owner == po, | KASSERT(pm->pm_owner == po, | ||||
("[pmc,%d] owner %p != po %p", __LINE__, pm->pm_owner, po)); | ("[pmc,%d] owner %p != po %p", __LINE__, pm->pm_owner, po)); | ||||
pmc_release_pmc_descriptor(pm); /* will unlink from the list */ | pmc_release_pmc_descriptor(pm); /* will unlink from the list */ | ||||
pmc_destroy_pmc_descriptor(pm); | pmc_destroy_pmc_descriptor(pm); | ||||
} | } | ||||
KASSERT(po->po_sscount == 0, | KASSERT(po->po_sscount == 0, | ||||
Show All 9 Lines | |||||
/* | /* | ||||
* remove an owner process record if all conditions are met. | * remove an owner process record if all conditions are met. | ||||
*/ | */ | ||||
static void | static void | ||||
pmc_maybe_remove_owner(struct pmc_owner *po) | pmc_maybe_remove_owner(struct pmc_owner *po) | ||||
{ | { | ||||
PMCDBG(OWN,OMR,1, "maybe-remove-owner po=%p", po); | PMCDBG1(OWN,OMR,1, "maybe-remove-owner po=%p", po); | ||||
/* | /* | ||||
* Remove owner record if | * Remove owner record if | ||||
* - this process does not own any PMCs | * - this process does not own any PMCs | ||||
* - this process has not allocated a system-wide sampling buffer | * - this process has not allocated a system-wide sampling buffer | ||||
*/ | */ | ||||
if (LIST_EMPTY(&po->po_pmcs) && | if (LIST_EMPTY(&po->po_pmcs) && | ||||
Show All 21 Lines | KASSERT(PMC_IS_VIRTUAL_MODE(PMC_TO_MODE(pm)), | ||||
("[pmc,%d] Attaching a non-process-virtual pmc=%p to pid=%d", | ("[pmc,%d] Attaching a non-process-virtual pmc=%p to pid=%d", | ||||
__LINE__, pm, pp->pp_proc->p_pid)); | __LINE__, pm, pp->pp_proc->p_pid)); | ||||
KASSERT(pp->pp_refcnt >= 0 && pp->pp_refcnt <= ((int) md->pmd_npmc - 1), | KASSERT(pp->pp_refcnt >= 0 && pp->pp_refcnt <= ((int) md->pmd_npmc - 1), | ||||
("[pmc,%d] Illegal reference count %d for process record %p", | ("[pmc,%d] Illegal reference count %d for process record %p", | ||||
__LINE__, pp->pp_refcnt, (void *) pp)); | __LINE__, pp->pp_refcnt, (void *) pp)); | ||||
ri = PMC_TO_ROWINDEX(pm); | ri = PMC_TO_ROWINDEX(pm); | ||||
PMCDBG(PRC,TLK,1, "link-target pmc=%p ri=%d pmc-process=%p", | PMCDBG3(PRC,TLK,1, "link-target pmc=%p ri=%d pmc-process=%p", | ||||
pm, ri, pp); | pm, ri, pp); | ||||
#ifdef HWPMC_DEBUG | #ifdef HWPMC_DEBUG | ||||
LIST_FOREACH(pt, &pm->pm_targets, pt_next) | LIST_FOREACH(pt, &pm->pm_targets, pt_next) | ||||
if (pt->pt_process == pp) | if (pt->pt_process == pp) | ||||
KASSERT(0, ("[pmc,%d] pp %p already in pmc %p targets", | KASSERT(0, ("[pmc,%d] pp %p already in pmc %p targets", | ||||
__LINE__, pp, pm)); | __LINE__, pp, pm)); | ||||
#endif | #endif | ||||
Show All 36 Lines | KASSERT(pm != NULL && pp != NULL, | ||||
("[pmc,%d] Null pm %p or pp %p", __LINE__, pm, pp)); | ("[pmc,%d] Null pm %p or pp %p", __LINE__, pm, pp)); | ||||
KASSERT(pp->pp_refcnt >= 1 && pp->pp_refcnt <= (int) md->pmd_npmc, | KASSERT(pp->pp_refcnt >= 1 && pp->pp_refcnt <= (int) md->pmd_npmc, | ||||
("[pmc,%d] Illegal ref count %d on process record %p", | ("[pmc,%d] Illegal ref count %d on process record %p", | ||||
__LINE__, pp->pp_refcnt, (void *) pp)); | __LINE__, pp->pp_refcnt, (void *) pp)); | ||||
ri = PMC_TO_ROWINDEX(pm); | ri = PMC_TO_ROWINDEX(pm); | ||||
PMCDBG(PRC,TUL,1, "unlink-target pmc=%p ri=%d pmc-process=%p", | PMCDBG3(PRC,TUL,1, "unlink-target pmc=%p ri=%d pmc-process=%p", | ||||
pm, ri, pp); | pm, ri, pp); | ||||
KASSERT(pp->pp_pmcs[ri].pp_pmc == pm, | KASSERT(pp->pp_pmcs[ri].pp_pmc == pm, | ||||
("[pmc,%d] PMC ri %d mismatch pmc %p pp->[ri] %p", __LINE__, | ("[pmc,%d] PMC ri %d mismatch pmc %p pp->[ri] %p", __LINE__, | ||||
ri, pm, pp->pp_pmcs[ri].pp_pmc)); | ri, pm, pp->pp_pmcs[ri].pp_pmc)); | ||||
pp->pp_pmcs[ri].pp_pmc = NULL; | pp->pp_pmcs[ri].pp_pmc = NULL; | ||||
pp->pp_pmcs[ri].pp_pmcval = (pmc_value_t) 0; | pp->pp_pmcs[ri].pp_pmcval = (pmc_value_t) 0; | ||||
Show All 19 Lines | pmc_unlink_target_process(struct pmc *pm, struct pmc_process *pp) | ||||
/* if the PMC now lacks targets, send the owner a SIGIO */ | /* if the PMC now lacks targets, send the owner a SIGIO */ | ||||
if (LIST_EMPTY(&pm->pm_targets)) { | if (LIST_EMPTY(&pm->pm_targets)) { | ||||
p = pm->pm_owner->po_owner; | p = pm->pm_owner->po_owner; | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
kern_psignal(p, SIGIO); | kern_psignal(p, SIGIO); | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
PMCDBG(PRC,SIG,2, "signalling proc=%p signal=%d", p, | PMCDBG2(PRC,SIG,2, "signalling proc=%p signal=%d", p, | ||||
SIGIO); | SIGIO); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Check if PMC 'pm' may be attached to target process 't'. | * Check if PMC 'pm' may be attached to target process 't'. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | |||||
pmc_attach_one_process(struct proc *p, struct pmc *pm) | pmc_attach_one_process(struct proc *p, struct pmc *pm) | ||||
{ | { | ||||
int ri; | int ri; | ||||
char *fullpath, *freepath; | char *fullpath, *freepath; | ||||
struct pmc_process *pp; | struct pmc_process *pp; | ||||
sx_assert(&pmc_sx, SX_XLOCKED); | sx_assert(&pmc_sx, SX_XLOCKED); | ||||
PMCDBG(PRC,ATT,2, "attach-one pm=%p ri=%d proc=%p (%d, %s)", pm, | PMCDBG5(PRC,ATT,2, "attach-one pm=%p ri=%d proc=%p (%d, %s)", pm, | ||||
PMC_TO_ROWINDEX(pm), p, p->p_pid, p->p_comm); | PMC_TO_ROWINDEX(pm), p, p->p_pid, p->p_comm); | ||||
/* | /* | ||||
* Locate the process descriptor corresponding to process 'p', | * Locate the process descriptor corresponding to process 'p', | ||||
* allocating space as needed. | * allocating space as needed. | ||||
* | * | ||||
* Verify that rowindex 'pm_rowindex' is free in the process | * Verify that rowindex 'pm_rowindex' is free in the process | ||||
* descriptor. | * descriptor. | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
pmc_attach_process(struct proc *p, struct pmc *pm) | pmc_attach_process(struct proc *p, struct pmc *pm) | ||||
{ | { | ||||
int error; | int error; | ||||
struct proc *top; | struct proc *top; | ||||
sx_assert(&pmc_sx, SX_XLOCKED); | sx_assert(&pmc_sx, SX_XLOCKED); | ||||
PMCDBG(PRC,ATT,1, "attach pm=%p ri=%d proc=%p (%d, %s)", pm, | PMCDBG5(PRC,ATT,1, "attach pm=%p ri=%d proc=%p (%d, %s)", pm, | ||||
PMC_TO_ROWINDEX(pm), p, p->p_pid, p->p_comm); | PMC_TO_ROWINDEX(pm), p, p->p_pid, p->p_comm); | ||||
/* | /* | ||||
* If this PMC successfully allowed a GETMSR operation | * If this PMC successfully allowed a GETMSR operation | ||||
* in the past, disallow further ATTACHes. | * in the past, disallow further ATTACHes. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | pmc_detach_one_process(struct proc *p, struct pmc *pm, int flags) | ||||
sx_assert(&pmc_sx, SX_XLOCKED); | sx_assert(&pmc_sx, SX_XLOCKED); | ||||
KASSERT(pm != NULL, | KASSERT(pm != NULL, | ||||
("[pmc,%d] null pm pointer", __LINE__)); | ("[pmc,%d] null pm pointer", __LINE__)); | ||||
ri = PMC_TO_ROWINDEX(pm); | ri = PMC_TO_ROWINDEX(pm); | ||||
PMCDBG(PRC,ATT,2, "detach-one pm=%p ri=%d proc=%p (%d, %s) flags=0x%x", | PMCDBG6(PRC,ATT,2, "detach-one pm=%p ri=%d proc=%p (%d, %s) flags=0x%x", | ||||
pm, ri, p, p->p_pid, p->p_comm, flags); | pm, ri, p, p->p_pid, p->p_comm, flags); | ||||
if ((pp = pmc_find_process_descriptor(p, 0)) == NULL) | if ((pp = pmc_find_process_descriptor(p, 0)) == NULL) | ||||
return ESRCH; | return ESRCH; | ||||
if (pp->pp_pmcs[ri].pp_pmc != pm) | if (pp->pp_pmcs[ri].pp_pmc != pm) | ||||
return EINVAL; | return EINVAL; | ||||
Show All 33 Lines | |||||
static int | static int | ||||
pmc_detach_process(struct proc *p, struct pmc *pm) | pmc_detach_process(struct proc *p, struct pmc *pm) | ||||
{ | { | ||||
struct proc *top; | struct proc *top; | ||||
sx_assert(&pmc_sx, SX_XLOCKED); | sx_assert(&pmc_sx, SX_XLOCKED); | ||||
PMCDBG(PRC,ATT,1, "detach pm=%p ri=%d proc=%p (%d, %s)", pm, | PMCDBG5(PRC,ATT,1, "detach pm=%p ri=%d proc=%p (%d, %s)", pm, | ||||
PMC_TO_ROWINDEX(pm), p, p->p_pid, p->p_comm); | PMC_TO_ROWINDEX(pm), p, p->p_pid, p->p_comm); | ||||
if ((pm->pm_flags & PMC_F_DESCENDANTS) == 0) | if ((pm->pm_flags & PMC_F_DESCENDANTS) == 0) | ||||
return pmc_detach_one_process(p, pm, PMC_FLAG_REMOVE); | return pmc_detach_one_process(p, pm, PMC_FLAG_REMOVE); | ||||
/* | /* | ||||
* Traverse all children, detaching them from this PMC. We | * Traverse all children, detaching them from this PMC. We | ||||
* ignore errors since we could be detaching a PMC from a | * ignore errors since we could be detaching a PMC from a | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | pmc_process_csw_in(struct thread *td) | ||||
KASSERT(pp->pp_proc == td->td_proc, | KASSERT(pp->pp_proc == td->td_proc, | ||||
("[pmc,%d] not my thread state", __LINE__)); | ("[pmc,%d] not my thread state", __LINE__)); | ||||
critical_enter(); /* no preemption from this point */ | critical_enter(); /* no preemption from this point */ | ||||
cpu = PCPU_GET(cpuid); /* td->td_oncpu is invalid */ | cpu = PCPU_GET(cpuid); /* td->td_oncpu is invalid */ | ||||
PMCDBG(CSW,SWI,1, "cpu=%d proc=%p (%d, %s) pp=%p", cpu, p, | PMCDBG5(CSW,SWI,1, "cpu=%d proc=%p (%d, %s) pp=%p", cpu, p, | ||||
p->p_pid, p->p_comm, pp); | p->p_pid, p->p_comm, pp); | ||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), | KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), | ||||
("[pmc,%d] wierd CPU id %d", __LINE__, cpu)); | ("[pmc,%d] wierd CPU id %d", __LINE__, cpu)); | ||||
pc = pmc_pcpu[cpu]; | pc = pmc_pcpu[cpu]; | ||||
for (ri = 0; ri < md->pmd_npmc; ri++) { | for (ri = 0; ri < md->pmd_npmc; ri++) { | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | if (PMC_TO_MODE(pm) == PMC_MODE_TS) { | ||||
("[pmc,%d] illegal mode=%d", __LINE__, | ("[pmc,%d] illegal mode=%d", __LINE__, | ||||
PMC_TO_MODE(pm))); | PMC_TO_MODE(pm))); | ||||
mtx_pool_lock_spin(pmc_mtxpool, pm); | mtx_pool_lock_spin(pmc_mtxpool, pm); | ||||
newvalue = PMC_PCPU_SAVED(cpu, ri) = | newvalue = PMC_PCPU_SAVED(cpu, ri) = | ||||
pm->pm_gv.pm_savedvalue; | pm->pm_gv.pm_savedvalue; | ||||
mtx_pool_unlock_spin(pmc_mtxpool, pm); | mtx_pool_unlock_spin(pmc_mtxpool, pm); | ||||
} | } | ||||
PMCDBG(CSW,SWI,1,"cpu=%d ri=%d new=%jd", cpu, ri, newvalue); | PMCDBG3(CSW,SWI,1,"cpu=%d ri=%d new=%jd", cpu, ri, newvalue); | ||||
pcd->pcd_write_pmc(cpu, adjri, newvalue); | pcd->pcd_write_pmc(cpu, adjri, newvalue); | ||||
pcd->pcd_start_pmc(cpu, adjri); | pcd->pcd_start_pmc(cpu, adjri); | ||||
} | } | ||||
/* | /* | ||||
* perform any other architecture/cpu dependent thread | * perform any other architecture/cpu dependent thread | ||||
* switch-in actions. | * switch-in actions. | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | pmc_process_csw_out(struct thread *td) | ||||
/* | /* | ||||
* save PMCs | * save PMCs | ||||
*/ | */ | ||||
critical_enter(); | critical_enter(); | ||||
cpu = PCPU_GET(cpuid); /* td->td_oncpu is invalid */ | cpu = PCPU_GET(cpuid); /* td->td_oncpu is invalid */ | ||||
PMCDBG(CSW,SWO,1, "cpu=%d proc=%p (%d, %s) pp=%p", cpu, p, | PMCDBG5(CSW,SWO,1, "cpu=%d proc=%p (%d, %s) pp=%p", cpu, p, | ||||
p->p_pid, p->p_comm, pp); | p->p_pid, p->p_comm, pp); | ||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), | KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), | ||||
("[pmc,%d wierd CPU id %d", __LINE__, cpu)); | ("[pmc,%d wierd CPU id %d", __LINE__, cpu)); | ||||
pc = pmc_pcpu[cpu]; | pc = pmc_pcpu[cpu]; | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if (pp != NULL && pp->pp_pmcs[ri].pp_pmc != NULL) { | ||||
KASSERT(pp->pp_refcnt > 0, | KASSERT(pp->pp_refcnt > 0, | ||||
("[pmc,%d] pp refcnt = %d", __LINE__, | ("[pmc,%d] pp refcnt = %d", __LINE__, | ||||
pp->pp_refcnt)); | pp->pp_refcnt)); | ||||
pcd->pcd_read_pmc(cpu, adjri, &newvalue); | pcd->pcd_read_pmc(cpu, adjri, &newvalue); | ||||
tmp = newvalue - PMC_PCPU_SAVED(cpu,ri); | tmp = newvalue - PMC_PCPU_SAVED(cpu,ri); | ||||
PMCDBG(CSW,SWO,1,"cpu=%d ri=%d tmp=%jd", cpu, ri, | PMCDBG3(CSW,SWO,1,"cpu=%d ri=%d tmp=%jd", cpu, ri, | ||||
tmp); | tmp); | ||||
if (mode == PMC_MODE_TS) { | if (mode == PMC_MODE_TS) { | ||||
/* | /* | ||||
* For sampling process-virtual PMCs, | * For sampling process-virtual PMCs, | ||||
* we expect the count to be | * we expect the count to be | ||||
* decreasing as the 'value' | * decreasing as the 'value' | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | pmc_log_kernel_mappings(struct pmc *pm) | ||||
if (po->po_flags & PMC_PO_INITIAL_MAPPINGS_DONE) | if (po->po_flags & PMC_PO_INITIAL_MAPPINGS_DONE) | ||||
return; | return; | ||||
/* | /* | ||||
* Log the current set of kernel modules. | * Log the current set of kernel modules. | ||||
*/ | */ | ||||
kmbase = linker_hwpmc_list_objects(); | kmbase = linker_hwpmc_list_objects(); | ||||
for (km = kmbase; km->pm_file != NULL; km++) { | for (km = kmbase; km->pm_file != NULL; km++) { | ||||
PMCDBG(LOG,REG,1,"%s %p", (char *) km->pm_file, | PMCDBG2(LOG,REG,1,"%s %p", (char *) km->pm_file, | ||||
(void *) km->pm_address); | (void *) km->pm_address); | ||||
pmclog_process_map_in(po, (pid_t) -1, km->pm_address, | pmclog_process_map_in(po, (pid_t) -1, km->pm_address, | ||||
km->pm_file); | km->pm_file); | ||||
} | } | ||||
free(kmbase, M_LINKER); | free(kmbase, M_LINKER); | ||||
po->po_flags |= PMC_PO_INITIAL_MAPPINGS_DONE; | po->po_flags |= PMC_PO_INITIAL_MAPPINGS_DONE; | ||||
} | } | ||||
Show All 24 Lines | if ((vm = vmspace_acquire_ref(p)) == NULL) | ||||
return; | return; | ||||
map = &vm->vm_map; | map = &vm->vm_map; | ||||
vm_map_lock_read(map); | vm_map_lock_read(map); | ||||
for (entry = map->header.next; entry != &map->header; entry = entry->next) { | for (entry = map->header.next; entry != &map->header; entry = entry->next) { | ||||
if (entry == NULL) { | if (entry == NULL) { | ||||
PMCDBG(LOG,OPS,2, "hwpmc: vm_map entry unexpectedly " | PMCDBG2(LOG,OPS,2, "hwpmc: vm_map entry unexpectedly " | ||||
"NULL! pid=%d vm_map=%p\n", p->p_pid, map); | "NULL! pid=%d vm_map=%p\n", p->p_pid, map); | ||||
break; | break; | ||||
} | } | ||||
/* | /* | ||||
* We only care about executable map entries. | * We only care about executable map entries. | ||||
*/ | */ | ||||
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) || | if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) || | ||||
Show All 16 Lines | for (lobj = tobj = obj; tobj != NULL; tobj = tobj->backing_object) { | ||||
VM_OBJECT_RUNLOCK(lobj); | VM_OBJECT_RUNLOCK(lobj); | ||||
lobj = tobj; | lobj = tobj; | ||||
} | } | ||||
/* | /* | ||||
* At this point lobj is the base vm_object and it is locked. | * At this point lobj is the base vm_object and it is locked. | ||||
*/ | */ | ||||
if (lobj == NULL) { | if (lobj == NULL) { | ||||
PMCDBG(LOG,OPS,2, "hwpmc: lobj unexpectedly NULL! pid=%d " | PMCDBG3(LOG,OPS,2, "hwpmc: lobj unexpectedly NULL! pid=%d " | ||||
"vm_map=%p vm_obj=%p\n", p->p_pid, map, obj); | "vm_map=%p vm_obj=%p\n", p->p_pid, map, obj); | ||||
VM_OBJECT_RUNLOCK(obj); | VM_OBJECT_RUNLOCK(obj); | ||||
continue; | continue; | ||||
} | } | ||||
if (lobj->type != OBJT_VNODE || lobj->handle == NULL) { | if (lobj->type != OBJT_VNODE || lobj->handle == NULL) { | ||||
if (lobj != obj) | if (lobj != obj) | ||||
VM_OBJECT_RUNLOCK(lobj); | VM_OBJECT_RUNLOCK(lobj); | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | const char *pmc_hooknames[] = { | ||||
"SOFTSAMPLING" | "SOFTSAMPLING" | ||||
}; | }; | ||||
#endif | #endif | ||||
static int | static int | ||||
pmc_hook_handler(struct thread *td, int function, void *arg) | pmc_hook_handler(struct thread *td, int function, void *arg) | ||||
{ | { | ||||
PMCDBG(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) | ||||
{ | { | ||||
/* | /* | ||||
* Process exec() | * Process exec() | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | for (ri = 0; ri < md->pmd_npmc; ri++) | ||||
p->p_pid, pk->pm_entryaddr, | p->p_pid, pk->pm_entryaddr, | ||||
fullpath); | fullpath); | ||||
} | } | ||||
if (freepath) | if (freepath) | ||||
free(freepath, M_TEMP); | free(freepath, M_TEMP); | ||||
PMCDBG(PRC,EXC,1, "exec proc=%p (%d, %s) cred-changed=%d", | PMCDBG4(PRC,EXC,1, "exec proc=%p (%d, %s) cred-changed=%d", | ||||
p, p->p_pid, p->p_comm, pk->pm_credentialschanged); | p, p->p_pid, p->p_comm, pk->pm_credentialschanged); | ||||
if (pk->pm_credentialschanged == 0) /* no change */ | if (pk->pm_credentialschanged == 0) /* no change */ | ||||
break; | break; | ||||
/* | /* | ||||
* If the newly exec()'ed process has a different credential | * If the newly exec()'ed process has a different credential | ||||
* than before, allow it to be the target of a PMC only if | * than before, allow it to be the target of a PMC only if | ||||
▲ Show 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | pmc_allocate_owner_descriptor(struct proc *p) | ||||
/* allocate space for N pointers and one descriptor struct */ | /* allocate space for N pointers and one descriptor struct */ | ||||
po = malloc(sizeof(struct pmc_owner), M_PMC, M_WAITOK|M_ZERO); | po = malloc(sizeof(struct pmc_owner), M_PMC, M_WAITOK|M_ZERO); | ||||
po->po_owner = p; | po->po_owner = p; | ||||
LIST_INSERT_HEAD(poh, po, po_next); /* insert into hash table */ | LIST_INSERT_HEAD(poh, po, po_next); /* insert into hash table */ | ||||
TAILQ_INIT(&po->po_logbuffers); | TAILQ_INIT(&po->po_logbuffers); | ||||
mtx_init(&po->po_mtx, "pmc-owner-mtx", "pmc-per-proc", MTX_SPIN); | mtx_init(&po->po_mtx, "pmc-owner-mtx", "pmc-per-proc", MTX_SPIN); | ||||
PMCDBG(OWN,ALL,1, "allocate-owner proc=%p (%d, %s) pmc-owner=%p", | PMCDBG4(OWN,ALL,1, "allocate-owner proc=%p (%d, %s) pmc-owner=%p", | ||||
p, p->p_pid, p->p_comm, po); | p, p->p_pid, p->p_comm, po); | ||||
return po; | return po; | ||||
} | } | ||||
static void | static void | ||||
pmc_destroy_owner_descriptor(struct pmc_owner *po) | pmc_destroy_owner_descriptor(struct pmc_owner *po) | ||||
{ | { | ||||
PMCDBG(OWN,REL,1, "destroy-owner po=%p proc=%p (%d, %s)", | PMCDBG4(OWN,REL,1, "destroy-owner po=%p proc=%p (%d, %s)", | ||||
po, po->po_owner, po->po_owner->p_pid, po->po_owner->p_comm); | po, po->po_owner, po->po_owner->p_pid, po->po_owner->p_comm); | ||||
mtx_destroy(&po->po_mtx); | mtx_destroy(&po->po_mtx); | ||||
free(po, M_PMC); | free(po, M_PMC); | ||||
} | } | ||||
/* | /* | ||||
* find the descriptor corresponding to process 'p', adding or removing it | * find the descriptor corresponding to process 'p', adding or removing it | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | pmc_find_owner_descriptor(struct proc *p) | ||||
hindex = PMC_HASH_PTR(p, pmc_ownerhashmask); | hindex = PMC_HASH_PTR(p, pmc_ownerhashmask); | ||||
poh = &pmc_ownerhash[hindex]; | poh = &pmc_ownerhash[hindex]; | ||||
po = NULL; | po = NULL; | ||||
LIST_FOREACH(po, poh, po_next) | LIST_FOREACH(po, poh, po_next) | ||||
if (po->po_owner == p) | if (po->po_owner == p) | ||||
break; | break; | ||||
PMCDBG(OWN,FND,1, "find-owner proc=%p (%d, %s) hindex=0x%x -> " | PMCDBG5(OWN,FND,1, "find-owner proc=%p (%d, %s) hindex=0x%x -> " | ||||
"pmc-owner=%p", p, p->p_pid, p->p_comm, hindex, po); | "pmc-owner=%p", p, p->p_pid, p->p_comm, hindex, po); | ||||
return po; | return po; | ||||
} | } | ||||
/* | /* | ||||
* pmc_allocate_pmc_descriptor | * pmc_allocate_pmc_descriptor | ||||
* | * | ||||
* Allocate a pmc descriptor and initialize its | * Allocate a pmc descriptor and initialize its | ||||
* fields. | * fields. | ||||
*/ | */ | ||||
static struct pmc * | static struct pmc * | ||||
pmc_allocate_pmc_descriptor(void) | pmc_allocate_pmc_descriptor(void) | ||||
{ | { | ||||
struct pmc *pmc; | struct pmc *pmc; | ||||
pmc = malloc(sizeof(struct pmc), M_PMC, M_WAITOK|M_ZERO); | pmc = malloc(sizeof(struct pmc), M_PMC, M_WAITOK|M_ZERO); | ||||
PMCDBG(PMC,ALL,1, "allocate-pmc -> pmc=%p", pmc); | PMCDBG1(PMC,ALL,1, "allocate-pmc -> pmc=%p", pmc); | ||||
return pmc; | return pmc; | ||||
} | } | ||||
/* | /* | ||||
* Destroy a pmc descriptor. | * Destroy a pmc descriptor. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | pmc_release_pmc_descriptor(struct pmc *pm) | ||||
sx_assert(&pmc_sx, SX_XLOCKED); | sx_assert(&pmc_sx, SX_XLOCKED); | ||||
KASSERT(pm, ("[pmc,%d] null pmc", __LINE__)); | KASSERT(pm, ("[pmc,%d] null pmc", __LINE__)); | ||||
ri = PMC_TO_ROWINDEX(pm); | ri = PMC_TO_ROWINDEX(pm); | ||||
pcd = pmc_ri_to_classdep(md, ri, &adjri); | pcd = pmc_ri_to_classdep(md, ri, &adjri); | ||||
mode = PMC_TO_MODE(pm); | mode = PMC_TO_MODE(pm); | ||||
PMCDBG(PMC,REL,1, "release-pmc pmc=%p ri=%d mode=%d", pm, ri, | PMCDBG3(PMC,REL,1, "release-pmc pmc=%p ri=%d mode=%d", pm, ri, | ||||
mode); | mode); | ||||
/* | /* | ||||
* First, we take the PMC off hardware. | * First, we take the PMC off hardware. | ||||
*/ | */ | ||||
cpu = 0; | cpu = 0; | ||||
if (PMC_IS_SYSTEM_MODE(mode)) { | if (PMC_IS_SYSTEM_MODE(mode)) { | ||||
Show All 11 Lines | if (PMC_IS_SYSTEM_MODE(mode)) { | ||||
if (pm->pm_state == PMC_STATE_RUNNING && | if (pm->pm_state == PMC_STATE_RUNNING && | ||||
pm->pm_stalled == 0) { | pm->pm_stalled == 0) { | ||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri]; | phw = pmc_pcpu[cpu]->pc_hwpmcs[ri]; | ||||
KASSERT(phw->phw_pmc == pm, | KASSERT(phw->phw_pmc == pm, | ||||
("[pmc, %d] pmc ptr ri(%d) hw(%p) pm(%p)", | ("[pmc, %d] pmc ptr ri(%d) hw(%p) pm(%p)", | ||||
__LINE__, ri, phw->phw_pmc, pm)); | __LINE__, ri, phw->phw_pmc, pm)); | ||||
PMCDBG(PMC,REL,2, "stopping cpu=%d ri=%d", cpu, ri); | PMCDBG2(PMC,REL,2, "stopping cpu=%d ri=%d", cpu, ri); | ||||
critical_enter(); | critical_enter(); | ||||
pcd->pcd_stop_pmc(cpu, adjri); | pcd->pcd_stop_pmc(cpu, adjri); | ||||
critical_exit(); | critical_exit(); | ||||
} | } | ||||
PMCDBG(PMC,REL,2, "decfg cpu=%d ri=%d", cpu, ri); | PMCDBG2(PMC,REL,2, "decfg cpu=%d ri=%d", cpu, ri); | ||||
critical_enter(); | critical_enter(); | ||||
pcd->pcd_config_pmc(cpu, adjri, NULL); | pcd->pcd_config_pmc(cpu, adjri, NULL); | ||||
critical_exit(); | critical_exit(); | ||||
/* adjust the global and process count of SS mode PMCs */ | /* adjust the global and process count of SS mode PMCs */ | ||||
if (mode == PMC_MODE_SS && pm->pm_state == PMC_STATE_RUNNING) { | if (mode == PMC_MODE_SS && pm->pm_state == PMC_STATE_RUNNING) { | ||||
po = pm->pm_owner; | po = pm->pm_owner; | ||||
Show All 39 Lines | if (PMC_IS_SYSTEM_MODE(mode)) { | ||||
* process-record's refcount falls to zero, we remove | * process-record's refcount falls to zero, we remove | ||||
* it from the hash table. The module-wide SX lock | * it from the hash table. The module-wide SX lock | ||||
* protects us from races. | * protects us from races. | ||||
*/ | */ | ||||
LIST_FOREACH_SAFE(ptgt, &pm->pm_targets, pt_next, tmp) { | LIST_FOREACH_SAFE(ptgt, &pm->pm_targets, pt_next, tmp) { | ||||
pp = ptgt->pt_process; | pp = ptgt->pt_process; | ||||
pmc_unlink_target_process(pm, pp); /* frees 'ptgt' */ | pmc_unlink_target_process(pm, pp); /* frees 'ptgt' */ | ||||
PMCDBG(PMC,REL,3, "pp->refcnt=%d", pp->pp_refcnt); | PMCDBG1(PMC,REL,3, "pp->refcnt=%d", pp->pp_refcnt); | ||||
/* | /* | ||||
* If the target process record shows that no | * If the target process record shows that no | ||||
* PMCs are attached to it, reclaim its space. | * PMCs are attached to it, reclaim its space. | ||||
*/ | */ | ||||
if (pp->pp_refcnt == 0) { | if (pp->pp_refcnt == 0) { | ||||
pmc_remove_process_descriptor(pp); | pmc_remove_process_descriptor(pp); | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | pmc_register_owner(struct proc *p, struct pmc *pmc) | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
p->p_flag |= P_HWPMC; | p->p_flag |= P_HWPMC; | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
if (po->po_flags & PMC_PO_OWNS_LOGFILE) | if (po->po_flags & PMC_PO_OWNS_LOGFILE) | ||||
pmclog_process_pmcallocate(pmc); | pmclog_process_pmcallocate(pmc); | ||||
PMCDBG(PMC,REG,1, "register-owner pmc-owner=%p pmc=%p", | PMCDBG2(PMC,REG,1, "register-owner pmc-owner=%p pmc=%p", | ||||
po, pmc); | po, pmc); | ||||
return 0; | return 0; | ||||
} | } | ||||
/* | /* | ||||
* Return the current row disposition: | * Return the current row disposition: | ||||
* == 0 => FREE | * == 0 => FREE | ||||
Show All 21 Lines | |||||
static int | static int | ||||
pmc_can_allocate_rowindex(struct proc *p, unsigned int ri, int cpu) | pmc_can_allocate_rowindex(struct proc *p, unsigned int ri, int cpu) | ||||
{ | { | ||||
enum pmc_mode mode; | enum pmc_mode mode; | ||||
struct pmc *pm; | struct pmc *pm; | ||||
struct pmc_owner *po; | struct pmc_owner *po; | ||||
struct pmc_process *pp; | struct pmc_process *pp; | ||||
PMCDBG(PMC,ALR,1, "can-allocate-rowindex proc=%p (%d, %s) ri=%d " | PMCDBG5(PMC,ALR,1, "can-allocate-rowindex proc=%p (%d, %s) ri=%d " | ||||
"cpu=%d", p, p->p_pid, p->p_comm, ri, cpu); | "cpu=%d", p, p->p_pid, p->p_comm, ri, cpu); | ||||
/* | /* | ||||
* We shouldn't have already allocated a process-mode PMC at | * We shouldn't have already allocated a process-mode PMC at | ||||
* row index 'ri'. | * row index 'ri'. | ||||
* | * | ||||
* We shouldn't have allocated a system-wide PMC on the same | * We shouldn't have allocated a system-wide PMC on the same | ||||
* CPU and same RI. | * CPU and same RI. | ||||
Show All 13 Lines | pmc_can_allocate_rowindex(struct proc *p, unsigned int ri, int cpu) | ||||
/* | /* | ||||
* We also shouldn't be the target of any PMC at this index | * We also shouldn't be the target of any PMC at this index | ||||
* since otherwise a PMC_ATTACH to ourselves will fail. | * since otherwise a PMC_ATTACH to ourselves will fail. | ||||
*/ | */ | ||||
if ((pp = pmc_find_process_descriptor(p, 0)) != NULL) | if ((pp = pmc_find_process_descriptor(p, 0)) != NULL) | ||||
if (pp->pp_pmcs[ri].pp_pmc) | if (pp->pp_pmcs[ri].pp_pmc) | ||||
return EEXIST; | return EEXIST; | ||||
PMCDBG(PMC,ALR,2, "can-allocate-rowindex proc=%p (%d, %s) ri=%d ok", | PMCDBG4(PMC,ALR,2, "can-allocate-rowindex proc=%p (%d, %s) ri=%d ok", | ||||
p, p->p_pid, p->p_comm, ri); | p, p->p_pid, p->p_comm, ri); | ||||
return 0; | return 0; | ||||
} | } | ||||
/* | /* | ||||
* Check if a given PMC at row index 'ri' can be currently used in | * Check if a given PMC at row index 'ri' can be currently used in | ||||
* mode 'mode'. | * mode 'mode'. | ||||
*/ | */ | ||||
static int | static int | ||||
pmc_can_allocate_row(int ri, enum pmc_mode mode) | pmc_can_allocate_row(int ri, enum pmc_mode mode) | ||||
{ | { | ||||
enum pmc_disp disp; | enum pmc_disp disp; | ||||
sx_assert(&pmc_sx, SX_XLOCKED); | sx_assert(&pmc_sx, SX_XLOCKED); | ||||
PMCDBG(PMC,ALR,1, "can-allocate-row ri=%d mode=%d", ri, mode); | PMCDBG2(PMC,ALR,1, "can-allocate-row ri=%d mode=%d", ri, mode); | ||||
if (PMC_IS_SYSTEM_MODE(mode)) | if (PMC_IS_SYSTEM_MODE(mode)) | ||||
disp = PMC_DISP_STANDALONE; | disp = PMC_DISP_STANDALONE; | ||||
else | else | ||||
disp = PMC_DISP_THREAD; | disp = PMC_DISP_THREAD; | ||||
/* | /* | ||||
* check disposition for PMC row 'ri': | * check disposition for PMC row 'ri': | ||||
Show All 10 Lines | if (!PMC_ROW_DISP_IS_FREE(ri) && | ||||
!(disp == PMC_DISP_THREAD && PMC_ROW_DISP_IS_THREAD(ri)) && | !(disp == PMC_DISP_THREAD && PMC_ROW_DISP_IS_THREAD(ri)) && | ||||
!(disp == PMC_DISP_STANDALONE && PMC_ROW_DISP_IS_STANDALONE(ri))) | !(disp == PMC_DISP_STANDALONE && PMC_ROW_DISP_IS_STANDALONE(ri))) | ||||
return EBUSY; | return EBUSY; | ||||
/* | /* | ||||
* All OK | * All OK | ||||
*/ | */ | ||||
PMCDBG(PMC,ALR,2, "can-allocate-row ri=%d mode=%d ok", ri, mode); | PMCDBG2(PMC,ALR,2, "can-allocate-row ri=%d mode=%d ok", ri, mode); | ||||
return 0; | return 0; | ||||
} | } | ||||
/* | /* | ||||
* Find a PMC descriptor with user handle 'pmcid' for thread 'td'. | * Find a PMC descriptor with user handle 'pmcid' for thread 'td'. | ||||
*/ | */ | ||||
Show All 16 Lines | |||||
static int | static int | ||||
pmc_find_pmc(pmc_id_t pmcid, struct pmc **pmc) | pmc_find_pmc(pmc_id_t pmcid, struct pmc **pmc) | ||||
{ | { | ||||
struct pmc *pm; | struct pmc *pm; | ||||
struct pmc_owner *po; | struct pmc_owner *po; | ||||
PMCDBG(PMC,FND,1, "find-pmc id=%d", pmcid); | PMCDBG1(PMC,FND,1, "find-pmc id=%d", pmcid); | ||||
if ((po = pmc_find_owner_descriptor(curthread->td_proc)) == NULL) | if ((po = pmc_find_owner_descriptor(curthread->td_proc)) == NULL) | ||||
return ESRCH; | return ESRCH; | ||||
if ((pm = pmc_find_pmc_descriptor_in_process(po, pmcid)) == NULL) | if ((pm = pmc_find_pmc_descriptor_in_process(po, pmcid)) == NULL) | ||||
return EINVAL; | return EINVAL; | ||||
PMCDBG(PMC,FND,2, "find-pmc id=%d -> pmc=%p", pmcid, pm); | PMCDBG2(PMC,FND,2, "find-pmc id=%d -> pmc=%p", pmcid, pm); | ||||
*pmc = pm; | *pmc = pm; | ||||
return 0; | return 0; | ||||
} | } | ||||
/* | /* | ||||
* Start a PMC. | * Start a PMC. | ||||
*/ | */ | ||||
Show All 11 Lines | KASSERT(pm != NULL, | ||||
("[pmc,%d] null pm", __LINE__)); | ("[pmc,%d] null pm", __LINE__)); | ||||
mode = PMC_TO_MODE(pm); | mode = PMC_TO_MODE(pm); | ||||
ri = PMC_TO_ROWINDEX(pm); | ri = PMC_TO_ROWINDEX(pm); | ||||
pcd = pmc_ri_to_classdep(md, ri, &adjri); | pcd = pmc_ri_to_classdep(md, ri, &adjri); | ||||
error = 0; | error = 0; | ||||
PMCDBG(PMC,OPS,1, "start pmc=%p mode=%d ri=%d", pm, mode, ri); | PMCDBG3(PMC,OPS,1, "start pmc=%p mode=%d ri=%d", pm, mode, ri); | ||||
po = pm->pm_owner; | po = pm->pm_owner; | ||||
/* | /* | ||||
* Disallow PMCSTART if a logfile is required but has not been | * Disallow PMCSTART if a logfile is required but has not been | ||||
* configured yet. | * configured yet. | ||||
*/ | */ | ||||
if ((pm->pm_flags & PMC_F_NEEDS_LOGFILE) && | if ((pm->pm_flags & PMC_F_NEEDS_LOGFILE) && | ||||
Show All 39 Lines | pmc_start(struct pmc *pm) | ||||
* Add the owner to the global list if this is a system-wide | * Add the owner to the global list if this is a system-wide | ||||
* sampling PMC. | * sampling PMC. | ||||
*/ | */ | ||||
if (mode == PMC_MODE_SS) { | if (mode == PMC_MODE_SS) { | ||||
if (po->po_sscount == 0) { | if (po->po_sscount == 0) { | ||||
LIST_INSERT_HEAD(&pmc_ss_owners, po, po_ssnext); | LIST_INSERT_HEAD(&pmc_ss_owners, po, po_ssnext); | ||||
atomic_add_rel_int(&pmc_ss_count, 1); | atomic_add_rel_int(&pmc_ss_count, 1); | ||||
PMCDBG(PMC,OPS,1, "po=%p in global list", po); | PMCDBG1(PMC,OPS,1, "po=%p in global list", po); | ||||
} | } | ||||
po->po_sscount++; | po->po_sscount++; | ||||
/* | /* | ||||
* Log mapping information for all existing processes in the | * Log mapping information for all existing processes in the | ||||
* system. Subsequent mappings are logged as they happen; | * system. Subsequent mappings are logged as they happen; | ||||
* see pmc_process_mmap(). | * see pmc_process_mmap(). | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct pmc_owner *po; | struct pmc_owner *po; | ||||
struct pmc_binding pb; | struct pmc_binding pb; | ||||
struct pmc_classdep *pcd; | struct pmc_classdep *pcd; | ||||
int adjri, cpu, error, ri; | int adjri, cpu, error, ri; | ||||
KASSERT(pm != NULL, ("[pmc,%d] null pmc", __LINE__)); | KASSERT(pm != NULL, ("[pmc,%d] null pmc", __LINE__)); | ||||
PMCDBG(PMC,OPS,1, "stop pmc=%p mode=%d ri=%d", pm, | PMCDBG3(PMC,OPS,1, "stop pmc=%p mode=%d ri=%d", pm, | ||||
PMC_TO_MODE(pm), PMC_TO_ROWINDEX(pm)); | PMC_TO_MODE(pm), PMC_TO_ROWINDEX(pm)); | ||||
pm->pm_state = PMC_STATE_STOPPED; | pm->pm_state = PMC_STATE_STOPPED; | ||||
/* | /* | ||||
* If the PMC is a virtual mode one, changing the state to | * If the PMC is a virtual mode one, changing the state to | ||||
* non-RUNNING is enough to ensure that the PMC never gets | * non-RUNNING is enough to ensure that the PMC never gets | ||||
* scheduled. | * scheduled. | ||||
Show All 38 Lines | pmc_stop(struct pmc *pm) | ||||
po = pm->pm_owner; | po = pm->pm_owner; | ||||
/* remove this owner from the global list of SS PMC owners */ | /* remove this owner from the global list of SS PMC owners */ | ||||
if (PMC_TO_MODE(pm) == PMC_MODE_SS) { | if (PMC_TO_MODE(pm) == PMC_MODE_SS) { | ||||
po->po_sscount--; | po->po_sscount--; | ||||
if (po->po_sscount == 0) { | if (po->po_sscount == 0) { | ||||
atomic_subtract_rel_int(&pmc_ss_count, 1); | atomic_subtract_rel_int(&pmc_ss_count, 1); | ||||
LIST_REMOVE(po, po_ssnext); | LIST_REMOVE(po, po_ssnext); | ||||
PMCDBG(PMC,OPS,2,"po=%p removed from global list", po); | PMCDBG1(PMC,OPS,2,"po=%p removed from global list", po); | ||||
} | } | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
#ifdef HWPMC_DEBUG | #ifdef HWPMC_DEBUG | ||||
Show All 36 Lines | pmc_syscall_handler(struct thread *td, void *syscall_args) | ||||
is_sx_downgraded = 0; | is_sx_downgraded = 0; | ||||
is_sx_locked = 1; | is_sx_locked = 1; | ||||
c = (struct pmc_syscall_args *) syscall_args; | c = (struct pmc_syscall_args *) syscall_args; | ||||
op = c->pmop_code; | op = c->pmop_code; | ||||
arg = c->pmop_data; | arg = c->pmop_data; | ||||
PMCDBG(MOD,PMS,1, "syscall op=%d \"%s\" arg=%p", op, | PMCDBG3(MOD,PMS,1, "syscall op=%d \"%s\" arg=%p", op, | ||||
pmc_op_to_name[op], arg); | pmc_op_to_name[op], arg); | ||||
error = 0; | error = 0; | ||||
atomic_add_int(&pmc_stats.pm_syscalls, 1); | atomic_add_int(&pmc_stats.pm_syscalls, 1); | ||||
switch(op) | switch(op) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 496 Lines • ▼ Show 20 Lines | case PMC_OP_PMCALLOCATE: | ||||
} | } | ||||
/* The requested PMC capabilities should be feasible. */ | /* The requested PMC capabilities should be feasible. */ | ||||
if ((md->pmd_classdep[n].pcd_caps & caps) != caps) { | if ((md->pmd_classdep[n].pcd_caps & caps) != caps) { | ||||
error = EOPNOTSUPP; | error = EOPNOTSUPP; | ||||
break; | break; | ||||
} | } | ||||
PMCDBG(PMC,ALL,2, "event=%d caps=0x%x mode=%d cpu=%d", | PMCDBG4(PMC,ALL,2, "event=%d caps=0x%x mode=%d cpu=%d", | ||||
pa.pm_ev, caps, mode, cpu); | pa.pm_ev, caps, mode, cpu); | ||||
pmc = pmc_allocate_pmc_descriptor(); | pmc = pmc_allocate_pmc_descriptor(); | ||||
pmc->pm_id = PMC_ID_MAKE_ID(cpu,pa.pm_mode,pa.pm_class, | pmc->pm_id = PMC_ID_MAKE_ID(cpu,pa.pm_mode,pa.pm_class, | ||||
PMC_ID_INVALID); | PMC_ID_INVALID); | ||||
pmc->pm_event = pa.pm_ev; | pmc->pm_event = pa.pm_ev; | ||||
pmc->pm_state = PMC_STATE_FREE; | pmc->pm_state = PMC_STATE_FREE; | ||||
pmc->pm_caps = caps; | pmc->pm_caps = caps; | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if (n == (int) md->pmd_npmc) { | ||||
pmc = NULL; | pmc = NULL; | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
/* Fill in the correct value in the ID field */ | /* Fill in the correct value in the ID field */ | ||||
pmc->pm_id = PMC_ID_MAKE_ID(cpu,mode,pa.pm_class,n); | pmc->pm_id = PMC_ID_MAKE_ID(cpu,mode,pa.pm_class,n); | ||||
PMCDBG(PMC,ALL,2, "ev=%d class=%d mode=%d n=%d -> pmcid=%x", | PMCDBG5(PMC,ALL,2, "ev=%d class=%d mode=%d n=%d -> pmcid=%x", | ||||
pmc->pm_event, pa.pm_class, mode, n, pmc->pm_id); | pmc->pm_event, pa.pm_class, mode, n, pmc->pm_id); | ||||
/* Process mode PMCs with logging enabled need log files */ | /* Process mode PMCs with logging enabled need log files */ | ||||
if (pmc->pm_flags & (PMC_F_LOG_PROCEXIT | PMC_F_LOG_PROCCSW)) | if (pmc->pm_flags & (PMC_F_LOG_PROCEXIT | PMC_F_LOG_PROCCSW)) | ||||
pmc->pm_flags |= PMC_F_NEEDS_LOGFILE; | pmc->pm_flags |= PMC_F_NEEDS_LOGFILE; | ||||
/* All system mode sampling PMCs require a log file */ | /* All system mode sampling PMCs require a log file */ | ||||
if (PMC_IS_SAMPLING_MODE(mode) && PMC_IS_SYSTEM_MODE(mode)) | if (PMC_IS_SAMPLING_MODE(mode) && PMC_IS_SYSTEM_MODE(mode)) | ||||
▲ Show 20 Lines • Show All 306 Lines • ▼ Show 20 Lines | case PMC_OP_PMCRW: | ||||
struct pmc_op_pmcrw *pprw; | struct pmc_op_pmcrw *pprw; | ||||
PMC_DOWNGRADE_SX(); | PMC_DOWNGRADE_SX(); | ||||
if ((error = copyin(arg, &prw, sizeof(prw))) != 0) | if ((error = copyin(arg, &prw, sizeof(prw))) != 0) | ||||
break; | break; | ||||
ri = 0; | ri = 0; | ||||
PMCDBG(PMC,OPS,1, "rw id=%d flags=0x%x", prw.pm_pmcid, | PMCDBG2(PMC,OPS,1, "rw id=%d flags=0x%x", prw.pm_pmcid, | ||||
prw.pm_flags); | prw.pm_flags); | ||||
/* must have at least one flag set */ | /* must have at least one flag set */ | ||||
if ((prw.pm_flags & (PMC_F_OLDVALUE|PMC_F_NEWVALUE)) == 0) { | if ((prw.pm_flags & (PMC_F_OLDVALUE|PMC_F_NEWVALUE)) == 0) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | error: | ||||
if (error) | if (error) | ||||
break; | break; | ||||
} | } | ||||
pprw = (struct pmc_op_pmcrw *) arg; | pprw = (struct pmc_op_pmcrw *) arg; | ||||
#ifdef HWPMC_DEBUG | #ifdef HWPMC_DEBUG | ||||
if (prw.pm_flags & PMC_F_NEWVALUE) | if (prw.pm_flags & PMC_F_NEWVALUE) | ||||
PMCDBG(PMC,OPS,2, "rw id=%d new %jx -> old %jx", | PMCDBG3(PMC,OPS,2, "rw id=%d new %jx -> old %jx", | ||||
ri, prw.pm_value, oldvalue); | ri, prw.pm_value, oldvalue); | ||||
else if (prw.pm_flags & PMC_F_OLDVALUE) | else if (prw.pm_flags & PMC_F_OLDVALUE) | ||||
PMCDBG(PMC,OPS,2, "rw id=%d -> old %jx", ri, oldvalue); | PMCDBG2(PMC,OPS,2, "rw id=%d -> old %jx", ri, oldvalue); | ||||
#endif | #endif | ||||
/* return old value if requested */ | /* return old value if requested */ | ||||
if (prw.pm_flags & PMC_F_OLDVALUE) | if (prw.pm_flags & PMC_F_OLDVALUE) | ||||
if ((error = copyout(&oldvalue, &pprw->pm_value, | if ((error = copyout(&oldvalue, &pprw->pm_value, | ||||
sizeof(prw.pm_value)))) | sizeof(prw.pm_value)))) | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 226 Lines • ▼ Show 20 Lines | pmc_process_interrupt(int cpu, int ring, struct pmc *pm, struct trapframe *tf, | ||||
* Allocate space for a sample buffer. | * Allocate space for a sample buffer. | ||||
*/ | */ | ||||
psb = pmc_pcpu[cpu]->pc_sb[ring]; | psb = pmc_pcpu[cpu]->pc_sb[ring]; | ||||
ps = psb->ps_write; | ps = psb->ps_write; | ||||
if (ps->ps_nsamples) { /* in use, reader hasn't caught up */ | if (ps->ps_nsamples) { /* in use, reader hasn't caught up */ | ||||
pm->pm_stalled = 1; | pm->pm_stalled = 1; | ||||
atomic_add_int(&pmc_stats.pm_intr_bufferfull, 1); | atomic_add_int(&pmc_stats.pm_intr_bufferfull, 1); | ||||
PMCDBG(SAM,INT,1,"(spc) cpu=%d pm=%p tf=%p um=%d wr=%d rd=%d", | PMCDBG6(SAM,INT,1,"(spc) cpu=%d pm=%p tf=%p um=%d wr=%d rd=%d", | ||||
cpu, pm, (void *) tf, inuserspace, | cpu, pm, (void *) tf, inuserspace, | ||||
(int) (psb->ps_write - psb->ps_samples), | (int) (psb->ps_write - psb->ps_samples), | ||||
(int) (psb->ps_read - psb->ps_samples)); | (int) (psb->ps_read - psb->ps_samples)); | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto done; | goto done; | ||||
} | } | ||||
/* Fill in entry. */ | /* Fill in entry. */ | ||||
PMCDBG(SAM,INT,1,"cpu=%d pm=%p tf=%p um=%d wr=%d rd=%d", cpu, pm, | PMCDBG6(SAM,INT,1,"cpu=%d pm=%p tf=%p um=%d wr=%d rd=%d", cpu, pm, | ||||
(void *) tf, inuserspace, | (void *) tf, inuserspace, | ||||
(int) (psb->ps_write - psb->ps_samples), | (int) (psb->ps_write - psb->ps_samples), | ||||
(int) (psb->ps_read - psb->ps_samples)); | (int) (psb->ps_read - psb->ps_samples)); | ||||
KASSERT(pm->pm_runcount >= 0, | KASSERT(pm->pm_runcount >= 0, | ||||
("[pmc,%d] pm=%p runcount %d", __LINE__, (void *) pm, | ("[pmc,%d] pm=%p runcount %d", __LINE__, (void *) pm, | ||||
pm->pm_runcount)); | pm->pm_runcount)); | ||||
▲ Show 20 Lines • Show All 166 Lines • ▼ Show 20 Lines | for (n = 0; n < pmc_nsamples; n++) { /* bound on #iterations */ | ||||
/* If there is a pending AST wait for completion */ | /* If there is a pending AST wait for completion */ | ||||
if (ps->ps_nsamples == PMC_SAMPLE_INUSE) { | if (ps->ps_nsamples == PMC_SAMPLE_INUSE) { | ||||
/* Need a rescan at a later time. */ | /* Need a rescan at a later time. */ | ||||
CPU_SET_ATOMIC(cpu, &pmc_cpumask); | CPU_SET_ATOMIC(cpu, &pmc_cpumask); | ||||
break; | break; | ||||
} | } | ||||
PMCDBG(SAM,OPS,1,"cpu=%d pm=%p n=%d fl=%x wr=%d rd=%d", cpu, | PMCDBG6(SAM,OPS,1,"cpu=%d pm=%p n=%d fl=%x wr=%d rd=%d", cpu, | ||||
pm, ps->ps_nsamples, ps->ps_flags, | pm, ps->ps_nsamples, ps->ps_flags, | ||||
(int) (psb->ps_write - psb->ps_samples), | (int) (psb->ps_write - psb->ps_samples), | ||||
(int) (psb->ps_read - psb->ps_samples)); | (int) (psb->ps_read - psb->ps_samples)); | ||||
/* | /* | ||||
* If this is a process-mode PMC that is attached to | * If this is a process-mode PMC that is attached to | ||||
* its owner, and if the PC is in user mode, update | * its owner, and if the PC is in user mode, update | ||||
* profiling statistics like timer-based profiling | * profiling statistics like timer-based profiling | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | pmc_process_exit(void *arg __unused, struct proc *p) | ||||
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) | LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) | ||||
if (po->po_flags & PMC_PO_OWNS_LOGFILE) | if (po->po_flags & PMC_PO_OWNS_LOGFILE) | ||||
pmclog_process_sysexit(po, p->p_pid); | pmclog_process_sysexit(po, p->p_pid); | ||||
if (!is_using_hwpmcs) | if (!is_using_hwpmcs) | ||||
return; | return; | ||||
PMC_GET_SX_XLOCK(); | PMC_GET_SX_XLOCK(); | ||||
PMCDBG(PRC,EXT,1,"process-exit proc=%p (%d, %s)", p, p->p_pid, | PMCDBG3(PRC,EXT,1,"process-exit proc=%p (%d, %s)", p, p->p_pid, | ||||
p->p_comm); | p->p_comm); | ||||
/* | /* | ||||
* Since this code is invoked by the last thread in an exiting | * Since this code is invoked by the last thread in an exiting | ||||
* process, we would have context switched IN at some prior | * process, we would have context switched IN at some prior | ||||
* point. However, with PREEMPTION, kernel mode context | * point. However, with PREEMPTION, kernel mode context | ||||
* switches may happen any time, so we want to disable a | * switches may happen any time, so we want to disable a | ||||
* context switch OUT till we get any PMCs targetting this | * context switch OUT till we get any PMCs targetting this | ||||
* process off the hardware. | * process off the hardware. | ||||
* | * | ||||
* We also need to atomically remove this process' | * We also need to atomically remove this process' | ||||
* entry from our target process hash table, using | * entry from our target process hash table, using | ||||
* PMC_FLAG_REMOVE. | * PMC_FLAG_REMOVE. | ||||
*/ | */ | ||||
PMCDBG(PRC,EXT,1, "process-exit proc=%p (%d, %s)", p, p->p_pid, | PMCDBG3(PRC,EXT,1, "process-exit proc=%p (%d, %s)", p, p->p_pid, | ||||
p->p_comm); | p->p_comm); | ||||
critical_enter(); /* no preemption */ | critical_enter(); /* no preemption */ | ||||
cpu = curthread->td_oncpu; | cpu = curthread->td_oncpu; | ||||
if ((pp = pmc_find_process_descriptor(p, | if ((pp = pmc_find_process_descriptor(p, | ||||
PMC_FLAG_REMOVE)) != NULL) { | PMC_FLAG_REMOVE)) != NULL) { | ||||
PMCDBG(PRC,EXT,2, | PMCDBG2(PRC,EXT,2, | ||||
"process-exit proc=%p pmc-process=%p", p, pp); | "process-exit proc=%p pmc-process=%p", p, pp); | ||||
/* | /* | ||||
* The exiting process could the target of | * The exiting process could the target of | ||||
* some PMCs which will be running on | * some PMCs which will be running on | ||||
* currently executing CPU. | * currently executing CPU. | ||||
* | * | ||||
* We need to turn these PMCs off like we | * We need to turn these PMCs off like we | ||||
* would do at context switch OUT time. | * would do at context switch OUT time. | ||||
*/ | */ | ||||
for (ri = 0; ri < md->pmd_npmc; ri++) { | for (ri = 0; ri < md->pmd_npmc; ri++) { | ||||
/* | /* | ||||
* Pick up the pmc pointer from hardware | * Pick up the pmc pointer from hardware | ||||
* state similar to the CSW_OUT code. | * state similar to the CSW_OUT code. | ||||
*/ | */ | ||||
pm = NULL; | pm = NULL; | ||||
pcd = pmc_ri_to_classdep(md, ri, &adjri); | pcd = pmc_ri_to_classdep(md, ri, &adjri); | ||||
(void) (*pcd->pcd_get_config)(cpu, adjri, &pm); | (void) (*pcd->pcd_get_config)(cpu, adjri, &pm); | ||||
PMCDBG(PRC,EXT,2, "ri=%d pm=%p", ri, pm); | PMCDBG2(PRC,EXT,2, "ri=%d pm=%p", ri, pm); | ||||
if (pm == NULL || | if (pm == NULL || | ||||
!PMC_IS_VIRTUAL_MODE(PMC_TO_MODE(pm))) | !PMC_IS_VIRTUAL_MODE(PMC_TO_MODE(pm))) | ||||
continue; | continue; | ||||
PMCDBG(PRC,EXT,2, "ppmcs[%d]=%p pm=%p " | PMCDBG4(PRC,EXT,2, "ppmcs[%d]=%p pm=%p " | ||||
"state=%d", ri, pp->pp_pmcs[ri].pp_pmc, | "state=%d", ri, pp->pp_pmcs[ri].pp_pmc, | ||||
pm, pm->pm_state); | pm, pm->pm_state); | ||||
KASSERT(PMC_TO_ROWINDEX(pm) == ri, | KASSERT(PMC_TO_ROWINDEX(pm) == ri, | ||||
("[pmc,%d] ri mismatch pmc(%d) ri(%d)", | ("[pmc,%d] ri mismatch pmc(%d) ri(%d)", | ||||
__LINE__, PMC_TO_ROWINDEX(pm), ri)); | __LINE__, PMC_TO_ROWINDEX(pm), ri)); | ||||
KASSERT(pm == pp->pp_pmcs[ri].pp_pmc, | KASSERT(pm == pp->pp_pmcs[ri].pp_pmc, | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | pmc_process_fork(void *arg __unused, struct proc *p1, struct proc *newproc, | ||||
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) | LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) | ||||
if (po->po_flags & PMC_PO_OWNS_LOGFILE) | if (po->po_flags & PMC_PO_OWNS_LOGFILE) | ||||
pmclog_process_procfork(po, p1->p_pid, newproc->p_pid); | pmclog_process_procfork(po, p1->p_pid, newproc->p_pid); | ||||
if (!is_using_hwpmcs) | if (!is_using_hwpmcs) | ||||
return; | return; | ||||
PMC_GET_SX_XLOCK(); | PMC_GET_SX_XLOCK(); | ||||
PMCDBG(PMC,FRK,1, "process-fork proc=%p (%d, %s) -> %p", p1, | PMCDBG4(PMC,FRK,1, "process-fork proc=%p (%d, %s) -> %p", p1, | ||||
p1->p_pid, p1->p_comm, newproc); | p1->p_pid, p1->p_comm, newproc); | ||||
/* | /* | ||||
* If the parent process (curthread->td_proc) is a | * If the parent process (curthread->td_proc) is a | ||||
* target of any PMCs, look for PMCs that are to be | * target of any PMCs, look for PMCs that are to be | ||||
* inherited, and link these into the new process | * inherited, and link these into the new process | ||||
* descriptor. | * descriptor. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 178 Lines • ▼ Show 20 Lines | |||||
#ifdef HWPMC_DEBUG | #ifdef HWPMC_DEBUG | ||||
/* parse debug flags first */ | /* parse debug flags first */ | ||||
if (TUNABLE_STR_FETCH(PMC_SYSCTL_NAME_PREFIX "debugflags", | if (TUNABLE_STR_FETCH(PMC_SYSCTL_NAME_PREFIX "debugflags", | ||||
pmc_debugstr, sizeof(pmc_debugstr))) | pmc_debugstr, sizeof(pmc_debugstr))) | ||||
pmc_debugflags_parse(pmc_debugstr, | pmc_debugflags_parse(pmc_debugstr, | ||||
pmc_debugstr+strlen(pmc_debugstr)); | pmc_debugstr+strlen(pmc_debugstr)); | ||||
#endif | #endif | ||||
PMCDBG(MOD,INI,0, "PMC Initialize (version %x)", PMC_VERSION); | PMCDBG1(MOD,INI,0, "PMC Initialize (version %x)", PMC_VERSION); | ||||
/* check kernel version */ | /* check kernel version */ | ||||
if (pmc_kernel_version != PMC_VERSION) { | if (pmc_kernel_version != PMC_VERSION) { | ||||
if (pmc_kernel_version == 0) | if (pmc_kernel_version == 0) | ||||
printf("hwpmc: this kernel has not been compiled with " | printf("hwpmc: this kernel has not been compiled with " | ||||
"'options HWPMC_HOOKS'.\n"); | "'options HWPMC_HOOKS'.\n"); | ||||
else | else | ||||
printf("hwpmc: kernel version (0x%x) does not match " | printf("hwpmc: kernel version (0x%x) does not match " | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | #endif | ||||
LIST_INIT(&pmc_ss_owners); | LIST_INIT(&pmc_ss_owners); | ||||
pmc_ss_count = 0; | pmc_ss_count = 0; | ||||
/* allocate a pool of spin mutexes */ | /* allocate a pool of spin mutexes */ | ||||
pmc_mtxpool = mtx_pool_create("pmc-leaf", pmc_mtxpool_size, | pmc_mtxpool = mtx_pool_create("pmc-leaf", pmc_mtxpool_size, | ||||
MTX_SPIN); | MTX_SPIN); | ||||
PMCDBG(MOD,INI,1, "pmc_ownerhash=%p, mask=0x%lx " | PMCDBG4(MOD,INI,1, "pmc_ownerhash=%p, mask=0x%lx " | ||||
"targethash=%p mask=0x%lx", pmc_ownerhash, pmc_ownerhashmask, | "targethash=%p mask=0x%lx", pmc_ownerhash, pmc_ownerhashmask, | ||||
pmc_processhash, pmc_processhashmask); | pmc_processhash, pmc_processhashmask); | ||||
/* register process {exit,fork,exec} handlers */ | /* register process {exit,fork,exec} handlers */ | ||||
pmc_exit_tag = EVENTHANDLER_REGISTER(process_exit, | pmc_exit_tag = EVENTHANDLER_REGISTER(process_exit, | ||||
pmc_process_exit, NULL, EVENTHANDLER_PRI_ANY); | pmc_process_exit, NULL, EVENTHANDLER_PRI_ANY); | ||||
pmc_fork_tag = EVENTHANDLER_REGISTER(process_fork, | pmc_fork_tag = EVENTHANDLER_REGISTER(process_fork, | ||||
pmc_process_fork, NULL, EVENTHANDLER_PRI_ANY); | pmc_process_fork, NULL, EVENTHANDLER_PRI_ANY); | ||||
Show All 39 Lines | pmc_cleanup(void) | ||||
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; | ||||
#ifdef HWPMC_DEBUG | #ifdef HWPMC_DEBUG | ||||
struct pmc_processhash *prh; | struct pmc_processhash *prh; | ||||
#endif | #endif | ||||
PMCDBG(MOD,INI,0, "%s", "cleanup"); | PMCDBG0(MOD,INI,0, "cleanup"); | ||||
/* switch off sampling */ | /* switch off sampling */ | ||||
CPU_ZERO(&pmc_cpumask); | CPU_ZERO(&pmc_cpumask); | ||||
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); | ||||
Show All 12 Lines | #endif | ||||
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++) { | ||||
LIST_FOREACH_SAFE(po, ph, po_next, tmp) { | LIST_FOREACH_SAFE(po, ph, po_next, tmp) { | ||||
pmc_remove_owner(po); | pmc_remove_owner(po); | ||||
/* send SIGBUS to owner processes */ | /* send SIGBUS to owner processes */ | ||||
PMCDBG(MOD,INI,2, "cleanup signal proc=%p " | PMCDBG3(MOD,INI,2, "cleanup signal proc=%p " | ||||
"(%d, %s)", po->po_owner, | "(%d, %s)", po->po_owner, | ||||
po->po_owner->p_pid, | po->po_owner->p_pid, | ||||
po->po_owner->p_comm); | po->po_owner->p_comm); | ||||
PROC_LOCK(po->po_owner); | PROC_LOCK(po->po_owner); | ||||
kern_psignal(po->po_owner, SIGBUS); | kern_psignal(po->po_owner, SIGBUS); | ||||
PROC_UNLOCK(po->po_owner); | PROC_UNLOCK(po->po_owner); | ||||
pmc_destroy_owner_descriptor(po); | pmc_destroy_owner_descriptor(po); | ||||
} | } | ||||
} | } | ||||
/* reclaim allocated data structures */ | /* reclaim allocated data structures */ | ||||
if (pmc_mtxpool) | if (pmc_mtxpool) | ||||
mtx_pool_destroy(&pmc_mtxpool); | mtx_pool_destroy(&pmc_mtxpool); | ||||
mtx_destroy(&pmc_processhash_mtx); | mtx_destroy(&pmc_processhash_mtx); | ||||
if (pmc_processhash) { | if (pmc_processhash) { | ||||
#ifdef HWPMC_DEBUG | #ifdef HWPMC_DEBUG | ||||
struct pmc_process *pp; | struct pmc_process *pp; | ||||
PMCDBG(MOD,INI,3, "%s", "destroy process hash"); | PMCDBG0(MOD,INI,3, "destroy process hash"); | ||||
for (prh = pmc_processhash; | for (prh = pmc_processhash; | ||||
prh <= &pmc_processhash[pmc_processhashmask]; | prh <= &pmc_processhash[pmc_processhashmask]; | ||||
prh++) | prh++) | ||||
LIST_FOREACH(pp, prh, pp_next) | LIST_FOREACH(pp, prh, pp_next) | ||||
PMCDBG(MOD,INI,3, "pid=%d", pp->pp_proc->p_pid); | PMCDBG1(MOD,INI,3, "pid=%d", pp->pp_proc->p_pid); | ||||
#endif | #endif | ||||
hashdestroy(pmc_processhash, M_PMC, pmc_processhashmask); | hashdestroy(pmc_processhash, M_PMC, pmc_processhashmask); | ||||
pmc_processhash = NULL; | pmc_processhash = NULL; | ||||
} | } | ||||
if (pmc_ownerhash) { | if (pmc_ownerhash) { | ||||
PMCDBG(MOD,INI,3, "%s", "destroy owner hash"); | PMCDBG0(MOD,INI,3, "destroy owner hash"); | ||||
hashdestroy(pmc_ownerhash, M_PMC, pmc_ownerhashmask); | hashdestroy(pmc_ownerhash, M_PMC, pmc_ownerhashmask); | ||||
pmc_ownerhash = NULL; | pmc_ownerhash = NULL; | ||||
} | } | ||||
KASSERT(LIST_EMPTY(&pmc_ss_owners), | KASSERT(LIST_EMPTY(&pmc_ss_owners), | ||||
("[pmc,%d] Global SS owner list not empty", __LINE__)); | ("[pmc,%d] Global SS owner list not empty", __LINE__)); | ||||
KASSERT(pmc_ss_count == 0, | KASSERT(pmc_ss_count == 0, | ||||
("[pmc,%d] Global SS count not empty", __LINE__)); | ("[pmc,%d] Global SS count not empty", __LINE__)); | ||||
/* do processor and pmc-class dependent cleanup */ | /* do processor and pmc-class dependent cleanup */ | ||||
maxcpu = pmc_cpu_max(); | maxcpu = pmc_cpu_max(); | ||||
PMCDBG(MOD,INI,3, "%s", "md cleanup"); | PMCDBG0(MOD,INI,3, "md cleanup"); | ||||
if (md) { | if (md) { | ||||
pmc_save_cpu_binding(&pb); | pmc_save_cpu_binding(&pb); | ||||
for (cpu = 0; cpu < maxcpu; cpu++) { | for (cpu = 0; cpu < maxcpu; cpu++) { | ||||
PMCDBG(MOD,INI,1,"pmc-cleanup cpu=%d pcs=%p", | PMCDBG2(MOD,INI,1,"pmc-cleanup cpu=%d pcs=%p", | ||||
cpu, pmc_pcpu[cpu]); | cpu, pmc_pcpu[cpu]); | ||||
if (!pmc_cpu_is_active(cpu) || pmc_pcpu[cpu] == NULL) | if (!pmc_cpu_is_active(cpu) || pmc_pcpu[cpu] == NULL) | ||||
continue; | continue; | ||||
pmc_select_cpu(cpu); | pmc_select_cpu(cpu); | ||||
for (c = 0; c < md->pmd_nclass; c++) | for (c = 0; c < md->pmd_nclass; c++) | ||||
md->pmd_classdep[c].pcd_pcpu_fini(md, cpu); | md->pmd_classdep[c].pcd_pcpu_fini(md, cpu); | ||||
if (md->pmd_pcpu_fini) | if (md->pmd_pcpu_fini) | ||||
md->pmd_pcpu_fini(md, cpu); | md->pmd_pcpu_fini(md, cpu); | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | load (struct module *module __unused, int cmd, void *arg __unused) | ||||
error = 0; | error = 0; | ||||
switch (cmd) { | switch (cmd) { | ||||
case MOD_LOAD : | case MOD_LOAD : | ||||
/* initialize the subsystem */ | /* initialize the subsystem */ | ||||
error = pmc_initialize(); | error = pmc_initialize(); | ||||
if (error != 0) | if (error != 0) | ||||
break; | break; | ||||
PMCDBG(MOD,INI,1, "syscall=%d maxcpu=%d", | PMCDBG2(MOD,INI,1, "syscall=%d maxcpu=%d", | ||||
pmc_syscall_num, pmc_cpu_max()); | pmc_syscall_num, pmc_cpu_max()); | ||||
break; | break; | ||||
case MOD_UNLOAD : | case MOD_UNLOAD : | ||||
case MOD_SHUTDOWN: | case MOD_SHUTDOWN: | ||||
pmc_cleanup(); | pmc_cleanup(); | ||||
PMCDBG(MOD,INI,1, "%s", "unloaded"); | PMCDBG0(MOD,INI,1, "unloaded"); | ||||
break; | break; | ||||
default : | default : | ||||
error = EINVAL; /* XXX should panic(9) */ | error = EINVAL; /* XXX should panic(9) */ | ||||
break; | break; | ||||
} | } | ||||
return error; | return error; | ||||
} | } | ||||
/* memory pool */ | /* memory pool */ | ||||
MALLOC_DEFINE(M_PMC, "pmc", "Memory space for the PMC module"); | MALLOC_DEFINE(M_PMC, "pmc", "Memory space for the PMC module"); |