- Separate PMC class dependent code from other kinds of machine
dependencies. A 'struct pmc_classdep' structure describes operations on PMCs; 'struct pmc_mdep' contains one or more 'struct pmc_classdep' structures depending on the CPU in question. Inside PMC class dependent code, row indices are relative to the PMCs supported by the PMC class; MI code in "hwpmc_mod.c" translates global row indices before invoking class dependent operations. - Augment the OP_GETCPUINFO request with the number of PMCs present in a PMC class. - Move code common to Intel CPUs to file "hwpmc_intel.c". - Move TSC handling to file "hwpmc_tsc.c".
This commit is contained in:
parent
2ad65bf421
commit
e829eb6d61
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=184802
@ -35,8 +35,34 @@
|
||||
#ifndef _MACHINE_PMC_MDEP_H
|
||||
#define _MACHINE_PMC_MDEP_H 1
|
||||
|
||||
#ifdef _KERNEL
|
||||
struct pmc_mdep;
|
||||
#endif
|
||||
|
||||
#include <dev/hwpmc/hwpmc_amd.h>
|
||||
#include <dev/hwpmc/hwpmc_piv.h>
|
||||
#include <dev/hwpmc/hwpmc_tsc.h>
|
||||
|
||||
/*
|
||||
* Intel processors implementing V2 and later of the Intel performance
|
||||
* measurement architecture have PMCs of the following classes: TSC,
|
||||
* IAF and IAP.
|
||||
*/
|
||||
#define PMC_MDEP_CLASS_INDEX_TSC 0
|
||||
#define PMC_MDEP_CLASS_INDEX_K8 1
|
||||
#define PMC_MDEP_CLASS_INDEX_P4 1
|
||||
#define PMC_MDEP_CLASS_INDEX_IAF 1
|
||||
#define PMC_MDEP_CLASS_INDEX_IAP 2
|
||||
|
||||
/*
|
||||
* On the amd64 platform we support the following PMCs.
|
||||
*
|
||||
* TSC The timestamp counter
|
||||
* K8 AMD Athlon64 and Opteron PMCs in 64 bit mode.
|
||||
* PIV Intel P4/HTT and P4/EMT64
|
||||
* IAP Intel Core/Core2/Atom CPUs in 64 bits mode.
|
||||
* IAF Intel fixed-function PMCs in Core2 and later CPUs.
|
||||
*/
|
||||
|
||||
union pmc_md_op_pmcallocate {
|
||||
struct pmc_md_amd_op_pmcallocate pm_amd;
|
||||
@ -55,8 +81,6 @@ union pmc_md_pmc {
|
||||
struct pmc_md_p4_pmc pm_p4;
|
||||
};
|
||||
|
||||
struct pmc;
|
||||
|
||||
#define PMC_TRAPFRAME_TO_PC(TF) ((TF)->tf_rip)
|
||||
#define PMC_TRAPFRAME_TO_FP(TF) ((TF)->tf_rbp)
|
||||
#define PMC_TRAPFRAME_TO_USER_SP(TF) ((TF)->tf_rsp)
|
||||
@ -88,5 +112,10 @@ struct pmc;
|
||||
void start_exceptions(void), end_exceptions(void);
|
||||
void pmc_x86_lapic_enable_pmc_interrupt(void);
|
||||
|
||||
#endif
|
||||
struct pmc_mdep *pmc_amd_initialize(void);
|
||||
void pmc_amd_finalize(struct pmc_mdep *_md);
|
||||
struct pmc_mdep *pmc_intel_initialize(void);
|
||||
void pmc_intel_finalize(struct pmc_mdep *_md);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
#endif /* _MACHINE_PMC_MDEP_H */
|
||||
|
@ -188,7 +188,9 @@ dev/hptrr/hptrr_os_bsd.c optional hptrr
|
||||
dev/hptrr/hptrr_osm_bsd.c optional hptrr
|
||||
dev/hptrr/hptrr_config.c optional hptrr
|
||||
dev/hwpmc/hwpmc_amd.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_intel.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_piv.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_tsc.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_x86.c optional hwpmc
|
||||
dev/k8temp/k8temp.c optional k8temp
|
||||
dev/kbd/kbd.c optional atkbd | sc | ukbd
|
||||
|
@ -186,9 +186,11 @@ dev/hptrr/hptrr_os_bsd.c optional hptrr
|
||||
dev/hptrr/hptrr_osm_bsd.c optional hptrr
|
||||
dev/hptrr/hptrr_config.c optional hptrr
|
||||
dev/hwpmc/hwpmc_amd.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_intel.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_pentium.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_piv.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_ppro.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_tsc.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_x86.c optional hwpmc
|
||||
dev/ichwd/ichwd.c optional ichwd
|
||||
dev/if_ndis/if_ndis.c optional ndis
|
||||
|
@ -110,9 +110,11 @@ dev/ed/if_ed_wd80x3.c optional ed isa
|
||||
dev/fb/fb.c optional fb | gdc
|
||||
dev/fe/if_fe_cbus.c optional fe isa
|
||||
dev/hwpmc/hwpmc_amd.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_intel.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_pentium.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_piv.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_ppro.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_tsc.c optional hwpmc
|
||||
dev/hwpmc/hwpmc_x86.c optional hwpmc
|
||||
dev/io/iodev.c optional io
|
||||
dev/kbd/kbd.c optional pckbd | sc | ukbd
|
||||
|
@ -26,7 +26,6 @@
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
@ -61,18 +60,6 @@ struct amd_descr {
|
||||
|
||||
static struct amd_descr amd_pmcdesc[AMD_NPMCS] =
|
||||
{
|
||||
{
|
||||
.pm_descr =
|
||||
{
|
||||
.pd_name = "TSC",
|
||||
.pd_class = PMC_CLASS_TSC,
|
||||
.pd_caps = PMC_CAP_READ,
|
||||
.pd_width = 64
|
||||
},
|
||||
.pm_evsel = MSR_TSC,
|
||||
.pm_perfctr = 0 /* unused */
|
||||
},
|
||||
|
||||
{
|
||||
.pm_descr =
|
||||
{
|
||||
@ -257,6 +244,16 @@ const struct amd_event_code_map amd_event_codes[] = {
|
||||
const int amd_event_codes_size =
|
||||
sizeof(amd_event_codes) / sizeof(amd_event_codes[0]);
|
||||
|
||||
/*
|
||||
* Per-processor information
|
||||
*/
|
||||
|
||||
struct amd_cpu {
|
||||
struct pmc_hw pc_amdpmcs[AMD_NPMCS];
|
||||
};
|
||||
|
||||
static struct amd_cpu **amd_pcpu;
|
||||
|
||||
/*
|
||||
* read a pmc register
|
||||
*/
|
||||
@ -267,17 +264,17 @@ amd_read_pmc(int cpu, int ri, pmc_value_t *v)
|
||||
enum pmc_mode mode;
|
||||
const struct amd_descr *pd;
|
||||
struct pmc *pm;
|
||||
const struct pmc_hw *phw;
|
||||
pmc_value_t tmp;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[amd,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < AMD_NPMCS,
|
||||
("[amd,%d] illegal row-index %d", __LINE__, ri));
|
||||
KASSERT(amd_pcpu[cpu],
|
||||
("[amd,%d] null per-cpu, cpu %d", __LINE__, cpu));
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
pd = &amd_pmcdesc[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pm = amd_pcpu[cpu]->pc_amdpmcs[ri].phw_pmc;
|
||||
pd = &amd_pmcdesc[ri];
|
||||
|
||||
KASSERT(pm != NULL,
|
||||
("[amd,%d] No owner for HWPMC [cpu%d,pmc%d]", __LINE__,
|
||||
@ -287,15 +284,6 @@ amd_read_pmc(int cpu, int ri, pmc_value_t *v)
|
||||
|
||||
PMCDBG(MDP,REA,1,"amd-read id=%d class=%d", ri, pd->pm_descr.pd_class);
|
||||
|
||||
/* Reading the TSC is a special case */
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC) {
|
||||
KASSERT(PMC_IS_COUNTING_MODE(mode),
|
||||
("[amd,%d] TSC counter in non-counting mode", __LINE__));
|
||||
*v = rdtsc();
|
||||
PMCDBG(MDP,REA,2,"amd-read id=%d -> %jd", ri, *v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
KASSERT(pd->pm_descr.pd_class == amd_pmc_class,
|
||||
("[amd,%d] unknown PMC class (%d)", __LINE__,
|
||||
@ -324,18 +312,16 @@ static int
|
||||
amd_write_pmc(int cpu, int ri, pmc_value_t v)
|
||||
{
|
||||
const struct amd_descr *pd;
|
||||
struct pmc *pm;
|
||||
const struct pmc_hw *phw;
|
||||
enum pmc_mode mode;
|
||||
struct pmc *pm;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[amd,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < AMD_NPMCS,
|
||||
("[amd,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
pd = &amd_pmcdesc[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pm = amd_pcpu[cpu]->pc_amdpmcs[ri].phw_pmc;
|
||||
pd = &amd_pmcdesc[ri];
|
||||
|
||||
KASSERT(pm != NULL,
|
||||
("[amd,%d] PMC not owned (cpu%d,pmc%d)", __LINE__,
|
||||
@ -343,9 +329,6 @@ amd_write_pmc(int cpu, int ri, pmc_value_t v)
|
||||
|
||||
mode = PMC_TO_MODE(pm);
|
||||
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
|
||||
return 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
KASSERT(pd->pm_descr.pd_class == amd_pmc_class,
|
||||
("[amd,%d] unknown PMC class (%d)", __LINE__,
|
||||
@ -380,7 +363,7 @@ amd_config_pmc(int cpu, int ri, struct pmc *pm)
|
||||
KASSERT(ri >= 0 && ri < AMD_NPMCS,
|
||||
("[amd,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
phw = &amd_pcpu[cpu]->pc_amdpmcs[ri];
|
||||
|
||||
KASSERT(pm == NULL || phw->phw_pmc == NULL,
|
||||
("[amd,%d] pm=%p phw->pm=%p hwpmc not unconfigured",
|
||||
@ -397,7 +380,7 @@ amd_config_pmc(int cpu, int ri, struct pmc *pm)
|
||||
static int
|
||||
amd_get_config(int cpu, int ri, struct pmc **ppm)
|
||||
{
|
||||
*ppm = pmc_pcpu[cpu]->pc_hwpmcs[ri]->phw_pmc;
|
||||
*ppm = amd_pcpu[cpu]->pc_amdpmcs[ri].phw_pmc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -474,18 +457,6 @@ amd_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
|
||||
if ((pd->pd_caps & caps) != caps)
|
||||
return EPERM;
|
||||
if (pd->pd_class == PMC_CLASS_TSC) {
|
||||
/* TSC's are always allocated in system-wide counting mode */
|
||||
if (a->pm_ev != PMC_EV_TSC_TSC ||
|
||||
a->pm_mode != PMC_MODE_SC)
|
||||
return EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
KASSERT(pd->pd_class == amd_pmc_class,
|
||||
("[amd,%d] Unknown PMC class (%d)", __LINE__, pd->pd_class));
|
||||
#endif
|
||||
|
||||
pe = a->pm_ev;
|
||||
|
||||
@ -556,7 +527,7 @@ amd_release_pmc(int cpu, int ri, struct pmc *pmc)
|
||||
KASSERT(ri >= 0 && ri < AMD_NPMCS,
|
||||
("[amd,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
phw = &amd_pcpu[cpu]->pc_amdpmcs[ri];
|
||||
|
||||
KASSERT(phw->phw_pmc == NULL,
|
||||
("[amd,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
|
||||
@ -588,7 +559,7 @@ amd_start_pmc(int cpu, int ri)
|
||||
KASSERT(ri >= 0 && ri < AMD_NPMCS,
|
||||
("[amd,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
phw = &amd_pcpu[cpu]->pc_amdpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pd = &amd_pmcdesc[ri];
|
||||
|
||||
@ -598,15 +569,6 @@ amd_start_pmc(int cpu, int ri)
|
||||
|
||||
PMCDBG(MDP,STA,1,"amd-start cpu=%d ri=%d", cpu, ri);
|
||||
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
|
||||
return 0; /* TSCs are always running */
|
||||
|
||||
#ifdef DEBUG
|
||||
KASSERT(pd->pm_descr.pd_class == amd_pmc_class,
|
||||
("[amd,%d] unknown PMC class (%d)", __LINE__,
|
||||
pd->pm_descr.pd_class));
|
||||
#endif
|
||||
|
||||
KASSERT(AMD_PMC_IS_STOPPED(pd->pm_evsel),
|
||||
("[amd,%d] pmc%d,cpu%d: Starting active PMC \"%s\"", __LINE__,
|
||||
ri, cpu, pd->pm_descr.pd_name));
|
||||
@ -637,24 +599,13 @@ amd_stop_pmc(int cpu, int ri)
|
||||
KASSERT(ri >= 0 && ri < AMD_NPMCS,
|
||||
("[amd,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
phw = &amd_pcpu[cpu]->pc_amdpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pd = &amd_pmcdesc[ri];
|
||||
|
||||
KASSERT(pm != NULL,
|
||||
("[amd,%d] cpu%d,pmc%d no PMC to stop", __LINE__,
|
||||
cpu, ri));
|
||||
|
||||
/* can't stop a TSC */
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
|
||||
return 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
KASSERT(pd->pm_descr.pd_class == amd_pmc_class,
|
||||
("[amd,%d] unknown PMC class (%d)", __LINE__,
|
||||
pd->pm_descr.pd_class));
|
||||
#endif
|
||||
|
||||
KASSERT(!AMD_PMC_IS_STOPPED(pd->pm_evsel),
|
||||
("[amd,%d] PMC%d, CPU%d \"%s\" already stopped",
|
||||
__LINE__, ri, cpu, pd->pm_descr.pd_name));
|
||||
@ -677,10 +628,10 @@ amd_stop_pmc(int cpu, int ri)
|
||||
static int
|
||||
amd_intr(int cpu, struct trapframe *tf)
|
||||
{
|
||||
int i, error, retval, ri;
|
||||
int i, error, retval;
|
||||
uint32_t config, evsel, perfctr;
|
||||
struct pmc *pm;
|
||||
struct pmc_cpu *pc;
|
||||
struct amd_cpu *pac;
|
||||
struct pmc_hw *phw;
|
||||
pmc_value_t v;
|
||||
|
||||
@ -692,11 +643,10 @@ amd_intr(int cpu, struct trapframe *tf)
|
||||
|
||||
retval = 0;
|
||||
|
||||
pc = pmc_pcpu[cpu];
|
||||
pac = amd_pcpu[cpu];
|
||||
|
||||
/*
|
||||
* look for all PMCs that have interrupted:
|
||||
* - skip over the TSC [PMC#0]
|
||||
* - look for a running, sampling PMC which has overflowed
|
||||
* and which has a valid 'struct pmc' association
|
||||
*
|
||||
@ -708,14 +658,12 @@ amd_intr(int cpu, struct trapframe *tf)
|
||||
* interrupt at a time.
|
||||
*/
|
||||
|
||||
for (i = 0; retval == 0 && i < AMD_NPMCS-1; i++) {
|
||||
|
||||
ri = i + 1; /* row index; TSC is at ri == 0 */
|
||||
for (i = 0; retval == 0 && i < AMD_NPMCS; i++) {
|
||||
|
||||
if (!AMD_PMC_HAS_OVERFLOWED(i))
|
||||
continue;
|
||||
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
phw = &pac->pc_amdpmcs[i];
|
||||
|
||||
KASSERT(phw != NULL, ("[amd,%d] null PHW pointer", __LINE__));
|
||||
|
||||
@ -769,7 +717,7 @@ amd_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
|
||||
KASSERT(ri >= 0 && ri < AMD_NPMCS,
|
||||
("[amd,%d] row-index %d out of range", __LINE__, ri));
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
phw = &amd_pcpu[cpu]->pc_amdpmcs[ri];
|
||||
pd = &amd_pmcdesc[ri];
|
||||
|
||||
if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name,
|
||||
@ -804,33 +752,20 @@ amd_get_msr(int ri, uint32_t *msr)
|
||||
("[amd,%d] ri %d out of range", __LINE__, ri));
|
||||
|
||||
*msr = amd_pmcdesc[ri].pm_perfctr - AMD_PMC_PERFCTR_0;
|
||||
return 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* processor dependent initialization.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Per-processor data structure
|
||||
*
|
||||
* [common stuff]
|
||||
* [5 struct pmc_hw pointers]
|
||||
* [5 struct pmc_hw structures]
|
||||
*/
|
||||
|
||||
struct amd_cpu {
|
||||
struct pmc_cpu pc_common;
|
||||
struct pmc_hw *pc_hwpmcs[AMD_NPMCS];
|
||||
struct pmc_hw pc_amdpmcs[AMD_NPMCS];
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
amd_init(int cpu)
|
||||
amd_pcpu_init(struct pmc_mdep *md, int cpu)
|
||||
{
|
||||
int n;
|
||||
struct amd_cpu *pcs;
|
||||
int classindex, first_ri, n;
|
||||
struct pmc_cpu *pc;
|
||||
struct amd_cpu *pac;
|
||||
struct pmc_hw *phw;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
@ -838,29 +773,32 @@ amd_init(int cpu)
|
||||
|
||||
PMCDBG(MDP,INI,1,"amd-init cpu=%d", cpu);
|
||||
|
||||
pcs = malloc(sizeof(struct amd_cpu), M_PMC,
|
||||
amd_pcpu[cpu] = pac = malloc(sizeof(struct amd_cpu), M_PMC,
|
||||
M_WAITOK|M_ZERO);
|
||||
|
||||
phw = &pcs->pc_amdpmcs[0];
|
||||
|
||||
/*
|
||||
* Initialize the per-cpu mutex and set the content of the
|
||||
* hardware descriptors to a known state.
|
||||
* Set the content of the hardware descriptors to a known
|
||||
* state and initialize pointers in the MI per-cpu descriptor.
|
||||
*/
|
||||
pc = pmc_pcpu[cpu];
|
||||
#if defined(__amd64__)
|
||||
classindex = PMC_MDEP_CLASS_INDEX_K8;
|
||||
#elif defined(__i386__)
|
||||
classindex = md->pmd_cputype == PMC_CPU_AMD_K8 ?
|
||||
PMC_MDEP_CLASS_INDEX_K8 : PMC_MDEP_CLASS_INDEX_K7;
|
||||
#endif
|
||||
first_ri = md->pmd_classdep[classindex].pcd_ri;
|
||||
|
||||
for (n = 0; n < AMD_NPMCS; n++, phw++) {
|
||||
KASSERT(pc != NULL, ("[amd,%d] NULL per-cpu pointer", __LINE__));
|
||||
|
||||
for (n = 0, phw = pac->pc_amdpmcs; n < AMD_NPMCS; n++, phw++) {
|
||||
phw->phw_state = PMC_PHW_FLAG_IS_ENABLED |
|
||||
PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n);
|
||||
phw->phw_pmc = NULL;
|
||||
pcs->pc_hwpmcs[n] = phw;
|
||||
pc->pc_hwpmcs[n + first_ri] = phw;
|
||||
}
|
||||
|
||||
/* Mark the TSC as shareable */
|
||||
pcs->pc_hwpmcs[0]->phw_state |= PMC_PHW_FLAG_IS_SHAREABLE;
|
||||
|
||||
pmc_pcpu[cpu] = (struct pmc_cpu *) pcs;
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
@ -870,11 +808,12 @@ amd_init(int cpu)
|
||||
*/
|
||||
|
||||
static int
|
||||
amd_cleanup(int cpu)
|
||||
amd_pcpu_fini(struct pmc_mdep *md, int cpu)
|
||||
{
|
||||
int i;
|
||||
int classindex, first_ri, i;
|
||||
uint32_t evsel;
|
||||
struct pmc_cpu *pcs;
|
||||
struct pmc_cpu *pc;
|
||||
struct amd_cpu *pac;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[amd,%d] insane cpu number (%d)", __LINE__, cpu));
|
||||
@ -884,7 +823,6 @@ amd_cleanup(int cpu)
|
||||
/*
|
||||
* First, turn off all PMCs on this CPU.
|
||||
*/
|
||||
|
||||
for (i = 0; i < 4; i++) { /* XXX this loop is now not needed */
|
||||
evsel = rdmsr(AMD_PMC_EVSEL_0 + i);
|
||||
evsel &= ~AMD_PMC_ENABLE;
|
||||
@ -894,25 +832,42 @@ amd_cleanup(int cpu)
|
||||
/*
|
||||
* Next, free up allocated space.
|
||||
*/
|
||||
if ((pac = amd_pcpu[cpu]) == NULL)
|
||||
return (0);
|
||||
|
||||
if ((pcs = pmc_pcpu[cpu]) == NULL)
|
||||
return 0;
|
||||
amd_pcpu[cpu] = NULL;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* check the TSC */
|
||||
KASSERT(pcs->pc_hwpmcs[0]->phw_pmc == NULL,
|
||||
("[amd,%d] CPU%d,PMC0 still in use", __LINE__, cpu));
|
||||
for (i = 1; i < AMD_NPMCS; i++) {
|
||||
KASSERT(pcs->pc_hwpmcs[i]->phw_pmc == NULL,
|
||||
for (i = 0; i < AMD_NPMCS; i++) {
|
||||
KASSERT(pac->pc_amdpmcs[i].phw_pmc == NULL,
|
||||
("[amd,%d] CPU%d/PMC%d in use", __LINE__, cpu, i));
|
||||
KASSERT(AMD_PMC_IS_STOPPED(AMD_PMC_EVSEL_0 + (i-1)),
|
||||
("[amd,%d] CPU%d/PMC%d not stopped", __LINE__, cpu, i));
|
||||
}
|
||||
#endif
|
||||
|
||||
pmc_pcpu[cpu] = NULL;
|
||||
free(pcs, M_PMC);
|
||||
return 0;
|
||||
pc = pmc_pcpu[cpu];
|
||||
KASSERT(pc != NULL, ("[amd,%d] NULL per-cpu state", __LINE__));
|
||||
|
||||
#if defined(__amd64__)
|
||||
classindex = PMC_MDEP_CLASS_INDEX_K8;
|
||||
#elif defined(__i386__)
|
||||
classindex = md->pmd_cputype == PMC_CPU_AMD_K8 ? PMC_MDEP_CLASS_INDEX_K8 :
|
||||
PMC_MDEP_CLASS_INDEX_K7;
|
||||
#endif
|
||||
first_ri = md->pmd_classdep[classindex].pcd_ri;
|
||||
|
||||
/*
|
||||
* Reset pointers in the MI 'per-cpu' state.
|
||||
*/
|
||||
for (i = 0; i < AMD_NPMCS; i++) {
|
||||
pc->pc_hwpmcs[i + first_ri] = NULL;
|
||||
}
|
||||
|
||||
|
||||
free(pac, M_PMC);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -922,11 +877,12 @@ amd_cleanup(int cpu)
|
||||
struct pmc_mdep *
|
||||
pmc_amd_initialize(void)
|
||||
{
|
||||
int classindex, error, i, nclasses, ncpus;
|
||||
struct pmc_classdep *pcd;
|
||||
enum pmc_cputype cputype;
|
||||
enum pmc_class class;
|
||||
struct pmc_mdep *pmc_mdep;
|
||||
enum pmc_class class;
|
||||
char *name;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* The presence of hardware performance counters on the AMD
|
||||
@ -939,12 +895,16 @@ pmc_amd_initialize(void)
|
||||
class = cputype = -1;
|
||||
name = NULL;
|
||||
switch (cpu_id & 0xF00) {
|
||||
#if defined(__i386__)
|
||||
case 0x600: /* Athlon(tm) processor */
|
||||
classindex = PMC_MDEP_CLASS_INDEX_K7;
|
||||
cputype = PMC_CPU_AMD_K7;
|
||||
class = PMC_CLASS_K7;
|
||||
name = "K7";
|
||||
break;
|
||||
#endif
|
||||
case 0xF00: /* Athlon64/Opteron processor */
|
||||
classindex = PMC_MDEP_CLASS_INDEX_K8;
|
||||
cputype = PMC_CPU_AMD_K8;
|
||||
class = PMC_CLASS_K8;
|
||||
name = "K8";
|
||||
@ -960,53 +920,121 @@ pmc_amd_initialize(void)
|
||||
amd_pmc_class = class;
|
||||
#endif
|
||||
|
||||
pmc_mdep = malloc(sizeof(struct pmc_mdep),
|
||||
/*
|
||||
* Allocate space for pointers to PMC HW descriptors and for
|
||||
* the MDEP structure used by MI code.
|
||||
*/
|
||||
amd_pcpu = malloc(sizeof(struct amd_cpu *) * pmc_cpu_max(), M_PMC,
|
||||
M_WAITOK|M_ZERO);
|
||||
|
||||
/*
|
||||
* These processors have two classes of PMCs: the TSC and
|
||||
* programmable PMCs.
|
||||
*/
|
||||
nclasses = 2;
|
||||
pmc_mdep = malloc(sizeof(struct pmc_mdep) + nclasses * sizeof (struct pmc_classdep),
|
||||
M_PMC, M_WAITOK|M_ZERO);
|
||||
|
||||
pmc_mdep->pmd_cputype = cputype;
|
||||
pmc_mdep->pmd_npmc = AMD_NPMCS;
|
||||
pmc_mdep->pmd_cputype = cputype;
|
||||
pmc_mdep->pmd_nclass = nclasses;
|
||||
|
||||
/* this processor has two classes of usable PMCs */
|
||||
pmc_mdep->pmd_nclass = 2;
|
||||
ncpus = pmc_cpu_max();
|
||||
|
||||
/* TSC */
|
||||
pmc_mdep->pmd_classes[0].pm_class = PMC_CLASS_TSC;
|
||||
pmc_mdep->pmd_classes[0].pm_caps = PMC_CAP_READ;
|
||||
pmc_mdep->pmd_classes[0].pm_width = 64;
|
||||
/* Initialize the TSC. */
|
||||
error = pmc_tsc_initialize(pmc_mdep, ncpus);
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
/* AMD K7/K8 PMCs */
|
||||
pmc_mdep->pmd_classes[1].pm_class = class;
|
||||
pmc_mdep->pmd_classes[1].pm_caps = AMD_PMC_CAPS;
|
||||
pmc_mdep->pmd_classes[1].pm_width = 48;
|
||||
/* Initialize AMD K7 and K8 PMC handling. */
|
||||
pcd = &pmc_mdep->pmd_classdep[classindex];
|
||||
|
||||
pmc_mdep->pmd_nclasspmcs[0] = 1;
|
||||
pmc_mdep->pmd_nclasspmcs[1] = (AMD_NPMCS-1);
|
||||
pcd->pcd_caps = AMD_PMC_CAPS;
|
||||
pcd->pcd_class = class;
|
||||
pcd->pcd_num = AMD_NPMCS;
|
||||
pcd->pcd_ri = pmc_mdep->pmd_npmc;
|
||||
pcd->pcd_width = 48;
|
||||
|
||||
/* fill in the correct pmc name and class */
|
||||
for (i = 1; i < AMD_NPMCS; i++) {
|
||||
for (i = 0; i < AMD_NPMCS; i++) {
|
||||
(void) snprintf(amd_pmcdesc[i].pm_descr.pd_name,
|
||||
sizeof(amd_pmcdesc[i].pm_descr.pd_name), "%s-%d",
|
||||
name, i-1);
|
||||
amd_pmcdesc[i].pm_descr.pd_class = class;
|
||||
}
|
||||
|
||||
pmc_mdep->pmd_init = amd_init;
|
||||
pmc_mdep->pmd_cleanup = amd_cleanup;
|
||||
pmc_mdep->pmd_switch_in = amd_switch_in;
|
||||
pmc_mdep->pmd_switch_out = amd_switch_out;
|
||||
pmc_mdep->pmd_read_pmc = amd_read_pmc;
|
||||
pmc_mdep->pmd_write_pmc = amd_write_pmc;
|
||||
pmc_mdep->pmd_config_pmc = amd_config_pmc;
|
||||
pmc_mdep->pmd_get_config = amd_get_config;
|
||||
pmc_mdep->pmd_allocate_pmc = amd_allocate_pmc;
|
||||
pmc_mdep->pmd_release_pmc = amd_release_pmc;
|
||||
pmc_mdep->pmd_start_pmc = amd_start_pmc;
|
||||
pmc_mdep->pmd_stop_pmc = amd_stop_pmc;
|
||||
pmc_mdep->pmd_intr = amd_intr;
|
||||
pmc_mdep->pmd_describe = amd_describe;
|
||||
pmc_mdep->pmd_get_msr = amd_get_msr; /* i386 */
|
||||
pcd->pcd_allocate_pmc = amd_allocate_pmc;
|
||||
pcd->pcd_config_pmc = amd_config_pmc;
|
||||
pcd->pcd_describe = amd_describe;
|
||||
pcd->pcd_get_config = amd_get_config;
|
||||
pcd->pcd_get_msr = amd_get_msr;
|
||||
pcd->pcd_pcpu_fini = amd_pcpu_fini;
|
||||
pcd->pcd_pcpu_init = amd_pcpu_init;
|
||||
pcd->pcd_read_pmc = amd_read_pmc;
|
||||
pcd->pcd_release_pmc = amd_release_pmc;
|
||||
pcd->pcd_start_pmc = amd_start_pmc;
|
||||
pcd->pcd_stop_pmc = amd_stop_pmc;
|
||||
pcd->pcd_write_pmc = amd_write_pmc;
|
||||
|
||||
pmc_mdep->pmd_pcpu_init = NULL;
|
||||
pmc_mdep->pmd_pcpu_fini = NULL;
|
||||
pmc_mdep->pmd_intr = amd_intr;
|
||||
pmc_mdep->pmd_switch_in = amd_switch_in;
|
||||
pmc_mdep->pmd_switch_out = amd_switch_out;
|
||||
|
||||
pmc_mdep->pmd_npmc += AMD_NPMCS;
|
||||
|
||||
PMCDBG(MDP,INI,0,"%s","amd-initialize");
|
||||
|
||||
return pmc_mdep;
|
||||
return (pmc_mdep);
|
||||
|
||||
error:
|
||||
if (error) {
|
||||
free(pmc_mdep, M_PMC);
|
||||
pmc_mdep = NULL;
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finalization code for AMD CPUs.
|
||||
*/
|
||||
|
||||
void
|
||||
pmc_amd_finalize(struct pmc_mdep *md)
|
||||
{
|
||||
#if defined(INVARIANTS)
|
||||
int classindex, i, ncpus, pmcclass;
|
||||
#endif
|
||||
|
||||
pmc_tsc_finalize(md);
|
||||
|
||||
KASSERT(amd_pcpu != NULL, ("[amd,%d] NULL per-cpu array pointer",
|
||||
__LINE__));
|
||||
|
||||
#if defined(INVARIANTS)
|
||||
switch (md->pmd_cputype) {
|
||||
#if defined(__i386__)
|
||||
case PMC_CPU_AMD_K7:
|
||||
classindex = PMC_MDEP_CLASS_INDEX_K7;
|
||||
pmcclass = PMC_CLASS_K7;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
classindex = PMC_MDEP_CLASS_INDEX_K8;
|
||||
pmcclass = PMC_CLASS_K8;
|
||||
}
|
||||
|
||||
KASSERT(md->pmd_classdep[classindex].pcd_class == pmcclass,
|
||||
("[amd,%d] pmc class mismatch", __LINE__));
|
||||
|
||||
ncpus = pmc_cpu_max();
|
||||
|
||||
for (i = 0; i < ncpus; i++)
|
||||
KASSERT(amd_pcpu[i] == NULL, ("[amd,%d] non-null pcpu",
|
||||
__LINE__));
|
||||
#endif
|
||||
|
||||
free(amd_pcpu, M_PMC);
|
||||
amd_pcpu = NULL;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@
|
||||
#define AMD_PMC_PERFCTR_3 0xC0010007
|
||||
|
||||
|
||||
#define AMD_NPMCS 5 /* 1 TSC + 4 PMCs */
|
||||
#define AMD_NPMCS 4
|
||||
|
||||
#define AMD_PMC_COUNTERMASK 0xFF000000
|
||||
#define AMD_PMC_TO_COUNTER(x) (((x) << 24) & AMD_PMC_COUNTERMASK)
|
||||
@ -93,11 +93,5 @@ struct pmc_md_amd_pmc {
|
||||
uint32_t pm_amd_evsel;
|
||||
};
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
|
||||
struct pmc_mdep *pmc_amd_initialize(void); /* AMD K7/K8 PMCs */
|
||||
|
||||
#endif /* _KERNEL */
|
||||
#endif /* _DEV_HWPMC_AMD_H_ */
|
||||
|
227
sys/dev/hwpmc/hwpmc_intel.c
Normal file
227
sys/dev/hwpmc/hwpmc_intel.c
Normal file
@ -0,0 +1,227 @@
|
||||
/*-
|
||||
* Copyright (c) 2008 Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Common code for handling Intel CPUs.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/pmc.h>
|
||||
#include <sys/pmckern.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <machine/cpu.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/specialreg.h>
|
||||
|
||||
static int
|
||||
intel_switch_in(struct pmc_cpu *pc, struct pmc_process *pp)
|
||||
{
|
||||
(void) pc;
|
||||
|
||||
PMCDBG(MDP,SWI,1, "pc=%p pp=%p enable-msr=%d", pc, pp,
|
||||
pp->pp_flags & PMC_PP_ENABLE_MSR_ACCESS);
|
||||
|
||||
/* allow the RDPMC instruction if needed */
|
||||
if (pp->pp_flags & PMC_PP_ENABLE_MSR_ACCESS)
|
||||
load_cr4(rcr4() | CR4_PCE);
|
||||
|
||||
PMCDBG(MDP,SWI,1, "cr4=0x%jx", (uintmax_t) rcr4());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_switch_out(struct pmc_cpu *pc, struct pmc_process *pp)
|
||||
{
|
||||
(void) pc;
|
||||
(void) pp; /* can be NULL */
|
||||
|
||||
PMCDBG(MDP,SWO,1, "pc=%p pp=%p cr4=0x%jx", pc, pp,
|
||||
(uintmax_t) rcr4());
|
||||
|
||||
/* always turn off the RDPMC instruction */
|
||||
load_cr4(rcr4() & ~CR4_PCE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pmc_mdep *
|
||||
pmc_intel_initialize(void)
|
||||
{
|
||||
struct pmc_mdep *pmc_mdep;
|
||||
enum pmc_cputype cputype;
|
||||
int error, model, nclasses, ncpus;
|
||||
|
||||
KASSERT(strcmp(cpu_vendor, "GenuineIntel") == 0,
|
||||
("[intel,%d] Initializing non-intel processor", __LINE__));
|
||||
|
||||
PMCDBG(MDP,INI,0, "intel-initialize cpuid=0x%x", cpu_id);
|
||||
|
||||
cputype = -1;
|
||||
nclasses = 2;
|
||||
|
||||
switch (cpu_id & 0xF00) {
|
||||
#if defined(__i386__)
|
||||
case 0x500: /* Pentium family processors */
|
||||
cputype = PMC_CPU_INTEL_P5;
|
||||
break;
|
||||
case 0x600: /* Pentium Pro, Celeron, Pentium II & III */
|
||||
switch ((cpu_id & 0xF0) >> 4) { /* model number field */
|
||||
case 0x1:
|
||||
cputype = PMC_CPU_INTEL_P6;
|
||||
break;
|
||||
case 0x3: case 0x5:
|
||||
cputype = PMC_CPU_INTEL_PII;
|
||||
break;
|
||||
case 0x6:
|
||||
cputype = PMC_CPU_INTEL_CL;
|
||||
break;
|
||||
case 0x7: case 0x8: case 0xA: case 0xB:
|
||||
cputype = PMC_CPU_INTEL_PIII;
|
||||
break;
|
||||
case 0x9: case 0xD:
|
||||
cputype = PMC_CPU_INTEL_PM;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
case 0xF00: /* P4 */
|
||||
model = ((cpu_id & 0xF0000) >> 12) | ((cpu_id & 0xF0) >> 4);
|
||||
if (model >= 0 && model <= 6) /* known models */
|
||||
cputype = PMC_CPU_INTEL_PIV;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((int) cputype == -1) {
|
||||
printf("pmc: Unknown Intel CPU.\n");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
pmc_mdep = malloc(sizeof(struct pmc_mdep) + nclasses *
|
||||
sizeof(struct pmc_classdep), M_PMC, M_WAITOK|M_ZERO);
|
||||
|
||||
pmc_mdep->pmd_cputype = cputype;
|
||||
pmc_mdep->pmd_nclass = nclasses;
|
||||
|
||||
pmc_mdep->pmd_switch_in = intel_switch_in;
|
||||
pmc_mdep->pmd_switch_out = intel_switch_out;
|
||||
|
||||
ncpus = pmc_cpu_max();
|
||||
|
||||
error = pmc_tsc_initialize(pmc_mdep, ncpus);
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
switch (cputype) {
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
|
||||
/*
|
||||
* Intel Pentium 4 Processors, and P4/EMT64 processors.
|
||||
*/
|
||||
|
||||
case PMC_CPU_INTEL_PIV:
|
||||
error = pmc_p4_initialize(pmc_mdep, ncpus);
|
||||
|
||||
KASSERT(md->pmd_npmc == TSC_NPMCS + P4_NPMCS, ("[intel,%d] "
|
||||
"incorrect npmc count %d", __LINE__, md->pmd_npmc));
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(__i386__)
|
||||
/*
|
||||
* P6 Family Processors
|
||||
*/
|
||||
|
||||
case PMC_CPU_INTEL_P6:
|
||||
case PMC_CPU_INTEL_CL:
|
||||
case PMC_CPU_INTEL_PII:
|
||||
case PMC_CPU_INTEL_PIII:
|
||||
case PMC_CPU_INTEL_PM:
|
||||
error = pmc_p6_initialize(pmc_mdep, ncpus);
|
||||
|
||||
KASSERT(md->pmd_npmc == TSC_NPMCS + P6_NPMCS, ("[intel,%d] "
|
||||
"incorrect npmc count %d", __LINE__, md->pmd_npmc));
|
||||
break;
|
||||
|
||||
/*
|
||||
* Intel Pentium PMCs.
|
||||
*/
|
||||
|
||||
case PMC_CPU_INTEL_P5:
|
||||
error = pmc_p5_initialize(pmc_mdep, ncpus);
|
||||
|
||||
KASSERT(md->pmd_npmc == TSC_NPMCS + PENTIUM_NPMCS, ("[intel,%d] "
|
||||
"incorrect npmc count %d", __LINE__, md->pmd_npmc));
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
KASSERT(0, ("[intel,%d] Unknown CPU type", __LINE__));
|
||||
}
|
||||
|
||||
|
||||
error:
|
||||
if (error) {
|
||||
free(pmc_mdep, M_PMC);
|
||||
pmc_mdep = NULL;
|
||||
}
|
||||
|
||||
return (pmc_mdep);
|
||||
}
|
||||
|
||||
void
|
||||
pmc_intel_finalize(struct pmc_mdep *md)
|
||||
{
|
||||
pmc_tsc_finalize(md);
|
||||
|
||||
switch (md->pmd_cputype) {
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
case PMC_CPU_INTEL_PIV:
|
||||
pmc_p4_finalize(md);
|
||||
break;
|
||||
#endif
|
||||
#if defined(__i386__)
|
||||
case PMC_CPU_INTEL_P6:
|
||||
case PMC_CPU_INTEL_CL:
|
||||
case PMC_CPU_INTEL_PII:
|
||||
case PMC_CPU_INTEL_PIII:
|
||||
case PMC_CPU_INTEL_PM:
|
||||
pmc_p6_finalize(md);
|
||||
break;
|
||||
case PMC_CPU_INTEL_P5:
|
||||
pmc_p5_finalize(md);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
KASSERT(0, ("[intel,%d] unknown CPU type", __LINE__));
|
||||
}
|
||||
}
|
@ -153,6 +153,11 @@ static LIST_HEAD(pmc_ownerhash, pmc_owner) *pmc_ownerhash;
|
||||
static LIST_HEAD(, pmc_owner) pmc_ss_owners;
|
||||
|
||||
|
||||
/*
|
||||
* A map of row indices to classdep structures.
|
||||
*/
|
||||
static struct pmc_classdep **pmc_rowindex_to_classdep;
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
@ -463,7 +468,7 @@ pmc_debugflags_sysctl_handler(SYSCTL_HANDLER_ARGS)
|
||||
(void) arg1; (void) arg2; /* unused parameters */
|
||||
|
||||
n = sizeof(pmc_debugstr);
|
||||
newstr = malloc(n, M_PMC, M_ZERO|M_WAITOK);
|
||||
newstr = malloc(n, M_PMC, M_WAITOK|M_ZERO);
|
||||
(void) strlcpy(newstr, pmc_debugstr, n);
|
||||
|
||||
error = sysctl_handle_string(oidp, newstr, n, req);
|
||||
@ -482,6 +487,33 @@ pmc_debugflags_sysctl_handler(SYSCTL_HANDLER_ARGS)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Map a row index to a classdep structure and return the adjusted row
|
||||
* index for the PMC class index.
|
||||
*/
|
||||
static struct pmc_classdep *
|
||||
pmc_ri_to_classdep(struct pmc_mdep *md, int ri, int *adjri)
|
||||
{
|
||||
struct pmc_classdep *pcd;
|
||||
|
||||
(void) md;
|
||||
|
||||
KASSERT(ri >= 0 && ri < md->pmd_npmc,
|
||||
("[pmc,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
pcd = pmc_rowindex_to_classdep[ri];
|
||||
|
||||
KASSERT(pcd != NULL,
|
||||
("[amd,%d] ri %d null pcd", __LINE__, ri));
|
||||
|
||||
*adjri = ri - pcd->pcd_ri;
|
||||
|
||||
KASSERT(*adjri >= 0 && *adjri < pcd->pcd_num,
|
||||
("[pmc,%d] adjusted row-index %d", __LINE__, *adjri));
|
||||
|
||||
return (pcd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Concurrency Control
|
||||
*
|
||||
@ -774,8 +806,7 @@ pmc_link_target_process(struct pmc *pm, struct pmc_process *pp)
|
||||
__LINE__, pp, pm));
|
||||
#endif
|
||||
|
||||
pt = malloc(sizeof(struct pmc_target), M_PMC, M_ZERO|M_WAITOK);
|
||||
|
||||
pt = malloc(sizeof(struct pmc_target), M_PMC, M_WAITOK|M_ZERO);
|
||||
pt->pt_process = pp;
|
||||
|
||||
LIST_INSERT_HEAD(&pm->pm_targets, pt, pt_next);
|
||||
@ -1159,13 +1190,14 @@ static void
|
||||
pmc_process_csw_in(struct thread *td)
|
||||
{
|
||||
int cpu;
|
||||
unsigned int ri;
|
||||
unsigned int adjri, ri;
|
||||
struct pmc *pm;
|
||||
struct proc *p;
|
||||
struct pmc_cpu *pc;
|
||||
struct pmc_hw *phw;
|
||||
struct pmc_process *pp;
|
||||
pmc_value_t newvalue;
|
||||
struct pmc_process *pp;
|
||||
struct pmc_classdep *pcd;
|
||||
|
||||
p = td->td_proc;
|
||||
|
||||
@ -1212,7 +1244,8 @@ pmc_process_csw_in(struct thread *td)
|
||||
atomic_add_rel_32(&pm->pm_runcount, 1);
|
||||
|
||||
/* configure the HWPMC we are going to use. */
|
||||
md->pmd_config_pmc(cpu, ri, pm);
|
||||
pcd = pmc_ri_to_classdep(md, ri, &adjri);
|
||||
pcd->pcd_config_pmc(cpu, adjri, pm);
|
||||
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
|
||||
@ -1247,8 +1280,8 @@ pmc_process_csw_in(struct thread *td)
|
||||
|
||||
PMCDBG(CSW,SWI,1,"cpu=%d ri=%d new=%jd", cpu, ri, newvalue);
|
||||
|
||||
md->pmd_write_pmc(cpu, ri, newvalue);
|
||||
md->pmd_start_pmc(cpu, ri);
|
||||
pcd->pcd_write_pmc(cpu, adjri, newvalue);
|
||||
pcd->pcd_start_pmc(cpu, adjri);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1270,14 +1303,16 @@ static void
|
||||
pmc_process_csw_out(struct thread *td)
|
||||
{
|
||||
int cpu;
|
||||
enum pmc_mode mode;
|
||||
unsigned int ri;
|
||||
int64_t tmp;
|
||||
struct pmc *pm;
|
||||
struct proc *p;
|
||||
enum pmc_mode mode;
|
||||
struct pmc_cpu *pc;
|
||||
struct pmc_process *pp;
|
||||
int64_t tmp;
|
||||
pmc_value_t newvalue;
|
||||
unsigned int adjri, ri;
|
||||
struct pmc_process *pp;
|
||||
struct pmc_classdep *pcd;
|
||||
|
||||
|
||||
/*
|
||||
* Locate our process descriptor; this may be NULL if
|
||||
@ -1325,8 +1360,9 @@ pmc_process_csw_out(struct thread *td)
|
||||
|
||||
for (ri = 0; ri < md->pmd_npmc; ri++) {
|
||||
|
||||
pm = NULL;
|
||||
(void) (*md->pmd_get_config)(cpu, ri, &pm);
|
||||
pcd = pmc_ri_to_classdep(md, ri, &adjri);
|
||||
pm = NULL;
|
||||
(void) (*pcd->pcd_get_config)(cpu, adjri, &pm);
|
||||
|
||||
if (pm == NULL) /* nothing at this row index */
|
||||
continue;
|
||||
@ -1341,7 +1377,7 @@ pmc_process_csw_out(struct thread *td)
|
||||
|
||||
/* Stop hardware if not already stopped */
|
||||
if (pm->pm_stalled == 0)
|
||||
md->pmd_stop_pmc(cpu, ri);
|
||||
pcd->pcd_stop_pmc(cpu, adjri);
|
||||
|
||||
/* reduce this PMC's runcount */
|
||||
atomic_subtract_rel_32(&pm->pm_runcount, 1);
|
||||
@ -1361,7 +1397,7 @@ pmc_process_csw_out(struct thread *td)
|
||||
("[pmc,%d] pp refcnt = %d", __LINE__,
|
||||
pp->pp_refcnt));
|
||||
|
||||
md->pmd_read_pmc(cpu, ri, &newvalue);
|
||||
pcd->pcd_read_pmc(cpu, adjri, &newvalue);
|
||||
|
||||
tmp = newvalue - PMC_PCPU_SAVED(cpu,ri);
|
||||
|
||||
@ -1412,7 +1448,7 @@ pmc_process_csw_out(struct thread *td)
|
||||
}
|
||||
|
||||
/* mark hardware as free */
|
||||
md->pmd_config_pmc(cpu, ri, NULL);
|
||||
pcd->pcd_config_pmc(cpu, adjri, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1857,8 +1893,7 @@ pmc_allocate_owner_descriptor(struct proc *p)
|
||||
poh = &pmc_ownerhash[hindex];
|
||||
|
||||
/* allocate space for N pointers and one descriptor struct */
|
||||
po = malloc(sizeof(struct pmc_owner), M_PMC, M_ZERO|M_WAITOK);
|
||||
|
||||
po = malloc(sizeof(struct pmc_owner), M_PMC, M_WAITOK|M_ZERO);
|
||||
po->po_sscount = po->po_error = po->po_flags = 0;
|
||||
po->po_file = NULL;
|
||||
po->po_owner = p;
|
||||
@ -1907,12 +1942,9 @@ pmc_find_process_descriptor(struct proc *p, uint32_t mode)
|
||||
* Pre-allocate memory in the FIND_ALLOCATE case since we
|
||||
* cannot call malloc(9) once we hold a spin lock.
|
||||
*/
|
||||
|
||||
if (mode & PMC_FLAG_ALLOCATE) {
|
||||
/* allocate additional space for 'n' pmc pointers */
|
||||
if (mode & PMC_FLAG_ALLOCATE)
|
||||
ppnew = malloc(sizeof(struct pmc_process) + md->pmd_npmc *
|
||||
sizeof(struct pmc_targetstate), M_PMC, M_ZERO|M_WAITOK);
|
||||
}
|
||||
sizeof(struct pmc_targetstate), M_PMC, M_WAITOK|M_ZERO);
|
||||
|
||||
mtx_lock_spin(&pmc_processhash_mtx);
|
||||
LIST_FOREACH(pp, pph, pp_next)
|
||||
@ -1991,7 +2023,7 @@ pmc_allocate_pmc_descriptor(void)
|
||||
{
|
||||
struct pmc *pmc;
|
||||
|
||||
pmc = malloc(sizeof(struct pmc), M_PMC, M_ZERO|M_WAITOK);
|
||||
pmc = malloc(sizeof(struct pmc), M_PMC, M_WAITOK|M_ZERO);
|
||||
|
||||
if (pmc != NULL) {
|
||||
pmc->pm_owner = NULL;
|
||||
@ -2066,19 +2098,21 @@ pmc_wait_for_pmc_idle(struct pmc *pm)
|
||||
static void
|
||||
pmc_release_pmc_descriptor(struct pmc *pm)
|
||||
{
|
||||
u_int ri, cpu;
|
||||
enum pmc_mode mode;
|
||||
struct pmc_hw *phw;
|
||||
u_int adjri, ri, cpu;
|
||||
struct pmc_owner *po;
|
||||
struct pmc_process *pp;
|
||||
struct pmc_target *ptgt, *tmp;
|
||||
struct pmc_binding pb;
|
||||
struct pmc_process *pp;
|
||||
struct pmc_classdep *pcd;
|
||||
struct pmc_target *ptgt, *tmp;
|
||||
|
||||
sx_assert(&pmc_sx, SX_XLOCKED);
|
||||
|
||||
KASSERT(pm, ("[pmc,%d] null pmc", __LINE__));
|
||||
|
||||
ri = PMC_TO_ROWINDEX(pm);
|
||||
pcd = pmc_ri_to_classdep(md, ri, &adjri);
|
||||
mode = PMC_TO_MODE(pm);
|
||||
|
||||
PMCDBG(PMC,REL,1, "release-pmc pmc=%p ri=%d mode=%d", pm, ri,
|
||||
@ -2112,14 +2146,14 @@ pmc_release_pmc_descriptor(struct pmc *pm)
|
||||
PMCDBG(PMC,REL,2, "stopping cpu=%d ri=%d", cpu, ri);
|
||||
|
||||
critical_enter();
|
||||
md->pmd_stop_pmc(cpu, ri);
|
||||
pcd->pcd_stop_pmc(cpu, adjri);
|
||||
critical_exit();
|
||||
}
|
||||
|
||||
PMCDBG(PMC,REL,2, "decfg cpu=%d ri=%d", cpu, ri);
|
||||
|
||||
critical_enter();
|
||||
md->pmd_config_pmc(cpu, ri, NULL);
|
||||
pcd->pcd_config_pmc(cpu, adjri, NULL);
|
||||
critical_exit();
|
||||
|
||||
/* adjust the global and process count of SS mode PMCs */
|
||||
@ -2192,8 +2226,7 @@ pmc_release_pmc_descriptor(struct pmc *pm)
|
||||
/*
|
||||
* Release any MD resources
|
||||
*/
|
||||
|
||||
(void) md->pmd_release_pmc(cpu, ri, pm);
|
||||
(void) pcd->pcd_release_pmc(cpu, adjri, pm);
|
||||
|
||||
/*
|
||||
* Update row disposition
|
||||
@ -2408,16 +2441,19 @@ pmc_find_pmc(pmc_id_t pmcid, struct pmc **pmc)
|
||||
static int
|
||||
pmc_start(struct pmc *pm)
|
||||
{
|
||||
int error, cpu, ri;
|
||||
enum pmc_mode mode;
|
||||
struct pmc_owner *po;
|
||||
struct pmc_binding pb;
|
||||
struct pmc_classdep *pcd;
|
||||
int adjri, error, cpu, ri;
|
||||
|
||||
KASSERT(pm != NULL,
|
||||
("[pmc,%d] null pm", __LINE__));
|
||||
|
||||
mode = PMC_TO_MODE(pm);
|
||||
ri = PMC_TO_ROWINDEX(pm);
|
||||
pcd = pmc_ri_to_classdep(md, ri, &adjri);
|
||||
|
||||
error = 0;
|
||||
|
||||
PMCDBG(PMC,OPS,1, "start pmc=%p mode=%d ri=%d", pm, mode, ri);
|
||||
@ -2430,7 +2466,7 @@ pmc_start(struct pmc *pm)
|
||||
*/
|
||||
if ((pm->pm_flags & PMC_F_NEEDS_LOGFILE) &&
|
||||
(po->po_flags & PMC_PO_OWNS_LOGFILE) == 0)
|
||||
return EDOOFUS; /* programming error */
|
||||
return (EDOOFUS); /* programming error */
|
||||
|
||||
/*
|
||||
* If this is a sampling mode PMC, log mapping information for
|
||||
@ -2461,7 +2497,7 @@ pmc_start(struct pmc *pm)
|
||||
pmc_force_context_switch();
|
||||
}
|
||||
|
||||
return error;
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
@ -2494,7 +2530,7 @@ pmc_start(struct pmc *pm)
|
||||
cpu = PMC_TO_CPU(pm);
|
||||
|
||||
if (!pmc_cpu_is_active(cpu))
|
||||
return ENXIO;
|
||||
return (ENXIO);
|
||||
|
||||
pmc_select_cpu(cpu);
|
||||
|
||||
@ -2506,16 +2542,16 @@ pmc_start(struct pmc *pm)
|
||||
pm->pm_state = PMC_STATE_RUNNING;
|
||||
|
||||
critical_enter();
|
||||
if ((error = md->pmd_write_pmc(cpu, ri,
|
||||
if ((error = pcd->pcd_write_pmc(cpu, adjri,
|
||||
PMC_IS_SAMPLING_MODE(mode) ?
|
||||
pm->pm_sc.pm_reloadcount :
|
||||
pm->pm_sc.pm_initial)) == 0)
|
||||
error = md->pmd_start_pmc(cpu, ri);
|
||||
error = pcd->pcd_start_pmc(cpu, adjri);
|
||||
critical_exit();
|
||||
|
||||
pmc_restore_cpu_binding(&pb);
|
||||
|
||||
return error;
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2525,9 +2561,10 @@ pmc_start(struct pmc *pm)
|
||||
static int
|
||||
pmc_stop(struct pmc *pm)
|
||||
{
|
||||
int cpu, error, ri;
|
||||
struct pmc_owner *po;
|
||||
struct pmc_binding pb;
|
||||
struct pmc_classdep *pcd;
|
||||
int adjri, cpu, error, ri;
|
||||
|
||||
KASSERT(pm != NULL, ("[pmc,%d] null pmc", __LINE__));
|
||||
|
||||
@ -2569,10 +2606,11 @@ pmc_stop(struct pmc *pm)
|
||||
pmc_select_cpu(cpu);
|
||||
|
||||
ri = PMC_TO_ROWINDEX(pm);
|
||||
pcd = pmc_ri_to_classdep(md, ri, &adjri);
|
||||
|
||||
critical_enter();
|
||||
if ((error = md->pmd_stop_pmc(cpu, ri)) == 0)
|
||||
error = md->pmd_read_pmc(cpu, ri, &pm->pm_sc.pm_initial);
|
||||
if ((error = pcd->pcd_stop_pmc(cpu, adjri)) == 0)
|
||||
error = pcd->pcd_read_pmc(cpu, adjri, &pm->pm_sc.pm_initial);
|
||||
critical_exit();
|
||||
|
||||
pmc_restore_cpu_binding(&pb);
|
||||
@ -2589,7 +2627,7 @@ pmc_stop(struct pmc *pm)
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
@ -2726,13 +2764,22 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
case PMC_OP_GETCPUINFO: /* CPU information */
|
||||
{
|
||||
struct pmc_op_getcpuinfo gci;
|
||||
struct pmc_classinfo *pci;
|
||||
struct pmc_classdep *pcd;
|
||||
int cl;
|
||||
|
||||
gci.pm_cputype = md->pmd_cputype;
|
||||
gci.pm_ncpu = pmc_cpu_max();
|
||||
gci.pm_npmc = md->pmd_npmc;
|
||||
gci.pm_nclass = md->pmd_nclass;
|
||||
bcopy(md->pmd_classes, &gci.pm_classes,
|
||||
sizeof(gci.pm_classes));
|
||||
pci = gci.pm_classes;
|
||||
pcd = md->pmd_classdep;
|
||||
for (cl = 0; cl < md->pmd_nclass; cl++, pci++, pcd++) {
|
||||
pci->pm_caps = pcd->pcd_caps;
|
||||
pci->pm_class = pcd->pcd_class;
|
||||
pci->pm_width = pcd->pcd_width;
|
||||
pci->pm_num = pcd->pcd_num;
|
||||
}
|
||||
error = copyout(&gci, arg, sizeof(gci));
|
||||
}
|
||||
break;
|
||||
@ -2781,13 +2828,15 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
|
||||
case PMC_OP_GETPMCINFO:
|
||||
{
|
||||
uint32_t cpu, n, npmc;
|
||||
size_t pmcinfo_size;
|
||||
int ari;
|
||||
struct pmc *pm;
|
||||
struct pmc_info *p, *pmcinfo;
|
||||
struct pmc_op_getpmcinfo *gpi;
|
||||
size_t pmcinfo_size;
|
||||
uint32_t cpu, n, npmc;
|
||||
struct pmc_owner *po;
|
||||
struct pmc_binding pb;
|
||||
struct pmc_classdep *pcd;
|
||||
struct pmc_info *p, *pmcinfo;
|
||||
struct pmc_op_getpmcinfo *gpi;
|
||||
|
||||
PMC_DOWNGRADE_SX();
|
||||
|
||||
@ -2819,7 +2868,12 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
|
||||
for (n = 0; n < md->pmd_npmc; n++, p++) {
|
||||
|
||||
if ((error = md->pmd_describe(cpu, n, p, &pm)) != 0)
|
||||
pcd = pmc_ri_to_classdep(md, n, &ari);
|
||||
|
||||
KASSERT(pcd != NULL,
|
||||
("[pmc,%d] null pcd ri=%d", __LINE__, n));
|
||||
|
||||
if ((error = pcd->pcd_describe(cpu, ari, p, &pm)) != 0)
|
||||
break;
|
||||
|
||||
if (PMC_ROW_DISP_IS_STANDALONE(n))
|
||||
@ -2964,14 +3018,15 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
|
||||
case PMC_OP_PMCALLOCATE:
|
||||
{
|
||||
uint32_t caps;
|
||||
int adjri, n;
|
||||
u_int cpu;
|
||||
int n;
|
||||
enum pmc_mode mode;
|
||||
uint32_t caps;
|
||||
struct pmc *pmc;
|
||||
enum pmc_mode mode;
|
||||
struct pmc_hw *phw;
|
||||
struct pmc_op_pmcallocate pa;
|
||||
struct pmc_binding pb;
|
||||
struct pmc_classdep *pcd;
|
||||
struct pmc_op_pmcallocate pa;
|
||||
|
||||
if ((error = copyin(arg, &pa, sizeof(pa))) != 0)
|
||||
break;
|
||||
@ -3056,7 +3111,7 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
|
||||
/* A valid class specifier should have been passed in. */
|
||||
for (n = 0; n < md->pmd_nclass; n++)
|
||||
if (md->pmd_classes[n].pm_class == pa.pm_class)
|
||||
if (md->pmd_classdep[n].pcd_class == pa.pm_class)
|
||||
break;
|
||||
if (n == md->pmd_nclass) {
|
||||
error = EINVAL;
|
||||
@ -3064,7 +3119,7 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
}
|
||||
|
||||
/* The requested PMC capabilities should be feasible. */
|
||||
if ((md->pmd_classes[n].pm_caps & caps) != caps) {
|
||||
if ((md->pmd_classdep[n].pcd_caps & caps) != caps) {
|
||||
error = EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
@ -3091,24 +3146,27 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
|
||||
if (PMC_IS_SYSTEM_MODE(mode)) {
|
||||
pmc_select_cpu(cpu);
|
||||
for (n = 0; n < (int) md->pmd_npmc; n++)
|
||||
for (n = 0; n < (int) md->pmd_npmc; n++) {
|
||||
pcd = pmc_ri_to_classdep(md, n, &adjri);
|
||||
if (pmc_can_allocate_row(n, mode) == 0 &&
|
||||
pmc_can_allocate_rowindex(
|
||||
curthread->td_proc, n, cpu) == 0 &&
|
||||
(PMC_IS_UNALLOCATED(cpu, n) ||
|
||||
PMC_IS_SHAREABLE_PMC(cpu, n)) &&
|
||||
md->pmd_allocate_pmc(cpu, n, pmc,
|
||||
pcd->pcd_allocate_pmc(cpu, adjri, pmc,
|
||||
&pa) == 0)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Process virtual mode */
|
||||
for (n = 0; n < (int) md->pmd_npmc; n++) {
|
||||
pcd = pmc_ri_to_classdep(md, n, &adjri);
|
||||
if (pmc_can_allocate_row(n, mode) == 0 &&
|
||||
pmc_can_allocate_rowindex(
|
||||
curthread->td_proc, n,
|
||||
PMC_CPU_ANY) == 0 &&
|
||||
md->pmd_allocate_pmc(curthread->td_oncpu,
|
||||
n, pmc, &pa) == 0)
|
||||
pcd->pcd_allocate_pmc(curthread->td_oncpu,
|
||||
adjri, pmc, &pa) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -3150,10 +3208,11 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
pmc_select_cpu(cpu);
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[n];
|
||||
pcd = pmc_ri_to_classdep(md, n, &adjri);
|
||||
|
||||
if ((phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) == 0 ||
|
||||
(error = md->pmd_config_pmc(cpu, n, pmc)) != 0) {
|
||||
(void) md->pmd_release_pmc(cpu, n, pmc);
|
||||
(error = pcd->pcd_config_pmc(cpu, adjri, pmc)) != 0) {
|
||||
(void) pcd->pcd_release_pmc(cpu, adjri, pmc);
|
||||
pmc_destroy_pmc_descriptor(pmc);
|
||||
free(pmc, M_PMC);
|
||||
pmc = NULL;
|
||||
@ -3319,19 +3378,14 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
|
||||
case PMC_OP_PMCGETMSR:
|
||||
{
|
||||
int ri;
|
||||
struct pmc *pm;
|
||||
int adjri, ri;
|
||||
struct pmc *pm;
|
||||
struct pmc_target *pt;
|
||||
struct pmc_op_getmsr gm;
|
||||
struct pmc_classdep *pcd;
|
||||
|
||||
PMC_DOWNGRADE_SX();
|
||||
|
||||
/* CPU has no 'GETMSR' support */
|
||||
if (md->pmd_get_msr == NULL) {
|
||||
error = ENOSYS;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((error = copyin(arg, &gm, sizeof(gm))) != 0)
|
||||
break;
|
||||
|
||||
@ -3371,8 +3425,15 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
}
|
||||
|
||||
ri = PMC_TO_ROWINDEX(pm);
|
||||
pcd = pmc_ri_to_classdep(md, ri, &adjri);
|
||||
|
||||
if ((error = (*md->pmd_get_msr)(ri, &gm.pm_msr)) < 0)
|
||||
/* PMC class has no 'GETMSR' support */
|
||||
if (pcd->pcd_get_msr == NULL) {
|
||||
error = ENOSYS;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((error = (*pcd->pcd_get_msr)(adjri, &gm.pm_msr)) < 0)
|
||||
break;
|
||||
|
||||
if ((error = copyout(&gm, arg, sizeof(gm))) < 0)
|
||||
@ -3436,12 +3497,14 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
|
||||
case PMC_OP_PMCRW:
|
||||
{
|
||||
uint32_t cpu, ri;
|
||||
int adjri;
|
||||
struct pmc *pm;
|
||||
struct pmc_op_pmcrw *pprw;
|
||||
struct pmc_op_pmcrw prw;
|
||||
struct pmc_binding pb;
|
||||
uint32_t cpu, ri;
|
||||
pmc_value_t oldvalue;
|
||||
struct pmc_binding pb;
|
||||
struct pmc_op_pmcrw prw;
|
||||
struct pmc_classdep *pcd;
|
||||
struct pmc_op_pmcrw *pprw;
|
||||
|
||||
PMC_DOWNGRADE_SX();
|
||||
|
||||
@ -3494,6 +3557,7 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
*/
|
||||
|
||||
ri = PMC_TO_ROWINDEX(pm);
|
||||
pcd = pmc_ri_to_classdep(md, ri, &adjri);
|
||||
|
||||
mtx_pool_lock_spin(pmc_mtxpool, pm);
|
||||
cpu = curthread->td_oncpu;
|
||||
@ -3501,7 +3565,7 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
if (prw.pm_flags & PMC_F_OLDVALUE) {
|
||||
if ((pm->pm_flags & PMC_F_ATTACHED_TO_OWNER) &&
|
||||
(pm->pm_state == PMC_STATE_RUNNING))
|
||||
error = (*md->pmd_read_pmc)(cpu, ri,
|
||||
error = (*pcd->pcd_read_pmc)(cpu, adjri,
|
||||
&oldvalue);
|
||||
else
|
||||
oldvalue = pm->pm_gv.pm_savedvalue;
|
||||
@ -3514,6 +3578,7 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
} else { /* System mode PMCs */
|
||||
cpu = PMC_TO_CPU(pm);
|
||||
ri = PMC_TO_ROWINDEX(pm);
|
||||
pcd = pmc_ri_to_classdep(md, ri, &adjri);
|
||||
|
||||
if (!pmc_cpu_is_active(cpu)) {
|
||||
error = ENXIO;
|
||||
@ -3527,12 +3592,12 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
critical_enter();
|
||||
/* save old value */
|
||||
if (prw.pm_flags & PMC_F_OLDVALUE)
|
||||
if ((error = (*md->pmd_read_pmc)(cpu, ri,
|
||||
if ((error = (*pcd->pcd_read_pmc)(cpu, adjri,
|
||||
&oldvalue)))
|
||||
goto error;
|
||||
/* write out new value */
|
||||
if (prw.pm_flags & PMC_F_NEWVALUE)
|
||||
error = (*md->pmd_write_pmc)(cpu, ri,
|
||||
error = (*pcd->pcd_write_pmc)(cpu, adjri,
|
||||
prw.pm_value);
|
||||
error:
|
||||
critical_exit();
|
||||
@ -3900,11 +3965,12 @@ pmc_capture_user_callchain(int cpu, struct trapframe *tf)
|
||||
static void
|
||||
pmc_process_samples(int cpu)
|
||||
{
|
||||
int n, ri;
|
||||
struct pmc *pm;
|
||||
int adjri, n, ri;
|
||||
struct thread *td;
|
||||
struct pmc_owner *po;
|
||||
struct pmc_sample *ps;
|
||||
struct pmc_classdep *pcd;
|
||||
struct pmc_samplebuffer *psb;
|
||||
|
||||
KASSERT(PCPU_GET(cpuid) == cpu,
|
||||
@ -3988,7 +4054,11 @@ pmc_process_samples(int cpu)
|
||||
* the next hardclock tick.
|
||||
*/
|
||||
for (n = 0; n < md->pmd_npmc; n++) {
|
||||
(void) (*md->pmd_get_config)(cpu,n,&pm);
|
||||
pcd = pmc_ri_to_classdep(md, n, &adjri);
|
||||
KASSERT(pcd != NULL,
|
||||
("[pmc,%d] null pcd ri=%d", __LINE__, n));
|
||||
(void) (*pcd->pcd_get_config)(cpu,adjri,&pm);
|
||||
|
||||
if (pm == NULL || /* !cfg'ed */
|
||||
pm->pm_state != PMC_STATE_RUNNING || /* !active */
|
||||
!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)) || /* !sampling */
|
||||
@ -3997,7 +4067,7 @@ pmc_process_samples(int cpu)
|
||||
|
||||
pm->pm_stalled = 0;
|
||||
ri = PMC_TO_ROWINDEX(pm);
|
||||
(*md->pmd_start_pmc)(cpu, ri);
|
||||
(*pcd->pcd_start_pmc)(cpu, adjri);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4025,12 +4095,13 @@ pmc_process_samples(int cpu)
|
||||
static void
|
||||
pmc_process_exit(void *arg __unused, struct proc *p)
|
||||
{
|
||||
int is_using_hwpmcs;
|
||||
int cpu;
|
||||
unsigned int ri;
|
||||
struct pmc *pm;
|
||||
struct pmc_process *pp;
|
||||
int adjri, cpu;
|
||||
unsigned int ri;
|
||||
int is_using_hwpmcs;
|
||||
struct pmc_owner *po;
|
||||
struct pmc_process *pp;
|
||||
struct pmc_classdep *pcd;
|
||||
pmc_value_t newvalue, tmp;
|
||||
|
||||
PROC_LOCK(p);
|
||||
@ -4091,7 +4162,10 @@ pmc_process_exit(void *arg __unused, struct proc *p)
|
||||
* state similar to the CSW_OUT code.
|
||||
*/
|
||||
pm = NULL;
|
||||
(void) (*md->pmd_get_config)(cpu, ri, &pm);
|
||||
|
||||
pcd = pmc_ri_to_classdep(md, ri, &adjri);
|
||||
|
||||
(void) (*pcd->pcd_get_config)(cpu, adjri, &pm);
|
||||
|
||||
PMCDBG(PRC,EXT,2, "ri=%d pm=%p", ri, pm);
|
||||
|
||||
@ -4111,7 +4185,7 @@ pmc_process_exit(void *arg __unused, struct proc *p)
|
||||
("[pmc,%d] pm %p != pp_pmcs[%d] %p",
|
||||
__LINE__, pm, ri, pp->pp_pmcs[ri].pp_pmc));
|
||||
|
||||
(void) md->pmd_stop_pmc(cpu, ri);
|
||||
(void) pcd->pcd_stop_pmc(cpu, adjri);
|
||||
|
||||
KASSERT(pm->pm_runcount > 0,
|
||||
("[pmc,%d] bad runcount ri %d rc %d",
|
||||
@ -4120,7 +4194,7 @@ pmc_process_exit(void *arg __unused, struct proc *p)
|
||||
/* Stop hardware only if it is actually running */
|
||||
if (pm->pm_state == PMC_STATE_RUNNING &&
|
||||
pm->pm_stalled == 0) {
|
||||
md->pmd_read_pmc(cpu, ri, &newvalue);
|
||||
pcd->pcd_read_pmc(cpu, adjri, &newvalue);
|
||||
tmp = newvalue -
|
||||
PMC_PCPU_SAVED(cpu,ri);
|
||||
|
||||
@ -4135,7 +4209,7 @@ pmc_process_exit(void *arg __unused, struct proc *p)
|
||||
KASSERT((int) pm->pm_runcount >= 0,
|
||||
("[pmc,%d] runcount is %d", __LINE__, ri));
|
||||
|
||||
(void) md->pmd_config_pmc(cpu, ri, NULL);
|
||||
(void) pcd->pcd_config_pmc(cpu, adjri, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4284,10 +4358,11 @@ static const char *pmc_name_of_pmcclass[] = {
|
||||
static int
|
||||
pmc_initialize(void)
|
||||
{
|
||||
int cpu, error, n;
|
||||
int c, cpu, error, n, ri;
|
||||
unsigned int maxcpu;
|
||||
struct pmc_binding pb;
|
||||
struct pmc_sample *ps;
|
||||
struct pmc_classdep *pcd;
|
||||
struct pmc_samplebuffer *sb;
|
||||
|
||||
md = NULL;
|
||||
@ -4340,14 +4415,33 @@ pmc_initialize(void)
|
||||
|
||||
md = pmc_md_initialize();
|
||||
|
||||
if (md == NULL || md->pmd_init == NULL)
|
||||
return ENOSYS;
|
||||
if (md == NULL)
|
||||
return (ENOSYS);
|
||||
|
||||
KASSERT(md->pmd_nclass >= 1 && md->pmd_npmc >= 1,
|
||||
("[pmc,%d] no classes or pmcs", __LINE__));
|
||||
|
||||
/* Compute the map from row-indices to classdep pointers. */
|
||||
pmc_rowindex_to_classdep = malloc(sizeof(struct pmc_classdep *) *
|
||||
md->pmd_npmc, M_PMC, M_WAITOK|M_ZERO);
|
||||
|
||||
for (n = 0; n < md->pmd_npmc; n++)
|
||||
pmc_rowindex_to_classdep[n] = NULL;
|
||||
for (ri = c = 0; c < md->pmd_nclass; c++) {
|
||||
pcd = &md->pmd_classdep[c];
|
||||
for (n = 0; n < pcd->pcd_num; n++, ri++)
|
||||
pmc_rowindex_to_classdep[ri] = pcd;
|
||||
}
|
||||
|
||||
KASSERT(ri == md->pmd_npmc,
|
||||
("[pmc,%d] npmc miscomputed: ri=%d, md->npmc=%d", __LINE__,
|
||||
ri, md->pmd_npmc));
|
||||
|
||||
maxcpu = pmc_cpu_max();
|
||||
|
||||
/* allocate space for the per-cpu array */
|
||||
pmc_pcpu = malloc(maxcpu * sizeof(struct pmc_cpu *),
|
||||
M_PMC, M_WAITOK|M_ZERO);
|
||||
pmc_pcpu = malloc(maxcpu * sizeof(struct pmc_cpu *), M_PMC,
|
||||
M_WAITOK|M_ZERO);
|
||||
|
||||
/* per-cpu 'saved values' for managing process-mode PMCs */
|
||||
pmc_pcpu_saved = malloc(sizeof(pmc_value_t) * maxcpu * md->pmd_npmc,
|
||||
@ -4355,34 +4449,40 @@ pmc_initialize(void)
|
||||
|
||||
/* Perform CPU-dependent initialization. */
|
||||
pmc_save_cpu_binding(&pb);
|
||||
for (cpu = 0; cpu < maxcpu; cpu++) {
|
||||
error = 0;
|
||||
for (cpu = 0; error == 0 && cpu < maxcpu; cpu++) {
|
||||
if (!pmc_cpu_is_active(cpu))
|
||||
continue;
|
||||
pmc_select_cpu(cpu);
|
||||
if ((error = md->pmd_init(cpu)) != 0)
|
||||
break;
|
||||
pmc_pcpu[cpu] = malloc(sizeof(struct pmc_cpu) +
|
||||
md->pmd_npmc * sizeof(struct pmc_hw *), M_PMC,
|
||||
M_WAITOK|M_ZERO);
|
||||
if (md->pmd_pcpu_init)
|
||||
error = md->pmd_pcpu_init(cpu);
|
||||
for (n = 0; error == 0 && n < md->pmd_nclass; n++)
|
||||
error = md->pmd_classdep[n].pcd_pcpu_init(md, cpu);
|
||||
}
|
||||
pmc_restore_cpu_binding(&pb);
|
||||
|
||||
if (error != 0)
|
||||
return error;
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* allocate space for the sample array */
|
||||
for (cpu = 0; cpu < maxcpu; cpu++) {
|
||||
if (!pmc_cpu_is_active(cpu))
|
||||
continue;
|
||||
|
||||
sb = malloc(sizeof(struct pmc_samplebuffer) +
|
||||
pmc_nsamples * sizeof(struct pmc_sample), M_PMC,
|
||||
M_WAITOK|M_ZERO);
|
||||
|
||||
sb->ps_read = sb->ps_write = sb->ps_samples;
|
||||
sb->ps_fence = sb->ps_samples + pmc_nsamples;
|
||||
|
||||
KASSERT(pmc_pcpu[cpu] != NULL,
|
||||
("[pmc,%d] cpu=%d Null per-cpu data", __LINE__, cpu));
|
||||
|
||||
sb->ps_callchains = malloc(pmc_callchaindepth *
|
||||
pmc_nsamples * sizeof(uintptr_t),
|
||||
M_PMC, M_WAITOK|M_ZERO);
|
||||
sb->ps_callchains = malloc(pmc_callchaindepth * pmc_nsamples *
|
||||
sizeof(uintptr_t), M_PMC, M_WAITOK|M_ZERO);
|
||||
|
||||
for (n = 0, ps = sb->ps_samples; n < pmc_nsamples; n++, ps++)
|
||||
ps->ps_pc = sb->ps_callchains +
|
||||
@ -4438,10 +4538,11 @@ pmc_initialize(void)
|
||||
if (error == 0) {
|
||||
printf(PMC_MODULE_NAME ":");
|
||||
for (n = 0; n < (int) md->pmd_nclass; n++) {
|
||||
pcd = &md->pmd_classdep[n];
|
||||
printf(" %s/%d/0x%b",
|
||||
pmc_name_of_pmcclass[md->pmd_classes[n].pm_class],
|
||||
md->pmd_nclasspmcs[n],
|
||||
md->pmd_classes[n].pm_caps,
|
||||
pmc_name_of_pmcclass[pcd->pcd_class],
|
||||
pcd->pcd_num,
|
||||
pcd->pcd_caps,
|
||||
"\20"
|
||||
"\1INT\2USR\3SYS\4EDG\5THR"
|
||||
"\6REA\7WRI\10INV\11QUA\12PRC"
|
||||
@ -4450,14 +4551,14 @@ pmc_initialize(void)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return error;
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* prepare to be unloaded */
|
||||
static void
|
||||
pmc_cleanup(void)
|
||||
{
|
||||
int cpu;
|
||||
int c, cpu;
|
||||
unsigned int maxcpu;
|
||||
struct pmc_ownerhash *ph;
|
||||
struct pmc_owner *po, *tmp;
|
||||
@ -4538,20 +4639,9 @@ pmc_cleanup(void)
|
||||
KASSERT(pmc_ss_count == 0,
|
||||
("[pmc,%d] Global SS count not empty", __LINE__));
|
||||
|
||||
/* Free the per-cpu sample buffers. */
|
||||
/* do processor and pmc-class dependent cleanup */
|
||||
maxcpu = pmc_cpu_max();
|
||||
for (cpu = 0; cpu < maxcpu; cpu++) {
|
||||
if (!pmc_cpu_is_active(cpu))
|
||||
continue;
|
||||
KASSERT(pmc_pcpu[cpu]->pc_sb != NULL,
|
||||
("[pmc,%d] Null cpu sample buffer cpu=%d", __LINE__,
|
||||
cpu));
|
||||
free(pmc_pcpu[cpu]->pc_sb->ps_callchains, M_PMC);
|
||||
free(pmc_pcpu[cpu]->pc_sb, M_PMC);
|
||||
pmc_pcpu[cpu]->pc_sb = NULL;
|
||||
}
|
||||
|
||||
/* do processor dependent cleanup */
|
||||
PMCDBG(MOD,INI,3, "%s", "md cleanup");
|
||||
if (md) {
|
||||
pmc_save_cpu_binding(&pb);
|
||||
@ -4561,15 +4651,28 @@ pmc_cleanup(void)
|
||||
if (!pmc_cpu_is_active(cpu) || pmc_pcpu[cpu] == NULL)
|
||||
continue;
|
||||
pmc_select_cpu(cpu);
|
||||
if (md->pmd_cleanup)
|
||||
md->pmd_cleanup(cpu);
|
||||
for (c = 0; c < md->pmd_nclass; c++)
|
||||
md->pmd_classdep[c].pcd_pcpu_fini(md, cpu);
|
||||
if (md->pmd_pcpu_fini)
|
||||
md->pmd_pcpu_fini(cpu);
|
||||
}
|
||||
free(md, M_PMC);
|
||||
md = NULL;
|
||||
pmc_restore_cpu_binding(&pb);
|
||||
}
|
||||
|
||||
/* deallocate per-cpu structures */
|
||||
/* Free per-cpu descriptors. */
|
||||
for (cpu = 0; cpu < maxcpu; cpu++) {
|
||||
if (!pmc_cpu_is_active(cpu))
|
||||
continue;
|
||||
KASSERT(pmc_pcpu[cpu]->pc_sb != NULL,
|
||||
("[pmc,%d] Null cpu sample buffer cpu=%d", __LINE__,
|
||||
cpu));
|
||||
free(pmc_pcpu[cpu]->pc_sb->ps_callchains, M_PMC);
|
||||
free(pmc_pcpu[cpu]->pc_sb, M_PMC);
|
||||
free(pmc_pcpu[cpu], M_PMC);
|
||||
}
|
||||
|
||||
free(pmc_pcpu, M_PMC);
|
||||
pmc_pcpu = NULL;
|
||||
|
||||
@ -4581,6 +4684,11 @@ pmc_cleanup(void)
|
||||
pmc_pmcdisp = NULL;
|
||||
}
|
||||
|
||||
if (pmc_rowindex_to_classdep) {
|
||||
free(pmc_rowindex_to_classdep, M_PMC);
|
||||
pmc_rowindex_to_classdep = NULL;
|
||||
}
|
||||
|
||||
pmclog_shutdown();
|
||||
|
||||
sx_xunlock(&pmc_sx); /* we are done */
|
||||
|
@ -44,8 +44,14 @@ __FBSDID("$FreeBSD$");
|
||||
*/
|
||||
|
||||
int
|
||||
pmc_initialize_p5(struct pmc_mdep *pmc_mdep)
|
||||
pmc_p5_initialize(struct pmc_mdep *pmc_mdep, int ncpus)
|
||||
{
|
||||
(void) pmc_mdep; (void) ncpus;
|
||||
return (ENOSYS); /* nothing here yet */
|
||||
}
|
||||
|
||||
void
|
||||
pmc_p5_finalize(struct pmc_mdep *pmc_mdep)
|
||||
{
|
||||
(void) pmc_mdep;
|
||||
return ENOSYS; /* nothing here yet */
|
||||
}
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
/* Intel Pentium PMCs */
|
||||
|
||||
#define PENTIUM_NPMCS 3 /* 1 TSC + 2 PMCs */
|
||||
#define PENTIUM_NPMCS 2
|
||||
#define PENTIUM_CESR_PC1 (1 << 25)
|
||||
#define PENTIUM_CESR_CC1_MASK 0x01C00000
|
||||
#define PENTIUM_CESR_TO_CC1(C) (((C) & 0x07) << 22)
|
||||
@ -66,7 +66,8 @@ struct pmc_md_pentium_pmc {
|
||||
* Prototypes
|
||||
*/
|
||||
|
||||
int pmc_initialize_p5(struct pmc_mdep *); /* Pentium PMCs */
|
||||
int pmc_p5_initialize(struct pmc_mdep *_md, int _ncpus);
|
||||
void pmc_p5_finalize(struct pmc_mdep *_md);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
#endif /* _DEV_HWPMC_PENTIUM_H_ */
|
||||
|
@ -153,11 +153,6 @@ __FBSDID("$FreeBSD$");
|
||||
* the two logical processors in the package. We keep track of config
|
||||
* and de-config operations using the CFGFLAGS fields of the per-physical
|
||||
* cpu state.
|
||||
*
|
||||
* Handling TSCs
|
||||
*
|
||||
* TSCs are architectural state and each CPU in a HTT pair has its own
|
||||
* TSC register.
|
||||
*/
|
||||
|
||||
#define P4_PMCS() \
|
||||
@ -364,28 +359,6 @@ struct p4pmc_descr {
|
||||
};
|
||||
|
||||
static struct p4pmc_descr p4_pmcdesc[P4_NPMCS] = {
|
||||
|
||||
/*
|
||||
* TSC descriptor
|
||||
*/
|
||||
|
||||
{
|
||||
.pm_descr =
|
||||
{
|
||||
.pd_name = "TSC",
|
||||
.pd_class = PMC_CLASS_TSC,
|
||||
.pd_caps = PMC_CAP_READ | PMC_CAP_WRITE,
|
||||
.pd_width = 64
|
||||
},
|
||||
.pm_pmcnum = ~0,
|
||||
.pm_cccr_msr = ~0,
|
||||
.pm_pmc_msr = 0x10,
|
||||
},
|
||||
|
||||
/*
|
||||
* P4 PMCS
|
||||
*/
|
||||
|
||||
#define P4_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | PMC_CAP_SYSTEM | \
|
||||
PMC_CAP_EDGE | PMC_CAP_THRESHOLD | PMC_CAP_READ | PMC_CAP_WRITE | \
|
||||
PMC_CAP_INVERT | PMC_CAP_QUALIFIER | PMC_CAP_PRECISE | \
|
||||
@ -435,8 +408,6 @@ static int p4_system_has_htt;
|
||||
/*
|
||||
* Per-CPU data structure for P4 class CPUs
|
||||
*
|
||||
* [common stuff]
|
||||
* [19 struct pmc_hw pointers]
|
||||
* [19 struct pmc_hw structures]
|
||||
* [45 ESCRs status bytes]
|
||||
* [per-cpu spin mutex]
|
||||
@ -448,8 +419,6 @@ static int p4_system_has_htt;
|
||||
*/
|
||||
|
||||
struct p4_cpu {
|
||||
struct pmc_cpu pc_common;
|
||||
struct pmc_hw *pc_hwpmcs[P4_NPMCS];
|
||||
struct pmc_hw pc_p4pmcs[P4_NPMCS];
|
||||
char pc_escrs[P4_NESCR];
|
||||
struct mtx pc_mtx; /* spin lock */
|
||||
@ -463,19 +432,7 @@ struct p4_cpu {
|
||||
pmc_value_t pc_pmc_values[P4_NPMCS * P4_NHTT];
|
||||
};
|
||||
|
||||
/*
|
||||
* A 'logical' CPU shares PMC resources with partner 'physical' CPU,
|
||||
* except the TSC, which is architectural and hence seperate. The
|
||||
* 'logical' CPU descriptor thus has pointers to the physical CPUs
|
||||
* descriptor state except for the TSC (rowindex 0) which is not
|
||||
* shared.
|
||||
*/
|
||||
|
||||
struct p4_logicalcpu {
|
||||
struct pmc_cpu pc_common;
|
||||
struct pmc_hw *pc_hwpmcs[P4_NPMCS];
|
||||
struct pmc_hw pc_tsc;
|
||||
};
|
||||
static struct p4_cpu **p4_pcpu;
|
||||
|
||||
#define P4_PCPU_PMC_VALUE(PC,RI,CPU) (PC)->pc_pmc_values[(RI)*((CPU) & 1)]
|
||||
#define P4_PCPU_HW_VALUE(PC,RI,CPU) (PC)->pc_si.pc_hw[(RI)*((CPU) & 1)]
|
||||
@ -579,8 +536,8 @@ p4_find_event(enum pmc_event ev)
|
||||
if (p4_events[n].pm_event == ev)
|
||||
break;
|
||||
if (n == P4_NEVENTS)
|
||||
return NULL;
|
||||
return &p4_events[n];
|
||||
return (NULL);
|
||||
return (&p4_events[n]);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -588,13 +545,13 @@ p4_find_event(enum pmc_event ev)
|
||||
*/
|
||||
|
||||
static int
|
||||
p4_init(int cpu)
|
||||
p4_pcpu_init(struct pmc_mdep *md, int cpu)
|
||||
{
|
||||
int n, phycpu;
|
||||
char *pescr;
|
||||
struct p4_cpu *pcs;
|
||||
struct p4_logicalcpu *plcs;
|
||||
int n, first_ri, phycpu;
|
||||
struct pmc_hw *phw;
|
||||
struct p4_cpu *p4c;
|
||||
struct pmc_cpu *pc, *plc;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p4,%d] insane cpu number %d", __LINE__, cpu));
|
||||
@ -602,6 +559,8 @@ p4_init(int cpu)
|
||||
PMCDBG(MDP,INI,0, "p4-init cpu=%d is-primary=%d", cpu,
|
||||
pmc_cpu_is_primary(cpu) != 0);
|
||||
|
||||
first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P4].pcd_ri;
|
||||
|
||||
/*
|
||||
* The two CPUs in an HT pair share their per-cpu state.
|
||||
*
|
||||
@ -619,56 +578,50 @@ p4_init(int cpu)
|
||||
p4_system_has_htt = 1;
|
||||
|
||||
phycpu = P4_TO_HTT_PRIMARY(cpu);
|
||||
pcs = (struct p4_cpu *) pmc_pcpu[phycpu];
|
||||
PMCDBG(MDP,INI,1, "p4-init cpu=%d phycpu=%d pcs=%p",
|
||||
cpu, phycpu, pcs);
|
||||
KASSERT(pcs,
|
||||
("[p4,%d] Null Per-Cpu state cpu=%d phycpu=%d", __LINE__,
|
||||
cpu, phycpu));
|
||||
if (pcs == NULL) /* decline to init */
|
||||
return ENXIO;
|
||||
pc = pmc_pcpu[phycpu];
|
||||
plc = pmc_pcpu[cpu];
|
||||
|
||||
plcs = malloc(sizeof(struct p4_logicalcpu),
|
||||
M_PMC, M_WAITOK|M_ZERO);
|
||||
KASSERT(plc != pc, ("[p4,%d] per-cpu config error", __LINE__));
|
||||
|
||||
/* The TSC is architectural state and is not shared */
|
||||
plcs->pc_hwpmcs[0] = &plcs->pc_tsc;
|
||||
plcs->pc_tsc.phw_state = PMC_PHW_FLAG_IS_ENABLED |
|
||||
PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(0) |
|
||||
PMC_PHW_FLAG_IS_SHAREABLE;
|
||||
PMCDBG(MDP,INI,1, "p4-init cpu=%d phycpu=%d pc=%p", cpu,
|
||||
phycpu, pc);
|
||||
KASSERT(pc, ("[p4,%d] Null Per-Cpu state cpu=%d phycpu=%d",
|
||||
__LINE__, cpu, phycpu));
|
||||
|
||||
/* Other PMCs are shared with the physical CPU */
|
||||
for (n = 1; n < P4_NPMCS; n++)
|
||||
plcs->pc_hwpmcs[n] = pcs->pc_hwpmcs[n];
|
||||
/* PMCs are shared with the physical CPU. */
|
||||
for (n = 0; n < P4_NPMCS; n++)
|
||||
plc->pc_hwpmcs[n + first_ri] =
|
||||
pc->pc_hwpmcs[n + first_ri];
|
||||
|
||||
pmc_pcpu[cpu] = (struct pmc_cpu *) plcs;
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
pcs = malloc(sizeof(struct p4_cpu), M_PMC, M_WAITOK|M_ZERO);
|
||||
p4c = malloc(sizeof(struct p4_cpu), M_PMC, M_WAITOK|M_ZERO);
|
||||
|
||||
if (pcs == NULL)
|
||||
return ENOMEM;
|
||||
phw = pcs->pc_p4pmcs;
|
||||
if (p4c == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
pc = pmc_pcpu[cpu];
|
||||
|
||||
KASSERT(pc != NULL, ("[p4,%d] cpu %d null per-cpu", __LINE__, cpu));
|
||||
|
||||
p4_pcpu[cpu] = p4c;
|
||||
phw = p4c->pc_p4pmcs;
|
||||
|
||||
for (n = 0; n < P4_NPMCS; n++, phw++) {
|
||||
phw->phw_state = PMC_PHW_FLAG_IS_ENABLED |
|
||||
PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n);
|
||||
phw->phw_pmc = NULL;
|
||||
pcs->pc_hwpmcs[n] = phw;
|
||||
pc->pc_hwpmcs[n + first_ri] = phw;
|
||||
}
|
||||
|
||||
/* Mark the TSC as shareable */
|
||||
pcs->pc_hwpmcs[0]->phw_state |= PMC_PHW_FLAG_IS_SHAREABLE;
|
||||
|
||||
pescr = pcs->pc_escrs;
|
||||
pescr = p4c->pc_escrs;
|
||||
for (n = 0; n < P4_NESCR; n++)
|
||||
*pescr++ = P4_INVALID_PMC_INDEX;
|
||||
pmc_pcpu[cpu] = (struct pmc_cpu *) pcs;
|
||||
|
||||
mtx_init(&pcs->pc_mtx, "p4-pcpu", "pmc-leaf", MTX_SPIN);
|
||||
mtx_init(&p4c->pc_mtx, "p4-pcpu", "pmc-leaf", MTX_SPIN);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -676,74 +629,39 @@ p4_init(int cpu)
|
||||
*/
|
||||
|
||||
static int
|
||||
p4_cleanup(int cpu)
|
||||
p4_pcpu_fini(struct pmc_mdep *md, int cpu)
|
||||
{
|
||||
int i;
|
||||
struct p4_cpu *pcs;
|
||||
int first_ri, i;
|
||||
struct p4_cpu *p4c;
|
||||
struct pmc_cpu *pc;
|
||||
|
||||
PMCDBG(MDP,INI,0, "p4-cleanup cpu=%d", cpu);
|
||||
|
||||
if ((pcs = (struct p4_cpu *) pmc_pcpu[cpu]) == NULL)
|
||||
return 0;
|
||||
pc = pmc_pcpu[cpu];
|
||||
first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P4].pcd_ri;
|
||||
|
||||
for (i = 0; i < P4_NPMCS; i++)
|
||||
pc->pc_hwpmcs[i + first_ri] = NULL;
|
||||
|
||||
if (!pmc_cpu_is_primary(cpu) && (cpu & 1))
|
||||
return (0);
|
||||
|
||||
p4c = p4_pcpu[cpu];
|
||||
|
||||
KASSERT(p4c != NULL, ("[p4,%d] NULL pcpu", __LINE__));
|
||||
|
||||
/* Turn off all PMCs on this CPU */
|
||||
for (i = 0; i < P4_NPMCS - 1; i++)
|
||||
wrmsr(P4_CCCR_MSR_FIRST + i,
|
||||
rdmsr(P4_CCCR_MSR_FIRST + i) & ~P4_CCCR_ENABLE);
|
||||
|
||||
/*
|
||||
* If the CPU is physical we need to teardown the
|
||||
* full MD state.
|
||||
*/
|
||||
if (!P4_CPU_IS_HTT_SECONDARY(cpu))
|
||||
mtx_destroy(&pcs->pc_mtx);
|
||||
mtx_destroy(&p4c->pc_mtx);
|
||||
|
||||
free(pcs, M_PMC);
|
||||
free(p4c, M_PMC);
|
||||
|
||||
pmc_pcpu[cpu] = NULL;
|
||||
p4_pcpu[cpu] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context switch in.
|
||||
*/
|
||||
|
||||
static int
|
||||
p4_switch_in(struct pmc_cpu *pc, struct pmc_process *pp)
|
||||
{
|
||||
(void) pc;
|
||||
|
||||
PMCDBG(MDP,SWI,1, "pc=%p pp=%p enable-msr=%d", pc, pp,
|
||||
(pp->pp_flags & PMC_PP_ENABLE_MSR_ACCESS) != 0);
|
||||
|
||||
/* enable the RDPMC instruction */
|
||||
if (pp->pp_flags & PMC_PP_ENABLE_MSR_ACCESS)
|
||||
load_cr4(rcr4() | CR4_PCE);
|
||||
|
||||
PMCDBG(MDP,SWI,2, "cr4=0x%x", (uint32_t) rcr4());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context switch out.
|
||||
*/
|
||||
|
||||
static int
|
||||
p4_switch_out(struct pmc_cpu *pc, struct pmc_process *pp)
|
||||
{
|
||||
(void) pc;
|
||||
(void) pp; /* can be null */
|
||||
|
||||
PMCDBG(MDP,SWO,1, "pc=%p pp=%p", pc, pp);
|
||||
|
||||
/* always disallow the RDPMC instruction */
|
||||
load_cr4(rcr4() & ~CR4_PCE);
|
||||
|
||||
PMCDBG(MDP,SWO,2, "cr4=0x%x", (uint32_t) rcr4());
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -753,50 +671,27 @@ p4_switch_out(struct pmc_cpu *pc, struct pmc_process *pp)
|
||||
static int
|
||||
p4_read_pmc(int cpu, int ri, pmc_value_t *v)
|
||||
{
|
||||
struct pmc *pm;
|
||||
pmc_value_t tmp;
|
||||
struct p4_cpu *pc;
|
||||
enum pmc_mode mode;
|
||||
struct p4pmc_descr *pd;
|
||||
struct pmc *pm;
|
||||
struct p4_cpu *pc;
|
||||
struct pmc_hw *phw;
|
||||
pmc_value_t tmp;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p4,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < P4_NPMCS,
|
||||
("[p4,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
|
||||
if (ri == 0) { /* TSC */
|
||||
#ifdef DEBUG
|
||||
pc = (struct p4_cpu *) pmc_pcpu[cpu];
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
|
||||
KASSERT(pm, ("[p4,%d] cpu=%d ri=%d not configured", __LINE__,
|
||||
cpu, ri));
|
||||
KASSERT(PMC_TO_CLASS(pm) == PMC_CLASS_TSC,
|
||||
("[p4,%d] cpu=%d ri=%d not a TSC (%d)", __LINE__, cpu, ri,
|
||||
PMC_TO_CLASS(pm)));
|
||||
KASSERT(PMC_IS_COUNTING_MODE(PMC_TO_MODE(pm)),
|
||||
("[p4,%d] TSC counter in non-counting mode", __LINE__));
|
||||
#endif
|
||||
*v = rdtsc();
|
||||
PMCDBG(MDP,REA,2, "p4-read -> %jx", *v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
pd = &p4_pmcdesc[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pc = p4_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
pm = pc->pc_p4pmcs[ri].phw_pmc;
|
||||
pd = &p4_pmcdesc[ri];
|
||||
|
||||
KASSERT(pm != NULL,
|
||||
("[p4,%d] No owner for HWPMC [cpu%d,pmc%d]", __LINE__,
|
||||
cpu, ri));
|
||||
("[p4,%d] No owner for HWPMC [cpu%d,pmc%d]", __LINE__, cpu, ri));
|
||||
|
||||
KASSERT(pd->pm_descr.pd_class == PMC_TO_CLASS(pm),
|
||||
("[p4,%d] class mismatch pd %d != id class %d", __LINE__,
|
||||
pd->pm_descr.pd_class, PMC_TO_CLASS(pm)));
|
||||
pd->pm_descr.pd_class, PMC_TO_CLASS(pm)));
|
||||
|
||||
mode = PMC_TO_MODE(pm);
|
||||
|
||||
@ -822,7 +717,8 @@ p4_read_pmc(int cpu, int ri, pmc_value_t *v)
|
||||
*v = tmp;
|
||||
|
||||
PMCDBG(MDP,REA,2, "p4-read -> %jx", *v);
|
||||
return 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -843,29 +739,8 @@ p4_write_pmc(int cpu, int ri, pmc_value_t v)
|
||||
KASSERT(ri >= 0 && ri < P4_NPMCS,
|
||||
("[amd,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
|
||||
/*
|
||||
* The P4's TSC register is writeable, but we don't allow a
|
||||
* write as changing the TSC's value could interfere with
|
||||
* timekeeping and other system functions.
|
||||
*/
|
||||
if (ri == 0) {
|
||||
#ifdef DEBUG
|
||||
pc = (struct p4_cpu *) pmc_pcpu[cpu];
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
KASSERT(pm, ("[p4,%d] cpu=%d ri=%d not configured", __LINE__,
|
||||
cpu, ri));
|
||||
KASSERT(PMC_TO_CLASS(pm) == PMC_CLASS_TSC,
|
||||
("[p4,%d] cpu=%d ri=%d not a TSC (%d)", __LINE__,
|
||||
cpu, ri, PMC_TO_CLASS(pm)));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Shared PMCs */
|
||||
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
pc = p4_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
phw = &pc->pc_p4pmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pd = &p4_pmcdesc[ri];
|
||||
|
||||
@ -891,7 +766,7 @@ p4_write_pmc(int cpu, int ri, pmc_value_t v)
|
||||
else
|
||||
P4_PCPU_PMC_VALUE(pc,ri,cpu) = v;
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -914,25 +789,14 @@ p4_config_pmc(int cpu, int ri, struct pmc *pm)
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p4,%d] illegal CPU %d", __LINE__, cpu));
|
||||
|
||||
KASSERT(ri >= 0 && ri < P4_NPMCS,
|
||||
("[p4,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
|
||||
|
||||
if (ri == 0) { /* TSC */
|
||||
pc = (struct p4_cpu *) pmc_pcpu[cpu];
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
|
||||
KASSERT(pm == NULL || phw->phw_pmc == NULL,
|
||||
("[p4,%d] hwpmc doubly config'ed", __LINE__));
|
||||
phw->phw_pmc = pm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Shared PMCs */
|
||||
|
||||
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
pc = p4_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
phw = &pc->pc_p4pmcs[ri];
|
||||
|
||||
KASSERT(pm == NULL || phw->phw_pmc == NULL ||
|
||||
(p4_system_has_htt && phw->phw_pmc == pm),
|
||||
@ -975,7 +839,7 @@ p4_config_pmc(int cpu, int ri, struct pmc *pm)
|
||||
|
||||
mtx_unlock_spin(&pc->pc_mtx);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -985,19 +849,22 @@ p4_config_pmc(int cpu, int ri, struct pmc *pm)
|
||||
static int
|
||||
p4_get_config(int cpu, int ri, struct pmc **ppm)
|
||||
{
|
||||
struct p4_cpu *pc;
|
||||
struct pmc_hw *phw;
|
||||
int cfgflags;
|
||||
struct p4_cpu *pc;
|
||||
|
||||
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p4,%d] illegal CPU %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < P4_NPMCS,
|
||||
("[p4,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
pc = p4_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
|
||||
mtx_lock_spin(&pc->pc_mtx);
|
||||
cfgflags = P4_PCPU_GET_CFGFLAGS(pc,ri);
|
||||
mtx_unlock_spin(&pc->pc_mtx);
|
||||
|
||||
if (cfgflags & P4_CPU_TO_FLAG(cpu))
|
||||
*ppm = phw->phw_pmc; /* PMC config'ed on this CPU */
|
||||
*ppm = pc->pc_p4pmcs[ri].phw_pmc; /* PMC config'ed on this CPU */
|
||||
else
|
||||
*ppm = NULL;
|
||||
|
||||
@ -1062,31 +929,23 @@ p4_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
|
||||
/* check class */
|
||||
if (pd->pm_descr.pd_class != a->pm_class)
|
||||
return EINVAL;
|
||||
return (EINVAL);
|
||||
|
||||
/* check requested capabilities */
|
||||
caps = a->pm_caps;
|
||||
if ((pd->pm_descr.pd_caps & caps) != caps)
|
||||
return EPERM;
|
||||
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC) {
|
||||
/* TSC's are always allocated in system-wide counting mode */
|
||||
if (a->pm_ev != PMC_EV_TSC_TSC ||
|
||||
a->pm_mode != PMC_MODE_SC)
|
||||
return EINVAL;
|
||||
return 0;
|
||||
}
|
||||
return (EPERM);
|
||||
|
||||
/*
|
||||
* If the system has HTT enabled, and the desired allocation
|
||||
* mode is process-private, and the PMC row disposition is not
|
||||
* free (0), decline the allocation.
|
||||
* FREE (0), decline the allocation.
|
||||
*/
|
||||
|
||||
if (p4_system_has_htt &&
|
||||
PMC_IS_VIRTUAL_MODE(PMC_TO_MODE(pm)) &&
|
||||
pmc_getrowdisp(ri) != 0)
|
||||
return EBUSY;
|
||||
return (EBUSY);
|
||||
|
||||
KASSERT(pd->pm_descr.pd_class == PMC_CLASS_P4,
|
||||
("[p4,%d] unknown PMC class %d", __LINE__,
|
||||
@ -1094,10 +953,10 @@ p4_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
|
||||
if (pm->pm_event < PMC_EV_P4_FIRST ||
|
||||
pm->pm_event > PMC_EV_P4_LAST)
|
||||
return EINVAL;
|
||||
return (EINVAL);
|
||||
|
||||
if ((pevent = p4_find_event(pm->pm_event)) == NULL)
|
||||
return ESRCH;
|
||||
return (ESRCH);
|
||||
|
||||
PMCDBG(MDP,ALL,2, "pevent={ev=%d,escrsel=0x%x,cccrsel=0x%x,isti=%d}",
|
||||
pevent->pm_event, pevent->pm_escr_eventselect,
|
||||
@ -1112,9 +971,9 @@ p4_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
if (P4_EVENT_IS_TI(pevent) &&
|
||||
PMC_IS_VIRTUAL_MODE(PMC_TO_MODE(pm)) &&
|
||||
p4_system_has_htt)
|
||||
return EINVAL;
|
||||
return (EINVAL);
|
||||
|
||||
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
pc = p4_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
|
||||
found = 0;
|
||||
|
||||
@ -1170,7 +1029,7 @@ p4_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
}
|
||||
|
||||
if (found == 0)
|
||||
return ESRCH;
|
||||
return (ESRCH);
|
||||
|
||||
KASSERT((int) escr >= 0 && escr < P4_NESCR,
|
||||
("[p4,%d] illegal ESCR value %d", __LINE__, escr));
|
||||
@ -1243,7 +1102,7 @@ p4_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
"escr=%d escrmsr=0x%x escrval=0x%x", pevent->pm_cccr_select,
|
||||
cccrvalue, escr, pm->pm_md.pm_p4.pm_p4_escrmsr, escrvalue);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1254,21 +1113,19 @@ static int
|
||||
p4_release_pmc(int cpu, int ri, struct pmc *pm)
|
||||
{
|
||||
enum pmc_p4escr escr;
|
||||
struct pmc_hw *phw;
|
||||
struct p4_cpu *pc;
|
||||
|
||||
if (p4_pmcdesc[ri].pm_descr.pd_class == PMC_CLASS_TSC)
|
||||
return 0;
|
||||
KASSERT(ri >= 0 && ri < P4_NPMCS,
|
||||
("[p4,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
escr = pm->pm_md.pm_p4.pm_p4_escr;
|
||||
|
||||
PMCDBG(MDP,REL,1, "p4-release cpu=%d ri=%d escr=%d", cpu, ri, escr);
|
||||
|
||||
if (PMC_IS_SYSTEM_MODE(PMC_TO_MODE(pm))) {
|
||||
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
pc = p4_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
|
||||
KASSERT(phw->phw_pmc == NULL,
|
||||
KASSERT(pc->pc_p4pmcs[ri].phw_pmc == NULL,
|
||||
("[p4,%d] releasing configured PMC ri=%d", __LINE__, ri));
|
||||
|
||||
P4_ESCR_UNMARK_ROW_STANDALONE(escr);
|
||||
@ -1279,7 +1136,7 @@ p4_release_pmc(int cpu, int ri, struct pmc *pm)
|
||||
} else
|
||||
P4_ESCR_UNMARK_ROW_THREAD(escr);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1290,31 +1147,25 @@ static int
|
||||
p4_start_pmc(int cpu, int ri)
|
||||
{
|
||||
int rc;
|
||||
uint32_t cccrvalue, cccrtbits, escrvalue, escrmsr, escrtbits;
|
||||
struct pmc *pm;
|
||||
struct p4_cpu *pc;
|
||||
struct pmc_hw *phw;
|
||||
struct p4pmc_descr *pd;
|
||||
uint32_t cccrvalue, cccrtbits, escrvalue, escrmsr, escrtbits;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p4,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < P4_NPMCS,
|
||||
("[p4,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pd = &p4_pmcdesc[ri];
|
||||
pc = p4_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
pm = pc->pc_p4pmcs[ri].phw_pmc;
|
||||
pd = &p4_pmcdesc[ri];
|
||||
|
||||
KASSERT(pm != NULL,
|
||||
("[p4,%d] starting cpu%d,pmc%d with null pmc", __LINE__,
|
||||
cpu, ri));
|
||||
("[p4,%d] starting cpu%d,pmc%d with null pmc", __LINE__, cpu, ri));
|
||||
|
||||
PMCDBG(MDP,STA,1, "p4-start cpu=%d ri=%d", cpu, ri);
|
||||
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC) /* TSC are always on */
|
||||
return 0;
|
||||
|
||||
KASSERT(pd->pm_descr.pd_class == PMC_CLASS_P4,
|
||||
("[p4,%d] wrong PMC class %d", __LINE__,
|
||||
pd->pm_descr.pd_class));
|
||||
@ -1430,7 +1281,7 @@ p4_start_pmc(int cpu, int ri)
|
||||
ri, pm->pm_md.pm_p4.pm_p4_escr, escrmsr, escrvalue,
|
||||
cccrvalue, P4_PCPU_HW_VALUE(pc,ri,cpu));
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1444,7 +1295,6 @@ p4_stop_pmc(int cpu, int ri)
|
||||
uint32_t cccrvalue, cccrtbits, escrvalue, escrmsr, escrtbits;
|
||||
struct pmc *pm;
|
||||
struct p4_cpu *pc;
|
||||
struct pmc_hw *phw;
|
||||
struct p4pmc_descr *pd;
|
||||
pmc_value_t tmp;
|
||||
|
||||
@ -1453,18 +1303,9 @@ p4_stop_pmc(int cpu, int ri)
|
||||
KASSERT(ri >= 0 && ri < P4_NPMCS,
|
||||
("[p4,%d] illegal row index %d", __LINE__, ri));
|
||||
|
||||
pd = &p4_pmcdesc[ri];
|
||||
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
|
||||
return 0;
|
||||
|
||||
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
|
||||
KASSERT(phw != NULL,
|
||||
("[p4,%d] null phw for cpu%d, ri%d", __LINE__, cpu, ri));
|
||||
|
||||
pm = phw->phw_pmc;
|
||||
pd = &p4_pmcdesc[ri];
|
||||
pc = p4_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
pm = pc->pc_p4pmcs[ri].phw_pmc;
|
||||
|
||||
KASSERT(pm != NULL,
|
||||
("[p4,%d] null pmc for cpu%d, ri%d", __LINE__, cpu, ri));
|
||||
@ -1474,7 +1315,7 @@ p4_stop_pmc(int cpu, int ri)
|
||||
if (PMC_IS_SYSTEM_MODE(PMC_TO_MODE(pm))) {
|
||||
wrmsr(pd->pm_cccr_msr,
|
||||
pm->pm_md.pm_p4.pm_p4_cccrvalue & ~P4_CCCR_ENABLE);
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1569,8 +1410,7 @@ static int
|
||||
p4_intr(int cpu, struct trapframe *tf)
|
||||
{
|
||||
uint32_t cccrval, ovf_mask, ovf_partner;
|
||||
int i, did_interrupt, error, ri;
|
||||
struct pmc_hw *phw;
|
||||
int did_interrupt, error, ri;
|
||||
struct p4_cpu *pc;
|
||||
struct pmc *pm;
|
||||
pmc_value_t v;
|
||||
@ -1578,7 +1418,7 @@ p4_intr(int cpu, struct trapframe *tf)
|
||||
PMCDBG(MDP,INT, 1, "cpu=%d tf=0x%p um=%d", cpu, (void *) tf,
|
||||
TRAPF_USERMODE(tf));
|
||||
|
||||
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
pc = p4_pcpu[P4_TO_HTT_PRIMARY(cpu)];
|
||||
|
||||
ovf_mask = P4_CPU_IS_HTT_SECONDARY(cpu) ?
|
||||
P4_CCCR_OVF_PMI_T1 : P4_CCCR_OVF_PMI_T0;
|
||||
@ -1597,9 +1437,7 @@ p4_intr(int cpu, struct trapframe *tf)
|
||||
* Loop through all CCCRs, looking for ones that have
|
||||
* interrupted this CPU.
|
||||
*/
|
||||
for (i = 0; i < P4_NPMCS-1; i++) {
|
||||
|
||||
ri = i + 1; /* row index */
|
||||
for (ri = 0; ri < P4_NPMCS; ri++) {
|
||||
|
||||
/*
|
||||
* Check if our partner logical CPU has already marked
|
||||
@ -1615,8 +1453,7 @@ p4_intr(int cpu, struct trapframe *tf)
|
||||
* Ignore de-configured or stopped PMCs.
|
||||
* Ignore PMCs not in sampling mode.
|
||||
*/
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pm = pc->pc_p4pmcs[ri].phw_pmc;
|
||||
if (pm == NULL ||
|
||||
pm->pm_state != PMC_STATE_RUNNING ||
|
||||
!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
|
||||
@ -1632,7 +1469,7 @@ p4_intr(int cpu, struct trapframe *tf)
|
||||
* and the OVF_Tx bit for this logical
|
||||
* processor being set.
|
||||
*/
|
||||
cccrval = rdmsr(P4_CCCR_MSR_FIRST + i);
|
||||
cccrval = rdmsr(P4_CCCR_MSR_FIRST + ri);
|
||||
|
||||
if ((cccrval & ovf_mask) != ovf_mask)
|
||||
continue;
|
||||
@ -1646,13 +1483,13 @@ p4_intr(int cpu, struct trapframe *tf)
|
||||
if (p4_system_has_htt && (cccrval & ovf_partner))
|
||||
P4_PCPU_SET_INTRFLAG(pc, ri, 1);
|
||||
|
||||
v = rdmsr(P4_PERFCTR_MSR_FIRST + i);
|
||||
v = rdmsr(P4_PERFCTR_MSR_FIRST + ri);
|
||||
|
||||
PMCDBG(MDP,INT, 2, "ri=%d v=%jx", ri, v);
|
||||
|
||||
/* Stop the counter, and reset the overflow bit */
|
||||
cccrval &= ~(P4_CCCR_OVF | P4_CCCR_ENABLE);
|
||||
wrmsr(P4_CCCR_MSR_FIRST + i, cccrval);
|
||||
wrmsr(P4_CCCR_MSR_FIRST + ri, cccrval);
|
||||
|
||||
did_interrupt = 1;
|
||||
|
||||
@ -1660,8 +1497,7 @@ p4_intr(int cpu, struct trapframe *tf)
|
||||
* Ignore de-configured or stopped PMCs. Ignore PMCs
|
||||
* not in sampling mode.
|
||||
*/
|
||||
phw = pc->pc_hwpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pm = pc->pc_p4pmcs[ri].phw_pmc;
|
||||
|
||||
if (pm == NULL ||
|
||||
pm->pm_state != PMC_STATE_RUNNING ||
|
||||
@ -1683,9 +1519,9 @@ p4_intr(int cpu, struct trapframe *tf)
|
||||
*/
|
||||
v = P4_RELOAD_COUNT_TO_PERFCTR_VALUE(
|
||||
pm->pm_sc.pm_reloadcount);
|
||||
wrmsr(P4_PERFCTR_MSR_FIRST + i, v);
|
||||
wrmsr(P4_PERFCTR_MSR_FIRST + ri, v);
|
||||
if (error == 0)
|
||||
wrmsr(P4_CCCR_MSR_FIRST + i,
|
||||
wrmsr(P4_CCCR_MSR_FIRST + ri,
|
||||
cccrval | P4_CCCR_ENABLE);
|
||||
}
|
||||
|
||||
@ -1718,7 +1554,6 @@ p4_describe(int cpu, int ri, struct pmc_info *pi,
|
||||
{
|
||||
int error;
|
||||
size_t copied;
|
||||
struct pmc_hw *phw;
|
||||
const struct p4pmc_descr *pd;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
@ -1729,26 +1564,25 @@ p4_describe(int cpu, int ri, struct pmc_info *pi,
|
||||
PMCDBG(MDP,OPS,1,"p4-describe cpu=%d ri=%d", cpu, ri);
|
||||
|
||||
if (P4_CPU_IS_HTT_SECONDARY(cpu))
|
||||
return EINVAL;
|
||||
return (EINVAL);
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
pd = &p4_pmcdesc[ri];
|
||||
|
||||
if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name,
|
||||
PMC_NAME_MAX, &copied)) != 0)
|
||||
return error;
|
||||
PMC_NAME_MAX, &copied)) != 0)
|
||||
return (error);
|
||||
|
||||
pi->pm_class = pd->pm_descr.pd_class;
|
||||
|
||||
if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
|
||||
if (p4_pcpu[cpu]->pc_p4pmcs[ri].phw_state & PMC_PHW_FLAG_IS_ENABLED) {
|
||||
pi->pm_enabled = TRUE;
|
||||
*ppmc = phw->phw_pmc;
|
||||
*ppmc = p4_pcpu[cpu]->pc_p4pmcs[ri].phw_pmc;
|
||||
} else {
|
||||
pi->pm_enabled = FALSE;
|
||||
*ppmc = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1770,41 +1604,52 @@ p4_get_msr(int ri, uint32_t *msr)
|
||||
|
||||
|
||||
int
|
||||
pmc_initialize_p4(struct pmc_mdep *pmc_mdep)
|
||||
pmc_p4_initialize(struct pmc_mdep *md, int ncpus)
|
||||
{
|
||||
struct pmc_classdep *pcd;
|
||||
struct p4_event_descr *pe;
|
||||
|
||||
KASSERT(md != NULL, ("[p4,%d] md is NULL", __LINE__));
|
||||
KASSERT(strcmp(cpu_vendor, "GenuineIntel") == 0,
|
||||
("[p4,%d] Initializing non-intel processor", __LINE__));
|
||||
|
||||
PMCDBG(MDP,INI,1, "%s", "p4-initialize");
|
||||
|
||||
switch (pmc_mdep->pmd_cputype) {
|
||||
/* Allocate space for pointers to per-cpu descriptors. */
|
||||
p4_pcpu = malloc(sizeof(struct p4_cpu **) * ncpus, M_PMC,
|
||||
M_ZERO|M_WAITOK);
|
||||
|
||||
/* Fill in the class dependent descriptor. */
|
||||
pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P4];
|
||||
|
||||
switch (md->pmd_cputype) {
|
||||
case PMC_CPU_INTEL_PIV:
|
||||
|
||||
pmc_mdep->pmd_npmc = P4_NPMCS;
|
||||
pmc_mdep->pmd_classes[1].pm_class = PMC_CLASS_P4;
|
||||
pmc_mdep->pmd_classes[1].pm_caps = P4_PMC_CAPS;
|
||||
pmc_mdep->pmd_classes[1].pm_width = 40;
|
||||
pmc_mdep->pmd_nclasspmcs[1] = 18;
|
||||
pcd->pcd_caps = P4_PMC_CAPS;
|
||||
pcd->pcd_class = PMC_CLASS_P4;
|
||||
pcd->pcd_num = P4_NPMCS;
|
||||
pcd->pcd_ri = md->pmd_npmc;
|
||||
pcd->pcd_width = 40;
|
||||
|
||||
pmc_mdep->pmd_init = p4_init;
|
||||
pmc_mdep->pmd_cleanup = p4_cleanup;
|
||||
pmc_mdep->pmd_switch_in = p4_switch_in;
|
||||
pmc_mdep->pmd_switch_out = p4_switch_out;
|
||||
pmc_mdep->pmd_read_pmc = p4_read_pmc;
|
||||
pmc_mdep->pmd_write_pmc = p4_write_pmc;
|
||||
pmc_mdep->pmd_config_pmc = p4_config_pmc;
|
||||
pmc_mdep->pmd_get_config = p4_get_config;
|
||||
pmc_mdep->pmd_allocate_pmc = p4_allocate_pmc;
|
||||
pmc_mdep->pmd_release_pmc = p4_release_pmc;
|
||||
pmc_mdep->pmd_start_pmc = p4_start_pmc;
|
||||
pmc_mdep->pmd_stop_pmc = p4_stop_pmc;
|
||||
pmc_mdep->pmd_intr = p4_intr;
|
||||
pmc_mdep->pmd_describe = p4_describe;
|
||||
pmc_mdep->pmd_get_msr = p4_get_msr; /* i386 */
|
||||
pcd->pcd_allocate_pmc = p4_allocate_pmc;
|
||||
pcd->pcd_config_pmc = p4_config_pmc;
|
||||
pcd->pcd_describe = p4_describe;
|
||||
pcd->pcd_get_config = p4_get_config;
|
||||
pcd->pcd_get_msr = p4_get_msr;
|
||||
pcd->pcd_pcpu_fini = p4_pcpu_fini;
|
||||
pcd->pcd_pcpu_init = p4_pcpu_init;
|
||||
pcd->pcd_read_pmc = p4_read_pmc;
|
||||
pcd->pcd_release_pmc = p4_release_pmc;
|
||||
pcd->pcd_start_pmc = p4_start_pmc;
|
||||
pcd->pcd_stop_pmc = p4_stop_pmc;
|
||||
pcd->pcd_write_pmc = p4_write_pmc;
|
||||
|
||||
/* model specific munging */
|
||||
md->pmd_pcpu_fini = NULL;
|
||||
md->pmd_pcpu_init = NULL;
|
||||
md->pmd_intr = p4_intr;
|
||||
md->pmd_npmc += P4_NPMCS;
|
||||
|
||||
/* model specific configuration */
|
||||
if ((cpu_id & 0xFFF) < 0xF27) {
|
||||
|
||||
/*
|
||||
@ -1824,5 +1669,26 @@ pmc_initialize_p4(struct pmc_mdep *pmc_mdep)
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
pmc_p4_finalize(struct pmc_mdep *md)
|
||||
{
|
||||
#if defined(INVARIANTS)
|
||||
int i, ncpus;
|
||||
#endif
|
||||
|
||||
KASSERT(p4_pcpu != NULL,
|
||||
("[p4,%d] NULL p4_pcpu", __LINE__));
|
||||
|
||||
#if defined(INVARIANTS)
|
||||
ncpus = pmc_cpu_max();
|
||||
for (i = 0; i < ncpus; i++)
|
||||
KASSERT(p4_pcpu[i] == NULL, ("[p4,%d] non-null pcpu %d",
|
||||
__LINE__, i));
|
||||
#endif
|
||||
|
||||
free(p4_pcpu, M_PMC);
|
||||
p4_pcpu = NULL;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
/* Intel P4 PMCs */
|
||||
|
||||
#define P4_NPMCS 19 /* 1 TSC + 18 PMCS */
|
||||
#define P4_NPMCS 18
|
||||
#define P4_NESCR 45
|
||||
#define P4_INVALID_PMC_INDEX -1
|
||||
#define P4_MAX_ESCR_PER_EVENT 2
|
||||
@ -118,7 +118,8 @@ struct pmc_md_p4_pmc {
|
||||
* Prototypes
|
||||
*/
|
||||
|
||||
int pmc_initialize_p4(struct pmc_mdep *); /* Pentium IV PMCs */
|
||||
int pmc_p4_initialize(struct pmc_mdep *_md, int _ncpus);
|
||||
void pmc_p4_finalize(struct pmc_mdep *md);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
#endif /* _MACHINE_PMC_MDEP_H */
|
||||
#endif /* _DEV_HWPMC_PIV_H_ */
|
||||
|
@ -73,19 +73,6 @@ struct p6pmc_descr {
|
||||
|
||||
static struct p6pmc_descr p6_pmcdesc[P6_NPMCS] = {
|
||||
|
||||
/* TSC */
|
||||
{
|
||||
.pm_descr =
|
||||
{
|
||||
.pd_name = "TSC",
|
||||
.pd_class = PMC_CLASS_TSC,
|
||||
.pd_caps = PMC_CAP_READ,
|
||||
.pd_width = 64
|
||||
},
|
||||
.pm_pmc_msr = 0x10,
|
||||
.pm_evsel_msr = ~0
|
||||
},
|
||||
|
||||
#define P6_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | PMC_CAP_SYSTEM | \
|
||||
PMC_CAP_EDGE | PMC_CAP_THRESHOLD | PMC_CAP_READ | PMC_CAP_WRITE | \
|
||||
PMC_CAP_INVERT | PMC_CAP_QUALIFIER)
|
||||
@ -306,12 +293,12 @@ p6_find_event(enum pmc_event ev)
|
||||
*/
|
||||
|
||||
struct p6_cpu {
|
||||
struct pmc_cpu pc_common;
|
||||
struct pmc_hw *pc_hwpmcs[P6_NPMCS];
|
||||
struct pmc_hw pc_p6pmcs[P6_NPMCS];
|
||||
uint32_t pc_state;
|
||||
};
|
||||
|
||||
static struct p6_cpu **p6_pcpu;
|
||||
|
||||
/*
|
||||
* If CTR1 is active, we need to keep the 'EN' bit if CTR0 set,
|
||||
* with the rest of CTR0 being zero'ed out.
|
||||
@ -338,10 +325,11 @@ struct p6_cpu {
|
||||
} while (0)
|
||||
|
||||
static int
|
||||
p6_init(int cpu)
|
||||
p6_pcpu_init(struct pmc_mdep *md, int cpu)
|
||||
{
|
||||
int n;
|
||||
struct p6_cpu *pcs;
|
||||
int first_ri, n;
|
||||
struct p6_cpu *p6c;
|
||||
struct pmc_cpu *pc;
|
||||
struct pmc_hw *phw;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
@ -349,94 +337,71 @@ p6_init(int cpu)
|
||||
|
||||
PMCDBG(MDP,INI,0,"p6-init cpu=%d", cpu);
|
||||
|
||||
pcs = malloc(sizeof(struct p6_cpu), M_PMC,
|
||||
M_WAITOK|M_ZERO);
|
||||
p6c = malloc(sizeof (struct p6_cpu), M_PMC, M_WAITOK|M_ZERO);
|
||||
pc = pmc_pcpu[cpu];
|
||||
|
||||
phw = pcs->pc_p6pmcs;
|
||||
KASSERT(pc != NULL, ("[p6,%d] cpu %d null per-cpu", __LINE__, cpu));
|
||||
|
||||
phw = p6c->pc_p6pmcs;
|
||||
p6_pcpu[cpu] = p6c;
|
||||
|
||||
first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6].pcd_ri;
|
||||
|
||||
for (n = 0; n < P6_NPMCS; n++, phw++) {
|
||||
phw->phw_state = PMC_PHW_FLAG_IS_ENABLED |
|
||||
PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n);
|
||||
phw->phw_pmc = NULL;
|
||||
pcs->pc_hwpmcs[n] = phw;
|
||||
pc->pc_hwpmcs[n + first_ri] = phw;
|
||||
}
|
||||
|
||||
/* Mark the TSC as shareable */
|
||||
pcs->pc_hwpmcs[0]->phw_state |= PMC_PHW_FLAG_IS_SHAREABLE;
|
||||
|
||||
pmc_pcpu[cpu] = (struct pmc_cpu *) pcs;
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
p6_cleanup(int cpu)
|
||||
p6_pcpu_fini(struct pmc_mdep *md, int cpu)
|
||||
{
|
||||
struct pmc_cpu *pcs;
|
||||
int first_ri, n;
|
||||
struct p6_cpu *p6c;
|
||||
struct pmc_cpu *pc;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p6,%d] bad cpu %d", __LINE__, cpu));
|
||||
|
||||
PMCDBG(MDP,INI,0,"p6-cleanup cpu=%d", cpu);
|
||||
|
||||
if ((pcs = pmc_pcpu[cpu]) != NULL)
|
||||
free(pcs, M_PMC);
|
||||
pmc_pcpu[cpu] = NULL;
|
||||
p6c = p6_pcpu[cpu];
|
||||
p6_pcpu[cpu] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
KASSERT(p6c != NULL, ("[p6,%d] null pcpu", __LINE__));
|
||||
|
||||
static int
|
||||
p6_switch_in(struct pmc_cpu *pc, struct pmc_process *pp)
|
||||
{
|
||||
(void) pc;
|
||||
free(p6c, M_PMC);
|
||||
|
||||
PMCDBG(MDP,SWI,1, "pc=%p pp=%p enable-msr=%d", pc, pp,
|
||||
pp->pp_flags & PMC_PP_ENABLE_MSR_ACCESS);
|
||||
first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6].pcd_ri;
|
||||
pc = pmc_pcpu[cpu];
|
||||
for (n = 0; n < P6_NPMCS; n++)
|
||||
pc->pc_hwpmcs[n + first_ri] = NULL;
|
||||
|
||||
/* allow the RDPMC instruction if needed */
|
||||
if (pp->pp_flags & PMC_PP_ENABLE_MSR_ACCESS)
|
||||
load_cr4(rcr4() | CR4_PCE);
|
||||
|
||||
PMCDBG(MDP,SWI,1, "cr4=0x%x", rcr4());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
p6_switch_out(struct pmc_cpu *pc, struct pmc_process *pp)
|
||||
{
|
||||
(void) pc;
|
||||
(void) pp; /* can be NULL */
|
||||
|
||||
PMCDBG(MDP,SWO,1, "pc=%p pp=%p cr4=0x%x", pc, pp, rcr4());
|
||||
|
||||
/* always turn off the RDPMC instruction */
|
||||
load_cr4(rcr4() & ~CR4_PCE);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
p6_read_pmc(int cpu, int ri, pmc_value_t *v)
|
||||
{
|
||||
struct pmc_hw *phw;
|
||||
struct pmc *pm;
|
||||
struct p6pmc_descr *pd;
|
||||
pmc_value_t tmp;
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pd = &p6_pmcdesc[ri];
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p6,%d] illegal cpu value %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < P6_NPMCS,
|
||||
("[p6,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
pm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc;
|
||||
pd = &p6_pmcdesc[ri];
|
||||
|
||||
KASSERT(pm,
|
||||
("[p6,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
|
||||
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC) {
|
||||
*v = rdtsc();
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmp = rdmsr(pd->pm_pmc_msr) & P6_PERFCTR_READ_MASK;
|
||||
if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
|
||||
*v = P6_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp);
|
||||
@ -446,26 +411,26 @@ p6_read_pmc(int cpu, int ri, pmc_value_t *v)
|
||||
PMCDBG(MDP,REA,1, "p6-read cpu=%d ri=%d msr=0x%x -> v=%jx", cpu, ri,
|
||||
pd->pm_pmc_msr, *v);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
p6_write_pmc(int cpu, int ri, pmc_value_t v)
|
||||
{
|
||||
struct pmc_hw *phw;
|
||||
struct pmc *pm;
|
||||
struct p6pmc_descr *pd;
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pd = &p6_pmcdesc[ri];
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p6,%d] illegal cpu value %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < P6_NPMCS,
|
||||
("[p6,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
pm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc;
|
||||
pd = &p6_pmcdesc[ri];
|
||||
|
||||
KASSERT(pm,
|
||||
("[p6,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
|
||||
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
|
||||
return 0;
|
||||
|
||||
PMCDBG(MDP,WRI,1, "p6-write cpu=%d ri=%d msr=0x%x v=%jx", cpu, ri,
|
||||
pd->pm_pmc_msr, v);
|
||||
|
||||
@ -474,20 +439,26 @@ p6_write_pmc(int cpu, int ri, pmc_value_t v)
|
||||
|
||||
wrmsr(pd->pm_pmc_msr, v & P6_PERFCTR_WRITE_MASK);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
p6_config_pmc(int cpu, int ri, struct pmc *pm)
|
||||
{
|
||||
struct pmc_hw *phw;
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p6,%d] illegal CPU %d", __LINE__, cpu));
|
||||
|
||||
KASSERT(ri >= 0 && ri < P6_NPMCS,
|
||||
("[p6,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
PMCDBG(MDP,CFG,1, "p6-config cpu=%d ri=%d pm=%p", cpu, ri, pm);
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
phw->phw_pmc = pm;
|
||||
KASSERT(p6_pcpu[cpu] != NULL, ("[p6,%d] null per-cpu %d", __LINE__,
|
||||
cpu));
|
||||
|
||||
return 0;
|
||||
p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc = pm;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -497,9 +468,15 @@ p6_config_pmc(int cpu, int ri, struct pmc *pm)
|
||||
static int
|
||||
p6_get_config(int cpu, int ri, struct pmc **ppm)
|
||||
{
|
||||
*ppm = pmc_pcpu[cpu]->pc_hwpmcs[ri]->phw_pmc;
|
||||
|
||||
return 0;
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p6,%d] illegal CPU %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < P6_NPMCS,
|
||||
("[p6,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
*ppm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
@ -521,9 +498,9 @@ p6_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
(void) cpu;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p4,%d] illegal CPU %d", __LINE__, cpu));
|
||||
("[p6,%d] illegal CPU %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < P6_NPMCS,
|
||||
("[p4,%d] illegal row-index value %d", __LINE__, ri));
|
||||
("[p6,%d] illegal row-index value %d", __LINE__, ri));
|
||||
|
||||
pd = &p6_pmcdesc[ri];
|
||||
|
||||
@ -533,36 +510,24 @@ p6_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
|
||||
/* check class */
|
||||
if (pd->pm_descr.pd_class != a->pm_class)
|
||||
return EINVAL;
|
||||
return (EINVAL);
|
||||
|
||||
/* check requested capabilities */
|
||||
caps = a->pm_caps;
|
||||
if ((pd->pm_descr.pd_caps & caps) != caps)
|
||||
return EPERM;
|
||||
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC) {
|
||||
/* TSC's are always allocated in system-wide counting mode */
|
||||
if (a->pm_ev != PMC_EV_TSC_TSC ||
|
||||
a->pm_mode != PMC_MODE_SC)
|
||||
return EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* P6 class events
|
||||
*/
|
||||
return (EPERM);
|
||||
|
||||
ev = pm->pm_event;
|
||||
|
||||
if (ev < PMC_EV_P6_FIRST || ev > PMC_EV_P6_LAST)
|
||||
return EINVAL;
|
||||
return (EINVAL);
|
||||
|
||||
if ((pevent = p6_find_event(ev)) == NULL)
|
||||
return ESRCH;
|
||||
return (ESRCH);
|
||||
|
||||
if (!P6_EVENT_VALID_FOR_CPU(pevent, p6_cputype) ||
|
||||
!P6_EVENT_VALID_FOR_CTR(pevent, (ri-1)))
|
||||
return EINVAL;
|
||||
return (EINVAL);
|
||||
|
||||
/* For certain events, Pentium M differs from the stock P6 */
|
||||
allowed_unitmask = 0;
|
||||
@ -577,7 +542,7 @@ p6_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
|
||||
unitmask = a->pm_md.pm_ppro.pm_ppro_config & P6_EVSEL_UMASK_MASK;
|
||||
if (unitmask & ~allowed_unitmask) /* disallow reserved bits */
|
||||
return EINVAL;
|
||||
return (EINVAL);
|
||||
|
||||
if (ev == PMC_EV_P6_MMX_UOPS_EXEC) /* hardcoded mask */
|
||||
unitmask = P6_EVSEL_TO_UMASK(0x0F);
|
||||
@ -612,14 +577,12 @@ p6_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
|
||||
PMCDBG(MDP,ALL,2, "p6-allocate config=0x%x", config);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
p6_release_pmc(int cpu, int ri, struct pmc *pm)
|
||||
{
|
||||
struct pmc_hw *phw;
|
||||
|
||||
(void) pm;
|
||||
|
||||
PMCDBG(MDP,REL,1, "p6-release cpu=%d ri=%d pm=%p", cpu, ri, pm);
|
||||
@ -629,12 +592,10 @@ p6_release_pmc(int cpu, int ri, struct pmc *pm)
|
||||
KASSERT(ri >= 0 && ri < P6_NPMCS,
|
||||
("[p6,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
KASSERT(p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc == NULL,
|
||||
("[p6,%d] PHW pmc non-NULL", __LINE__));
|
||||
|
||||
KASSERT(phw->phw_pmc == NULL,
|
||||
("[p6,%d] PHW pmc %p != pmc %p", __LINE__, phw->phw_pmc, pm));
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -643,7 +604,6 @@ p6_start_pmc(int cpu, int ri)
|
||||
uint32_t config;
|
||||
struct pmc *pm;
|
||||
struct p6_cpu *pc;
|
||||
struct pmc_hw *phw;
|
||||
const struct p6pmc_descr *pd;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
@ -651,10 +611,9 @@ p6_start_pmc(int cpu, int ri)
|
||||
KASSERT(ri >= 0 && ri < P6_NPMCS,
|
||||
("[p6,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
pc = (struct p6_cpu *) pmc_pcpu[cpu];
|
||||
phw = pc->pc_common.pc_hwpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pd = &p6_pmcdesc[ri];
|
||||
pc = p6_pcpu[cpu];
|
||||
pm = pc->pc_p6pmcs[ri].phw_pmc;
|
||||
pd = &p6_pmcdesc[ri];
|
||||
|
||||
KASSERT(pm,
|
||||
("[p6,%d] starting cpu%d,ri%d with no pmc configured",
|
||||
@ -662,13 +621,6 @@ p6_start_pmc(int cpu, int ri)
|
||||
|
||||
PMCDBG(MDP,STA,1, "p6-start cpu=%d ri=%d", cpu, ri);
|
||||
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
|
||||
return 0; /* TSC are always running */
|
||||
|
||||
KASSERT(pd->pm_descr.pd_class == PMC_CLASS_P6,
|
||||
("[p6,%d] unknown PMC class %d", __LINE__,
|
||||
pd->pm_descr.pd_class));
|
||||
|
||||
config = pm->pm_md.pm_ppro.pm_ppro_evsel;
|
||||
|
||||
PMCDBG(MDP,STA,2, "p6-start/2 cpu=%d ri=%d evselmsr=0x%x config=0x%x",
|
||||
@ -679,7 +631,7 @@ p6_start_pmc(int cpu, int ri)
|
||||
|
||||
P6_SYNC_CTR_STATE(pc);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -687,7 +639,6 @@ p6_stop_pmc(int cpu, int ri)
|
||||
{
|
||||
struct pmc *pm;
|
||||
struct p6_cpu *pc;
|
||||
struct pmc_hw *phw;
|
||||
struct p6pmc_descr *pd;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
@ -695,22 +646,14 @@ p6_stop_pmc(int cpu, int ri)
|
||||
KASSERT(ri >= 0 && ri < P6_NPMCS,
|
||||
("[p6,%d] illegal row index %d", __LINE__, ri));
|
||||
|
||||
pc = (struct p6_cpu *) pmc_pcpu[cpu];
|
||||
phw = pc->pc_common.pc_hwpmcs[ri];
|
||||
pm = phw->phw_pmc;
|
||||
pd = &p6_pmcdesc[ri];
|
||||
pc = p6_pcpu[cpu];
|
||||
pm = pc->pc_p6pmcs[ri].phw_pmc;
|
||||
pd = &p6_pmcdesc[ri];
|
||||
|
||||
KASSERT(pm,
|
||||
("[p6,%d] cpu%d ri%d no configured PMC to stop", __LINE__,
|
||||
cpu, ri));
|
||||
|
||||
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
|
||||
return 0;
|
||||
|
||||
KASSERT(pd->pm_descr.pd_class == PMC_CLASS_P6,
|
||||
("[p6,%d] unknown PMC class %d", __LINE__,
|
||||
pd->pm_descr.pd_class));
|
||||
|
||||
PMCDBG(MDP,STO,1, "p6-stop cpu=%d ri=%d", cpu, ri);
|
||||
|
||||
wrmsr(pd->pm_evsel_msr, 0); /* stop hw */
|
||||
@ -719,38 +662,35 @@ p6_stop_pmc(int cpu, int ri)
|
||||
P6_SYNC_CTR_STATE(pc); /* restart CTR1 if need be */
|
||||
|
||||
PMCDBG(MDP,STO,2, "p6-stop/2 cpu=%d ri=%d", cpu, ri);
|
||||
return 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
p6_intr(int cpu, struct trapframe *tf)
|
||||
{
|
||||
int i, error, retval, ri;
|
||||
int error, retval, ri;
|
||||
uint32_t perf0cfg;
|
||||
struct pmc *pm;
|
||||
struct p6_cpu *pc;
|
||||
struct pmc_hw *phw;
|
||||
pmc_value_t v;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p6,%d] CPU %d out of range", __LINE__, cpu));
|
||||
|
||||
retval = 0;
|
||||
pc = (struct p6_cpu *) pmc_pcpu[cpu];
|
||||
pc = p6_pcpu[cpu];
|
||||
|
||||
/* stop both PMCs */
|
||||
perf0cfg = rdmsr(P6_MSR_EVSEL0);
|
||||
wrmsr(P6_MSR_EVSEL0, perf0cfg & ~P6_EVSEL_EN);
|
||||
|
||||
for (i = 0; i < P6_NPMCS-1; i++) {
|
||||
ri = i + 1;
|
||||
for (ri = 0; ri < P6_NPMCS; ri++) {
|
||||
|
||||
if (!P6_PMC_HAS_OVERFLOWED(i))
|
||||
if (!P6_PMC_HAS_OVERFLOWED(ri))
|
||||
continue;
|
||||
|
||||
phw = pc->pc_common.pc_hwpmcs[ri];
|
||||
|
||||
if ((pm = phw->phw_pmc) == NULL ||
|
||||
if ((pm = pc->pc_p6pmcs[ri].phw_pmc) == NULL ||
|
||||
pm->pm_state != PMC_STATE_RUNNING ||
|
||||
!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
|
||||
continue;
|
||||
@ -765,7 +705,7 @@ p6_intr(int cpu, struct trapframe *tf)
|
||||
|
||||
/* reload sampling count */
|
||||
v = pm->pm_sc.pm_reloadcount;
|
||||
wrmsr(P6_MSR_PERFCTR0 + i,
|
||||
wrmsr(P6_MSR_PERFCTR0 + ri,
|
||||
P6_RELOAD_COUNT_TO_PERFCTR_VALUE(v));
|
||||
|
||||
}
|
||||
@ -783,7 +723,7 @@ p6_intr(int cpu, struct trapframe *tf)
|
||||
/* restart counters that can be restarted */
|
||||
P6_SYNC_CTR_STATE(pc);
|
||||
|
||||
return retval;
|
||||
return (retval);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -795,12 +735,20 @@ p6_describe(int cpu, int ri, struct pmc_info *pi,
|
||||
struct pmc_hw *phw;
|
||||
struct p6pmc_descr *pd;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[p6,%d] illegal CPU %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < P6_NPMCS,
|
||||
("[p6,%d] row-index %d out of range", __LINE__, ri));
|
||||
|
||||
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
||||
pd = &p6_pmcdesc[ri];
|
||||
|
||||
KASSERT(phw == &p6_pcpu[cpu]->pc_p6pmcs[ri],
|
||||
("[p6,%d] phw mismatch", __LINE__));
|
||||
|
||||
if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name,
|
||||
PMC_NAME_MAX, &copied)) != 0)
|
||||
return error;
|
||||
return (error);
|
||||
|
||||
pi->pm_class = pd->pm_descr.pd_class;
|
||||
|
||||
@ -812,7 +760,7 @@ p6_describe(int cpu, int ri, struct pmc_info *pi,
|
||||
*ppmc = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -822,58 +770,91 @@ p6_get_msr(int ri, uint32_t *msr)
|
||||
("[p6,%d ri %d out of range", __LINE__, ri));
|
||||
|
||||
*msr = p6_pmcdesc[ri].pm_pmc_msr - P6_MSR_PERFCTR0;
|
||||
return 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pmc_initialize_p6(struct pmc_mdep *pmc_mdep)
|
||||
pmc_p6_initialize(struct pmc_mdep *md, int ncpus)
|
||||
{
|
||||
struct pmc_classdep *pcd;
|
||||
|
||||
KASSERT(strcmp(cpu_vendor, "GenuineIntel") == 0,
|
||||
("[p6,%d] Initializing non-intel processor", __LINE__));
|
||||
|
||||
PMCDBG(MDP,INI,1, "%s", "p6-initialize");
|
||||
|
||||
switch (pmc_mdep->pmd_cputype) {
|
||||
/* Allocate space for pointers to per-cpu descriptors. */
|
||||
p6_pcpu = malloc(sizeof(struct p6_cpu **) * ncpus, M_PMC,
|
||||
M_ZERO|M_WAITOK);
|
||||
|
||||
/* Fill in the class dependent descriptor. */
|
||||
pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6];
|
||||
|
||||
switch (md->pmd_cputype) {
|
||||
|
||||
/*
|
||||
* P6 Family Processors
|
||||
*/
|
||||
|
||||
case PMC_CPU_INTEL_P6:
|
||||
case PMC_CPU_INTEL_CL:
|
||||
case PMC_CPU_INTEL_PII:
|
||||
case PMC_CPU_INTEL_PIII:
|
||||
case PMC_CPU_INTEL_PM:
|
||||
|
||||
p6_cputype = pmc_mdep->pmd_cputype;
|
||||
p6_cputype = md->pmd_cputype;
|
||||
|
||||
pmc_mdep->pmd_npmc = P6_NPMCS;
|
||||
pmc_mdep->pmd_classes[1].pm_class = PMC_CLASS_P6;
|
||||
pmc_mdep->pmd_classes[1].pm_caps = P6_PMC_CAPS;
|
||||
pmc_mdep->pmd_classes[1].pm_width = 40;
|
||||
pmc_mdep->pmd_nclasspmcs[1] = 2;
|
||||
pcd->pcd_caps = P6_PMC_CAPS;
|
||||
pcd->pcd_class = PMC_CLASS_P6;
|
||||
pcd->pcd_num = P6_NPMCS;
|
||||
pcd->pcd_ri = md->pmd_npmc;
|
||||
pcd->pcd_width = 40;
|
||||
|
||||
pmc_mdep->pmd_init = p6_init;
|
||||
pmc_mdep->pmd_cleanup = p6_cleanup;
|
||||
pmc_mdep->pmd_switch_in = p6_switch_in;
|
||||
pmc_mdep->pmd_switch_out = p6_switch_out;
|
||||
pmc_mdep->pmd_read_pmc = p6_read_pmc;
|
||||
pmc_mdep->pmd_write_pmc = p6_write_pmc;
|
||||
pmc_mdep->pmd_config_pmc = p6_config_pmc;
|
||||
pmc_mdep->pmd_get_config = p6_get_config;
|
||||
pmc_mdep->pmd_allocate_pmc = p6_allocate_pmc;
|
||||
pmc_mdep->pmd_release_pmc = p6_release_pmc;
|
||||
pmc_mdep->pmd_start_pmc = p6_start_pmc;
|
||||
pmc_mdep->pmd_stop_pmc = p6_stop_pmc;
|
||||
pmc_mdep->pmd_intr = p6_intr;
|
||||
pmc_mdep->pmd_describe = p6_describe;
|
||||
pmc_mdep->pmd_get_msr = p6_get_msr; /* i386 */
|
||||
pcd->pcd_allocate_pmc = p6_allocate_pmc;
|
||||
pcd->pcd_config_pmc = p6_config_pmc;
|
||||
pcd->pcd_describe = p6_describe;
|
||||
pcd->pcd_get_config = p6_get_config;
|
||||
pcd->pcd_get_msr = p6_get_msr;
|
||||
pcd->pcd_pcpu_fini = p6_pcpu_fini;
|
||||
pcd->pcd_pcpu_init = p6_pcpu_init;
|
||||
pcd->pcd_read_pmc = p6_read_pmc;
|
||||
pcd->pcd_release_pmc = p6_release_pmc;
|
||||
pcd->pcd_start_pmc = p6_start_pmc;
|
||||
pcd->pcd_stop_pmc = p6_stop_pmc;
|
||||
pcd->pcd_write_pmc = p6_write_pmc;
|
||||
|
||||
md->pmd_pcpu_fini = NULL;
|
||||
md->pmd_pcpu_init = NULL;
|
||||
md->pmd_intr = p6_intr;
|
||||
|
||||
md->pmd_npmc += P6_NPMCS;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
KASSERT(0,("[p6,%d] Unknown CPU type", __LINE__));
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
pmc_p6_finalize(struct pmc_mdep *md)
|
||||
{
|
||||
#if defined(INVARIANTS)
|
||||
int i, ncpus;
|
||||
#endif
|
||||
|
||||
KASSERT(p6_pcpu != NULL, ("[p6,%d] NULL p6_pcpu", __LINE__));
|
||||
|
||||
#if defined(INVARIANTS)
|
||||
ncpus = pmc_cpu_max();
|
||||
for (i = 0; i < ncpus; i++)
|
||||
KASSERT(p6_pcpu[i] == NULL, ("[p6,%d] non-null pcpu %d",
|
||||
__LINE__, i));
|
||||
#endif
|
||||
|
||||
free(p6_pcpu, M_PMC);
|
||||
p6_pcpu = NULL;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
/* Intel PPro, Celeron, P-II, P-III, Pentium-M PMCS */
|
||||
|
||||
#define P6_NPMCS 3 /* 1 TSC + 2 PMCs */
|
||||
#define P6_NPMCS 2 /* 2 PMCs */
|
||||
|
||||
#define P6_EVSEL_CMASK_MASK 0xFF000000
|
||||
#define P6_EVSEL_TO_CMASK(C) (((C) & 0xFF) << 24)
|
||||
@ -77,7 +77,8 @@ struct pmc_md_ppro_pmc {
|
||||
* Prototypes
|
||||
*/
|
||||
|
||||
int pmc_initialize_p6(struct pmc_mdep *); /* Pentium Pro PMCs */
|
||||
int pmc_p6_initialize(struct pmc_mdep *_md, int _ncpus);
|
||||
void pmc_p6_finalize(struct pmc_mdep *_md);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
#endif /* _DEV_HWPMC_PPRO_H_ */
|
||||
|
388
sys/dev/hwpmc/hwpmc_tsc.c
Normal file
388
sys/dev/hwpmc/hwpmc_tsc.c
Normal file
@ -0,0 +1,388 @@
|
||||
/*-
|
||||
* Copyright (c) 2008 Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/pmc.h>
|
||||
#include <sys/pmckern.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <machine/specialreg.h>
|
||||
|
||||
/*
|
||||
* TSC support.
|
||||
*/
|
||||
|
||||
#define TSC_CAPS PMC_CAP_READ
|
||||
|
||||
struct tsc_descr {
|
||||
struct pmc_descr pm_descr; /* "base class" */
|
||||
};
|
||||
|
||||
static struct tsc_descr tsc_pmcdesc[TSC_NPMCS] =
|
||||
{
|
||||
{
|
||||
.pm_descr =
|
||||
{
|
||||
.pd_name = "TSC",
|
||||
.pd_class = PMC_CLASS_TSC,
|
||||
.pd_caps = TSC_CAPS,
|
||||
.pd_width = 64
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-CPU data structure for TSCs.
|
||||
*/
|
||||
|
||||
struct tsc_cpu {
|
||||
struct pmc_hw tc_hw;
|
||||
};
|
||||
|
||||
static struct tsc_cpu **tsc_pcpu;
|
||||
|
||||
static int
|
||||
tsc_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
||||
const struct pmc_op_pmcallocate *a)
|
||||
{
|
||||
(void) cpu;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri >= 0 && ri < TSC_NPMCS,
|
||||
("[tsc,%d] illegal row index %d", __LINE__, ri));
|
||||
|
||||
if (a->pm_class != PMC_CLASS_TSC)
|
||||
return (EINVAL);
|
||||
|
||||
if ((pm->pm_caps & TSC_CAPS) == 0)
|
||||
return (EINVAL);
|
||||
|
||||
if ((pm->pm_caps & ~TSC_CAPS) != 0)
|
||||
return (EPERM);
|
||||
|
||||
if (a->pm_ev != PMC_EV_TSC_TSC ||
|
||||
a->pm_mode != PMC_MODE_SC)
|
||||
return (EINVAL);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_config_pmc(int cpu, int ri, struct pmc *pm)
|
||||
{
|
||||
struct pmc_hw *phw;
|
||||
|
||||
PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
phw = &tsc_pcpu[cpu]->tc_hw;
|
||||
|
||||
KASSERT(pm == NULL || phw->phw_pmc == NULL,
|
||||
("[tsc,%d] pm=%p phw->pm=%p hwpmc not unconfigured", __LINE__,
|
||||
pm, phw->phw_pmc));
|
||||
|
||||
phw->phw_pmc = pm;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
|
||||
{
|
||||
int error;
|
||||
size_t copied;
|
||||
const struct tsc_descr *pd;
|
||||
struct pmc_hw *phw;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal CPU %d", __LINE__, cpu));
|
||||
KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
phw = &tsc_pcpu[cpu]->tc_hw;
|
||||
pd = &tsc_pmcdesc[ri];
|
||||
|
||||
if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name,
|
||||
PMC_NAME_MAX, &copied)) != 0)
|
||||
return (error);
|
||||
|
||||
pi->pm_class = pd->pm_descr.pd_class;
|
||||
|
||||
if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
|
||||
pi->pm_enabled = TRUE;
|
||||
*ppmc = phw->phw_pmc;
|
||||
} else {
|
||||
pi->pm_enabled = FALSE;
|
||||
*ppmc = NULL;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_get_config(int cpu, int ri, struct pmc **ppm)
|
||||
{
|
||||
(void) ri;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal CPU %d", __LINE__, cpu));
|
||||
KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
*ppm = tsc_pcpu[cpu]->tc_hw.phw_pmc;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_get_msr(int ri, uint32_t *msr)
|
||||
{
|
||||
(void) ri;
|
||||
|
||||
KASSERT(ri >= 0 && ri < TSC_NPMCS,
|
||||
("[tsc,%d] ri %d out of range", __LINE__, ri));
|
||||
|
||||
*msr = MSR_TSC;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_pcpu_fini(struct pmc_mdep *md, int cpu)
|
||||
{
|
||||
int ri;
|
||||
struct pmc_cpu *pc;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal cpu %d", __LINE__, cpu));
|
||||
KASSERT(tsc_pcpu[cpu] != NULL, ("[tsc,%d] null pcpu", __LINE__));
|
||||
|
||||
free(tsc_pcpu[cpu], M_PMC);
|
||||
tsc_pcpu[cpu] = NULL;
|
||||
|
||||
ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_ri;
|
||||
|
||||
KASSERT(ri == 0 && ri < TSC_NPMCS, ("[tsc,%d] ri=%d", __LINE__,
|
||||
ri));
|
||||
|
||||
pc = pmc_pcpu[cpu];
|
||||
pc->pc_hwpmcs[ri] = NULL;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_pcpu_init(struct pmc_mdep *md, int cpu)
|
||||
{
|
||||
int ri;
|
||||
struct pmc_cpu *pc;
|
||||
struct tsc_cpu *tsc_pc;
|
||||
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal cpu %d", __LINE__, cpu));
|
||||
KASSERT(tsc_pcpu, ("[tsc,%d] null pcpu", __LINE__));
|
||||
KASSERT(tsc_pcpu[cpu] == NULL, ("[tsc,%d] non-null per-cpu",
|
||||
__LINE__));
|
||||
|
||||
tsc_pc = malloc(sizeof(struct tsc_cpu), M_PMC, M_WAITOK|M_ZERO);
|
||||
|
||||
tsc_pc->tc_hw.phw_state = PMC_PHW_FLAG_IS_ENABLED |
|
||||
PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(0) |
|
||||
PMC_PHW_FLAG_IS_SHAREABLE;
|
||||
|
||||
tsc_pcpu[cpu] = tsc_pc;
|
||||
|
||||
ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_ri;
|
||||
|
||||
KASSERT(pmc_pcpu, ("[tsc,%d] null generic pcpu", __LINE__));
|
||||
|
||||
pc = pmc_pcpu[cpu];
|
||||
|
||||
KASSERT(pc, ("[tsc,%d] null generic per-cpu", __LINE__));
|
||||
|
||||
pc->pc_hwpmcs[ri] = &tsc_pc->tc_hw;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_read_pmc(int cpu, int ri, pmc_value_t *v)
|
||||
{
|
||||
struct pmc *pm;
|
||||
enum pmc_mode mode;
|
||||
const struct pmc_hw *phw;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri == 0, ("[tsc,%d] illegal ri %d", __LINE__, ri));
|
||||
|
||||
phw = &tsc_pcpu[cpu]->tc_hw;
|
||||
pm = phw->phw_pmc;
|
||||
|
||||
KASSERT(pm != NULL,
|
||||
("[tsc,%d] no owner for PHW [cpu%d,pmc%d]", __LINE__, cpu, ri));
|
||||
|
||||
mode = PMC_TO_MODE(pm);
|
||||
|
||||
KASSERT(mode == PMC_MODE_SC,
|
||||
("[tsc,%d] illegal pmc mode %d", __LINE__, mode));
|
||||
|
||||
PMCDBG(MDP,REA,1,"tsc-read id=%d", ri);
|
||||
|
||||
*v = rdtsc();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_release_pmc(int cpu, int ri, struct pmc *pmc)
|
||||
{
|
||||
struct pmc_hw *phw;
|
||||
|
||||
(void) pmc;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri == 0,
|
||||
("[tsc,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
phw = &tsc_pcpu[cpu]->tc_hw;
|
||||
|
||||
KASSERT(phw->phw_pmc == NULL,
|
||||
("[tsc,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
|
||||
|
||||
/*
|
||||
* Nothing to do.
|
||||
*/
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_start_pmc(int cpu, int ri)
|
||||
{
|
||||
(void) cpu;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
return (0); /* TSCs are always running. */
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_stop_pmc(int cpu, int ri)
|
||||
{
|
||||
(void) cpu; (void) ri;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
return (0); /* Cannot actually stop a TSC. */
|
||||
}
|
||||
|
||||
static int
|
||||
tsc_write_pmc(int cpu, int ri, pmc_value_t v)
|
||||
{
|
||||
(void) cpu; (void) ri; (void) v;
|
||||
|
||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
|
||||
("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
|
||||
KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
|
||||
|
||||
/*
|
||||
* The TSCs are used as timecounters by the kernel, so even
|
||||
* though some i386 CPUs support writeable TSCs, we don't
|
||||
* support writing changing TSC values through the HWPMC API.
|
||||
*/
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pmc_tsc_initialize(struct pmc_mdep *md, int maxcpu)
|
||||
{
|
||||
struct pmc_classdep *pcd;
|
||||
|
||||
KASSERT(md != NULL, ("[tsc,%d] md is NULL", __LINE__));
|
||||
KASSERT(md->pmd_nclass >= 1, ("[tsc,%d] dubious md->nclass %d",
|
||||
__LINE__, md->pmd_nclass));
|
||||
|
||||
tsc_pcpu = malloc(sizeof(struct tsc_cpu *) * maxcpu, M_PMC,
|
||||
M_ZERO|M_WAITOK);
|
||||
|
||||
pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC];
|
||||
|
||||
pcd->pcd_caps = PMC_CAP_READ;
|
||||
pcd->pcd_class = PMC_CLASS_TSC;
|
||||
pcd->pcd_num = TSC_NPMCS;
|
||||
pcd->pcd_ri = md->pmd_npmc;
|
||||
pcd->pcd_width = 64;
|
||||
|
||||
pcd->pcd_allocate_pmc = tsc_allocate_pmc;
|
||||
pcd->pcd_config_pmc = tsc_config_pmc;
|
||||
pcd->pcd_describe = tsc_describe;
|
||||
pcd->pcd_get_config = tsc_get_config;
|
||||
pcd->pcd_get_msr = tsc_get_msr;
|
||||
pcd->pcd_pcpu_init = tsc_pcpu_init;
|
||||
pcd->pcd_pcpu_fini = tsc_pcpu_fini;
|
||||
pcd->pcd_read_pmc = tsc_read_pmc;
|
||||
pcd->pcd_release_pmc = tsc_release_pmc;
|
||||
pcd->pcd_start_pmc = tsc_start_pmc;
|
||||
pcd->pcd_stop_pmc = tsc_stop_pmc;
|
||||
pcd->pcd_write_pmc = tsc_write_pmc;
|
||||
|
||||
md->pmd_npmc += TSC_NPMCS;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
pmc_tsc_finalize(struct pmc_mdep *md)
|
||||
{
|
||||
#ifdef INVARIANTS
|
||||
int i, ncpus;
|
||||
|
||||
ncpus = pmc_cpu_max();
|
||||
for (i = 0; i < ncpus; i++)
|
||||
KASSERT(tsc_pcpu[i] == NULL, ("[tsc,%d] non-null pcpu cpu %d",
|
||||
__LINE__, i));
|
||||
|
||||
KASSERT(md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_class ==
|
||||
PMC_CLASS_TSC, ("[tsc,%d] class mismatch", __LINE__));
|
||||
|
||||
#else
|
||||
(void) md;
|
||||
#endif
|
||||
|
||||
free(tsc_pcpu, M_PMC);
|
||||
tsc_pcpu = NULL;
|
||||
}
|
43
sys/dev/hwpmc/hwpmc_tsc.h
Normal file
43
sys/dev/hwpmc/hwpmc_tsc.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*-
|
||||
* Copyright (c) 2008 Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _DEV_HWPMC_TSC_H_
|
||||
#define _DEV_HWPMC_TSC_H_ 1
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
#define TSC_NPMCS 1
|
||||
|
||||
/*
|
||||
* Prototypes.
|
||||
*/
|
||||
|
||||
int pmc_tsc_initialize(struct pmc_mdep *_md, int _maxcpu);
|
||||
void pmc_tsc_finalize(struct pmc_mdep *_md);
|
||||
#endif /* _KERNEL */
|
||||
#endif /* _DEV_HWPMC_TSC_H */
|
@ -240,120 +240,6 @@ pmc_save_kernel_callchain(uintptr_t *cc, int nframes, struct trapframe *tf)
|
||||
return (n);
|
||||
}
|
||||
|
||||
static struct pmc_mdep *
|
||||
pmc_intel_initialize(void)
|
||||
{
|
||||
struct pmc_mdep *pmc_mdep;
|
||||
enum pmc_cputype cputype;
|
||||
int error, model;
|
||||
|
||||
KASSERT(strcmp(cpu_vendor, "GenuineIntel") == 0,
|
||||
("[intel,%d] Initializing non-intel processor", __LINE__));
|
||||
|
||||
PMCDBG(MDP,INI,0, "intel-initialize cpuid=0x%x", cpu_id);
|
||||
|
||||
cputype = -1;
|
||||
|
||||
switch (cpu_id & 0xF00) {
|
||||
#if defined(__i386__)
|
||||
case 0x500: /* Pentium family processors */
|
||||
cputype = PMC_CPU_INTEL_P5;
|
||||
break;
|
||||
case 0x600: /* Pentium Pro, Celeron, Pentium II & III */
|
||||
switch ((cpu_id & 0xF0) >> 4) { /* model number field */
|
||||
case 0x1:
|
||||
cputype = PMC_CPU_INTEL_P6;
|
||||
break;
|
||||
case 0x3: case 0x5:
|
||||
cputype = PMC_CPU_INTEL_PII;
|
||||
break;
|
||||
case 0x6:
|
||||
cputype = PMC_CPU_INTEL_CL;
|
||||
break;
|
||||
case 0x7: case 0x8: case 0xA: case 0xB:
|
||||
cputype = PMC_CPU_INTEL_PIII;
|
||||
break;
|
||||
case 0x9: case 0xD:
|
||||
cputype = PMC_CPU_INTEL_PM;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
case 0xF00: /* P4 */
|
||||
model = ((cpu_id & 0xF0000) >> 12) | ((cpu_id & 0xF0) >> 4);
|
||||
if (model >= 0 && model <= 6) /* known models */
|
||||
cputype = PMC_CPU_INTEL_PIV;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((int) cputype == -1) {
|
||||
printf("pmc: Unknown Intel CPU.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pmc_mdep = malloc(sizeof(struct pmc_mdep),
|
||||
M_PMC, M_WAITOK|M_ZERO);
|
||||
|
||||
pmc_mdep->pmd_cputype = cputype;
|
||||
pmc_mdep->pmd_nclass = 2;
|
||||
pmc_mdep->pmd_classes[0].pm_class = PMC_CLASS_TSC;
|
||||
pmc_mdep->pmd_classes[0].pm_caps = PMC_CAP_READ;
|
||||
pmc_mdep->pmd_classes[0].pm_width = 64;
|
||||
pmc_mdep->pmd_nclasspmcs[0] = 1;
|
||||
|
||||
error = 0;
|
||||
|
||||
switch (cputype) {
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
|
||||
/*
|
||||
* Intel Pentium 4 Processors, and P4/EMT64 processors.
|
||||
*/
|
||||
|
||||
case PMC_CPU_INTEL_PIV:
|
||||
error = pmc_initialize_p4(pmc_mdep);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(__i386__)
|
||||
/*
|
||||
* P6 Family Processors
|
||||
*/
|
||||
|
||||
case PMC_CPU_INTEL_P6:
|
||||
case PMC_CPU_INTEL_CL:
|
||||
case PMC_CPU_INTEL_PII:
|
||||
case PMC_CPU_INTEL_PIII:
|
||||
case PMC_CPU_INTEL_PM:
|
||||
|
||||
error = pmc_initialize_p6(pmc_mdep);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Intel Pentium PMCs.
|
||||
*/
|
||||
|
||||
case PMC_CPU_INTEL_P5:
|
||||
error = pmc_initialize_p5(pmc_mdep);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
KASSERT(0,("[intel,%d] Unknown CPU type", __LINE__));
|
||||
}
|
||||
|
||||
if (error) {
|
||||
free(pmc_mdep, M_PMC);
|
||||
pmc_mdep = NULL;
|
||||
}
|
||||
|
||||
return pmc_mdep;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Machine dependent initialization for x86 class platforms.
|
||||
*/
|
||||
@ -370,11 +256,24 @@ pmc_md_initialize()
|
||||
md = pmc_amd_initialize();
|
||||
else if (strcmp(cpu_vendor, "GenuineIntel") == 0)
|
||||
md = pmc_intel_initialize();
|
||||
else
|
||||
KASSERT(0, ("[x86,%d] Unknown vendor", __LINE__));
|
||||
|
||||
/* disallow sampling if we do not have an LAPIC */
|
||||
if (md != NULL && lapic == NULL)
|
||||
for (i = 1; i < md->pmd_nclass; i++)
|
||||
md->pmd_classes[i].pm_caps &= ~PMC_CAP_INTERRUPT;
|
||||
md->pmd_classdep[i].pcd_caps &= ~PMC_CAP_INTERRUPT;
|
||||
|
||||
return md;
|
||||
return (md);
|
||||
}
|
||||
|
||||
void
|
||||
pmc_md_finalize(struct pmc_mdep *md)
|
||||
{
|
||||
if (strcmp(cpu_vendor, "AuthenticAMD") == 0)
|
||||
pmc_amd_finalize(md);
|
||||
else if (strcmp(cpu_vendor, "GenuineIntel") == 0)
|
||||
pmc_intel_finalize(md);
|
||||
else
|
||||
KASSERT(0, ("[x86,%d] Unknown vendor", __LINE__));
|
||||
}
|
||||
|
@ -33,21 +33,43 @@
|
||||
#ifndef _MACHINE_PMC_MDEP_H
|
||||
#define _MACHINE_PMC_MDEP_H 1
|
||||
|
||||
#ifdef _KERNEL
|
||||
struct pmc_mdep;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* On the i386 platform we support the following PMCs.
|
||||
*
|
||||
* TSC The timestamp counter
|
||||
* K7 AMD Athlon XP/MP and other 32 bit processors.
|
||||
* K8 AMD Athlon64 and Opteron PMCs in 32 bit mode.
|
||||
* PIV Intel P4/HTT and P4/EMT64
|
||||
* PPRO Intel Pentium Pro, Pentium-II, Pentium-III, Celeron and
|
||||
* Pentium-M processors
|
||||
* PENTIUM Intel Pentium MMX.
|
||||
* IAP Intel Core/Core2/Atom programmable PMCs.
|
||||
* IAF Intel fixed-function PMCs.
|
||||
*/
|
||||
|
||||
#include <dev/hwpmc/hwpmc_amd.h> /* K7 and K8 */
|
||||
#include <dev/hwpmc/hwpmc_piv.h>
|
||||
#include <dev/hwpmc/hwpmc_ppro.h>
|
||||
#include <dev/hwpmc/hwpmc_pentium.h>
|
||||
#include <dev/hwpmc/hwpmc_tsc.h>
|
||||
|
||||
/*
|
||||
* Intel processors implementing V2 and later of the Intel performance
|
||||
* measurement architecture have PMCs of the following classes: TSC,
|
||||
* IAF and IAP.
|
||||
*/
|
||||
#define PMC_MDEP_CLASS_INDEX_TSC 0
|
||||
#define PMC_MDEP_CLASS_INDEX_K7 1
|
||||
#define PMC_MDEP_CLASS_INDEX_K8 1
|
||||
#define PMC_MDEP_CLASS_INDEX_P4 1
|
||||
#define PMC_MDEP_CLASS_INDEX_P5 1
|
||||
#define PMC_MDEP_CLASS_INDEX_P6 1
|
||||
#define PMC_MDEP_CLASS_INDEX_IAF 1
|
||||
#define PMC_MDEP_CLASS_INDEX_IAP 2
|
||||
|
||||
/*
|
||||
* Architecture specific extensions to <sys/pmc.h> structures.
|
||||
@ -76,6 +98,7 @@ union pmc_md_pmc {
|
||||
};
|
||||
|
||||
struct pmc;
|
||||
struct pmc_mdep;
|
||||
|
||||
#define PMC_TRAPFRAME_TO_PC(TF) ((TF)->tf_eip)
|
||||
#define PMC_TRAPFRAME_TO_FP(TF) ((TF)->tf_ebp)
|
||||
@ -124,5 +147,10 @@ struct pmc;
|
||||
void start_exceptions(void), end_exceptions(void);
|
||||
void pmc_x86_lapic_enable_pmc_interrupt(void);
|
||||
|
||||
struct pmc_mdep *pmc_amd_initialize(void);
|
||||
void pmc_amd_finalize(struct pmc_mdep *_md);
|
||||
struct pmc_mdep *pmc_intel_initialize(void);
|
||||
void pmc_intel_finalize(struct pmc_mdep *_md);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
#endif /* _MACHINE_PMC_MDEP_H */
|
||||
|
@ -9,7 +9,7 @@ KMOD= hwpmc
|
||||
SRCS= hwpmc_mod.c hwpmc_logging.c vnode_if.h
|
||||
|
||||
.if ${MACHINE_ARCH} == "amd64"
|
||||
SRCS+= hwpmc_amd.c hwpmc_piv.c hwpmc_x86.c
|
||||
SRCS+= hwpmc_amd.c hwpmc_intel.c hwpmc_piv.c hwpmc_tsc.c hwpmc_x86.c
|
||||
SRCS+= device_if.h bus_if.h
|
||||
.endif
|
||||
|
||||
@ -18,7 +18,8 @@ SRCS+= hwpmc_arm.c
|
||||
.endif
|
||||
|
||||
.if ${MACHINE_ARCH} == "i386"
|
||||
SRCS+= hwpmc_amd.c hwpmc_piv.c hwpmc_ppro.c hwpmc_pentium.c hwpmc_x86.c
|
||||
SRCS+= hwpmc_amd.c hwpmc_intel.c hwpmc_piv.c hwpmc_ppro.c hwpmc_pentium.c
|
||||
SRCS+= hwpmc_tsc.c hwpmc_x86.c
|
||||
SRCS+= device_if.h bus_if.h
|
||||
.endif
|
||||
|
||||
|
@ -40,7 +40,7 @@
|
||||
|
||||
#define PMC_MODULE_NAME "hwpmc"
|
||||
#define PMC_NAME_MAX 16 /* HW counter name size */
|
||||
#define PMC_CLASS_MAX 4 /* #classes of PMCs in a system */
|
||||
#define PMC_CLASS_MAX 4 /* max #classes of PMCs per-system */
|
||||
|
||||
/*
|
||||
* Kernel<->userland API version number [MMmmpppp]
|
||||
@ -472,9 +472,10 @@ struct pmc_op_getpmcinfo {
|
||||
*/
|
||||
|
||||
struct pmc_classinfo {
|
||||
enum pmc_class pm_class; /* class id */
|
||||
enum pmc_class pm_class; /* class id */
|
||||
uint32_t pm_caps; /* counter capabilities */
|
||||
uint32_t pm_width; /* width of the PMC */
|
||||
uint32_t pm_num; /* number of PMCs in class */
|
||||
};
|
||||
|
||||
struct pmc_op_getcpuinfo {
|
||||
@ -634,7 +635,7 @@ struct pmc_target {
|
||||
|
||||
struct pmc {
|
||||
LIST_HEAD(,pmc_target) pm_targets; /* list of target processes */
|
||||
LIST_ENTRY(pmc) pm_next; /* owner's list */
|
||||
LIST_ENTRY(pmc) pm_next; /* owner's list */
|
||||
|
||||
/*
|
||||
* System-wide PMCs are allocated on a CPU and are not moved
|
||||
@ -678,7 +679,7 @@ struct pmc {
|
||||
* mode, class and the CPU# associated with the PMC.
|
||||
*/
|
||||
|
||||
pmc_id_t pm_id; /* allocated PMC id */
|
||||
pmc_id_t pm_id; /* allocated PMC id */
|
||||
|
||||
/* md extensions */
|
||||
union pmc_md_pmc pm_md;
|
||||
@ -721,7 +722,7 @@ struct pmc_targetstate {
|
||||
struct pmc_process {
|
||||
LIST_ENTRY(pmc_process) pp_next; /* hash chain */
|
||||
int pp_refcnt; /* reference count */
|
||||
uint32_t pp_flags; /* flags PMC_PP_* */
|
||||
uint32_t pp_flags; /* flags PMC_PP_* */
|
||||
struct proc *pp_proc; /* target thread */
|
||||
struct pmc_targetstate pp_pmcs[]; /* NHWPMCs */
|
||||
};
|
||||
@ -839,7 +840,6 @@ struct pmc_cpu {
|
||||
uint32_t pc_state; /* physical cpu number + flags */
|
||||
struct pmc_samplebuffer *pc_sb; /* space for samples */
|
||||
struct pmc_hw *pc_hwpmcs[]; /* 'npmc' pointers */
|
||||
/* other machine dependent fields come here */
|
||||
};
|
||||
|
||||
#define PMC_PCPU_CPU_MASK 0x000000FF
|
||||
@ -862,6 +862,48 @@ struct pmc_binding {
|
||||
int pb_cpu; /* if so, to which CPU */
|
||||
};
|
||||
|
||||
|
||||
struct pmc_mdep;
|
||||
|
||||
/*
|
||||
* struct pmc_classdep
|
||||
*
|
||||
* PMC class-dependent operations.
|
||||
*/
|
||||
struct pmc_classdep {
|
||||
uint32_t pcd_caps; /* class capabilities */
|
||||
enum pmc_class pcd_class; /* class id */
|
||||
int pcd_num; /* number of PMCs */
|
||||
int pcd_ri; /* row index of the first PMC in class */
|
||||
int pcd_width; /* width of the PMC */
|
||||
|
||||
/* configuring/reading/writing the hardware PMCs */
|
||||
int (*pcd_config_pmc)(int _cpu, int _ri, struct pmc *_pm);
|
||||
int (*pcd_get_config)(int _cpu, int _ri, struct pmc **_ppm);
|
||||
int (*pcd_read_pmc)(int _cpu, int _ri, pmc_value_t *_value);
|
||||
int (*pcd_write_pmc)(int _cpu, int _ri, pmc_value_t _value);
|
||||
|
||||
/* pmc allocation/release */
|
||||
int (*pcd_allocate_pmc)(int _cpu, int _ri, struct pmc *_t,
|
||||
const struct pmc_op_pmcallocate *_a);
|
||||
int (*pcd_release_pmc)(int _cpu, int _ri, struct pmc *_pm);
|
||||
|
||||
/* starting and stopping PMCs */
|
||||
int (*pcd_start_pmc)(int _cpu, int _ri);
|
||||
int (*pcd_stop_pmc)(int _cpu, int _ri);
|
||||
|
||||
/* description */
|
||||
int (*pcd_describe)(int _cpu, int _ri, struct pmc_info *_pi,
|
||||
struct pmc **_ppmc);
|
||||
|
||||
/* class-dependent initialization & finalization */
|
||||
int (*pcd_pcpu_init)(struct pmc_mdep *_md, int _cpu);
|
||||
int (*pcd_pcpu_fini)(struct pmc_mdep *_md, int _cpu);
|
||||
|
||||
/* machine-specific interface */
|
||||
int (*pcd_get_msr)(int _ri, uint32_t *_msr);
|
||||
};
|
||||
|
||||
/*
|
||||
* struct pmc_mdep
|
||||
*
|
||||
@ -870,45 +912,28 @@ struct pmc_binding {
|
||||
|
||||
struct pmc_mdep {
|
||||
uint32_t pmd_cputype; /* from enum pmc_cputype */
|
||||
uint32_t pmd_npmc; /* max PMCs per CPU */
|
||||
uint32_t pmd_nclass; /* # PMC classes supported */
|
||||
struct pmc_classinfo pmd_classes[PMC_CLASS_MAX];
|
||||
int pmd_nclasspmcs[PMC_CLASS_MAX];
|
||||
uint32_t pmd_npmc; /* number of PMCs per CPU */
|
||||
uint32_t pmd_nclass; /* number of PMC classes present */
|
||||
|
||||
/*
|
||||
* Methods
|
||||
* Machine dependent methods.
|
||||
*/
|
||||
|
||||
int (*pmd_init)(int _cpu); /* machine dependent initialization */
|
||||
int (*pmd_cleanup)(int _cpu); /* machine dependent cleanup */
|
||||
/* per-cpu initialization and finalization */
|
||||
int (*pmd_pcpu_init)(int _cpu); /* initialization */
|
||||
int (*pmd_pcpu_fini)(int _cpu); /* finalization */
|
||||
|
||||
/* thread context switch in/out */
|
||||
int (*pmd_switch_in)(struct pmc_cpu *_p, struct pmc_process *_pp);
|
||||
int (*pmd_switch_out)(struct pmc_cpu *_p, struct pmc_process *_pp);
|
||||
|
||||
/* configuring/reading/writing the hardware PMCs */
|
||||
int (*pmd_config_pmc)(int _cpu, int _ri, struct pmc *_pm);
|
||||
int (*pmd_get_config)(int _cpu, int _ri, struct pmc **_ppm);
|
||||
int (*pmd_read_pmc)(int _cpu, int _ri, pmc_value_t *_value);
|
||||
int (*pmd_write_pmc)(int _cpu, int _ri, pmc_value_t _value);
|
||||
|
||||
/* pmc allocation/release */
|
||||
int (*pmd_allocate_pmc)(int _cpu, int _ri, struct pmc *_t,
|
||||
const struct pmc_op_pmcallocate *_a);
|
||||
int (*pmd_release_pmc)(int _cpu, int _ri, struct pmc *_pm);
|
||||
|
||||
/* starting and stopping PMCs */
|
||||
int (*pmd_start_pmc)(int _cpu, int _ri);
|
||||
int (*pmd_stop_pmc)(int _cpu, int _ri);
|
||||
|
||||
/* handle a PMC interrupt */
|
||||
int (*pmd_intr)(int _cpu, struct trapframe *_tf);
|
||||
|
||||
int (*pmd_describe)(int _cpu, int _ri, struct pmc_info *_pi,
|
||||
struct pmc **_ppmc);
|
||||
|
||||
int (*pmd_get_msr)(int _ri, uint32_t *_msr);
|
||||
|
||||
/*
|
||||
* PMC class dependent information.
|
||||
*/
|
||||
struct pmc_classdep pmd_classdep[];
|
||||
};
|
||||
|
||||
/*
|
||||
@ -968,7 +993,7 @@ extern struct pmc_debugflags pmc_debugflags;
|
||||
#define PMC_DEBUG_MIN_FND 4 /* find */
|
||||
|
||||
/* MODULE */
|
||||
#define PMC_DEBUG_MIN_PMH 14 /* pmc_hook */
|
||||
#define PMC_DEBUG_MIN_PMH 14 /* pmc_hook */
|
||||
#define PMC_DEBUG_MIN_PMS 15 /* pmc_syscall */
|
||||
|
||||
/* OWN */
|
||||
@ -1001,7 +1026,7 @@ extern struct pmc_debugflags pmc_debugflags;
|
||||
#define PMC_DEBUG_MIN_INT 13 /* interrupts */
|
||||
|
||||
/* CPU */
|
||||
#define PMC_DEBUG_MIN_BND 8 /* bind */
|
||||
#define PMC_DEBUG_MIN_BND 8 /* bind */
|
||||
#define PMC_DEBUG_MIN_SEL 9 /* select */
|
||||
|
||||
/* LOG */
|
||||
@ -1022,6 +1047,7 @@ MALLOC_DECLARE(M_PMC);
|
||||
*/
|
||||
|
||||
struct pmc_mdep *pmc_md_initialize(void); /* MD init function */
|
||||
void pmc_md_finalize(struct pmc_mdep *_md); /* MD fini function */
|
||||
int pmc_getrowdisp(int _ri);
|
||||
int pmc_process_interrupt(int _cpu, struct pmc *_pm,
|
||||
struct trapframe *_tf, int _inuserspace);
|
||||
|
Loading…
Reference in New Issue
Block a user