c5153e190b
Have pmcstat(8) and pmccontrol(8) use these APIs. Return PMC class-related constants (PMC widths and capabilities) with the OP GETCPUINFO call leaving OP PMCINFO to return only the dynamic information associated with a PMC (i.e., whether enabled, owner pid, reload count etc.). Allow pmc_read() (i.e., OPS PMCRW) on active self-attached PMCs to get upto-date values from hardware since we can guarantee that the hardware is running the correct PMC at the time of the call. Bug fixes: - (x86 class processors) Fix a bug that prevented an RDPMC instruction from being recognized as permitted till after the attached process had context switched out and back in again after a pmc_start() call. Tighten the rules for using RDPMC class instructions: a GETMSR OP is now allowed only after an OP ATTACH has been done by the PMC's owner to itself. OP GETMSR is not allowed for PMCs that track descendants, for PMCs attached to processes other than their owner processes. - (P4/HTT processors only) Fix a bug that caused the MI and MD layers to get out of sync. Add a new MD operation 'get_config()' as part of this fix. - Allow multiple system-mode PMCs at the same row-index but on different CPUs to be allocated. - Reject allocation of an administratively disabled PMC. Misc. code cleanups and refactoring. Improve a few comments.
1029 lines
27 KiB
C
1029 lines
27 KiB
C
/*-
|
|
* Copyright (c) 2003-2005 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$");
|
|
|
|
/* Support for the AMD K7 and later processors */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/pmc.h>
|
|
#include <sys/smp.h>
|
|
#include <sys/systm.h>
|
|
|
|
#include <machine/md_var.h>
|
|
|
|
/* AMD K7 and K8 PMCs */
|
|
|
|
#define AMD_PMC_EVSEL_0 0xC0010000
|
|
#define AMD_PMC_EVSEL_1 0xC0010001
|
|
#define AMD_PMC_EVSEL_2 0xC0010002
|
|
#define AMD_PMC_EVSEL_3 0xC0010003
|
|
|
|
#define AMD_PMC_PERFCTR_0 0xC0010004
|
|
#define AMD_PMC_PERFCTR_1 0xC0010005
|
|
#define AMD_PMC_PERFCTR_2 0xC0010006
|
|
#define AMD_PMC_PERFCTR_3 0xC0010007
|
|
|
|
#define K7_VALID_EVENT_CODE(c) (((c) >= 0x40 && (c) <= 0x47) || \
|
|
((c) >= 0x80 && (c) <= 0x85) || ((c) >= 0xC0 && (c) <= 0xC7) || \
|
|
((c) >= 0xCD && (c) <= 0xCF))
|
|
|
|
#define AMD_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)
|
|
|
|
/* reserved bits include bit 21 and the top two bits of the unit mask */
|
|
#define K7_PMC_RESERVED ((1 << 21) | (3 << 13))
|
|
|
|
#define K8_PMC_RESERVED (1 << 21)
|
|
|
|
#define AMD_PMC_IS_STOPPED(evsel) ((rdmsr((evsel)) & AMD_PMC_ENABLE) == 0)
|
|
#define AMD_PMC_HAS_OVERFLOWED(pmc) ((rdpmc(pmc) & (1ULL << 47)) == 0)
|
|
|
|
#if __i386__
|
|
#define AMD_NPMCS K7_NPMCS
|
|
#define AMD_PMC_CLASS PMC_CLASS_K7
|
|
#define AMD_PMC_COUNTERMASK K7_PMC_COUNTERMASK
|
|
#define AMD_PMC_TO_COUNTER(x) K7_PMC_TO_COUNTER(x)
|
|
#define AMD_PMC_INVERT K7_PMC_INVERT
|
|
#define AMD_PMC_ENABLE K7_PMC_ENABLE
|
|
#define AMD_PMC_INT K7_PMC_INT
|
|
#define AMD_PMC_PC K7_PMC_PC
|
|
#define AMD_PMC_EDGE K7_PMC_EDGE
|
|
#define AMD_PMC_OS K7_PMC_OS
|
|
#define AMD_PMC_USR K7_PMC_USR
|
|
|
|
#define AMD_PMC_UNITMASK_M K7_PMC_UNITMASK_M
|
|
#define AMD_PMC_UNITMASK_O K7_PMC_UNITMASK_O
|
|
#define AMD_PMC_UNITMASK_E K7_PMC_UNITMASK_E
|
|
#define AMD_PMC_UNITMASK_S K7_PMC_UNITMASK_S
|
|
#define AMD_PMC_UNITMASK_I K7_PMC_UNITMASK_I
|
|
|
|
#define AMD_PMC_UNITMASK K7_PMC_UNITMASK
|
|
#define AMD_PMC_EVENTMASK K7_PMC_EVENTMASK
|
|
#define AMD_PMC_TO_UNITMASK(x) K7_PMC_TO_UNITMASK(x)
|
|
#define AMD_PMC_TO_EVENTMASK(x) K7_PMC_TO_EVENTMASK(x)
|
|
#define AMD_VALID_BITS K7_VALID_BITS
|
|
|
|
#define AMD_PMC_CLASS_NAME "K7-"
|
|
|
|
#elif __amd64__
|
|
|
|
#define AMD_NPMCS K8_NPMCS
|
|
#define AMD_PMC_CLASS PMC_CLASS_K8
|
|
#define AMD_PMC_COUNTERMASK K8_PMC_COUNTERMASK
|
|
#define AMD_PMC_TO_COUNTER(x) K8_PMC_TO_COUNTER(x)
|
|
#define AMD_PMC_INVERT K8_PMC_INVERT
|
|
#define AMD_PMC_ENABLE K8_PMC_ENABLE
|
|
#define AMD_PMC_INT K8_PMC_INT
|
|
#define AMD_PMC_PC K8_PMC_PC
|
|
#define AMD_PMC_EDGE K8_PMC_EDGE
|
|
#define AMD_PMC_OS K8_PMC_OS
|
|
#define AMD_PMC_USR K8_PMC_USR
|
|
|
|
#define AMD_PMC_UNITMASK_M K8_PMC_UNITMASK_M
|
|
#define AMD_PMC_UNITMASK_O K8_PMC_UNITMASK_O
|
|
#define AMD_PMC_UNITMASK_E K8_PMC_UNITMASK_E
|
|
#define AMD_PMC_UNITMASK_S K8_PMC_UNITMASK_S
|
|
#define AMD_PMC_UNITMASK_I K8_PMC_UNITMASK_I
|
|
|
|
#define AMD_PMC_UNITMASK K8_PMC_UNITMASK
|
|
#define AMD_PMC_EVENTMASK K8_PMC_EVENTMASK
|
|
#define AMD_PMC_TO_UNITMASK(x) K8_PMC_TO_UNITMASK(x)
|
|
#define AMD_PMC_TO_EVENTMASK(x) K8_PMC_TO_EVENTMASK(x)
|
|
#define AMD_VALID_BITS K8_VALID_BITS
|
|
|
|
#define AMD_PMC_CLASS_NAME "K8-"
|
|
|
|
#else
|
|
#error Unsupported architecture.
|
|
#endif
|
|
|
|
/* AMD K7 & K8 PMCs */
|
|
struct amd_descr {
|
|
struct pmc_descr pm_descr; /* "base class" */
|
|
uint32_t pm_evsel; /* address of EVSEL register */
|
|
uint32_t pm_perfctr; /* address of PERFCTR register */
|
|
};
|
|
|
|
static const 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 =
|
|
{
|
|
.pd_name = AMD_PMC_CLASS_NAME "0",
|
|
.pd_class = AMD_PMC_CLASS,
|
|
.pd_caps = AMD_PMC_CAPS,
|
|
.pd_width = 48
|
|
},
|
|
.pm_evsel = AMD_PMC_EVSEL_0,
|
|
.pm_perfctr = AMD_PMC_PERFCTR_0
|
|
},
|
|
{
|
|
.pm_descr =
|
|
{
|
|
.pd_name = AMD_PMC_CLASS_NAME "1",
|
|
.pd_class = AMD_PMC_CLASS,
|
|
.pd_caps = AMD_PMC_CAPS,
|
|
.pd_width = 48
|
|
},
|
|
.pm_evsel = AMD_PMC_EVSEL_1,
|
|
.pm_perfctr = AMD_PMC_PERFCTR_1
|
|
},
|
|
{
|
|
.pm_descr =
|
|
{
|
|
.pd_name = AMD_PMC_CLASS_NAME "2",
|
|
.pd_class = AMD_PMC_CLASS,
|
|
.pd_caps = AMD_PMC_CAPS,
|
|
.pd_width = 48
|
|
},
|
|
.pm_evsel = AMD_PMC_EVSEL_2,
|
|
.pm_perfctr = AMD_PMC_PERFCTR_2
|
|
},
|
|
{
|
|
.pm_descr =
|
|
{
|
|
.pd_name = AMD_PMC_CLASS_NAME "3",
|
|
.pd_class = AMD_PMC_CLASS,
|
|
.pd_caps = AMD_PMC_CAPS,
|
|
.pd_width = 48
|
|
},
|
|
.pm_evsel = AMD_PMC_EVSEL_3,
|
|
.pm_perfctr = AMD_PMC_PERFCTR_3
|
|
}
|
|
};
|
|
|
|
struct amd_event_code_map {
|
|
enum pmc_event pe_ev; /* enum value */
|
|
uint8_t pe_code; /* encoded event mask */
|
|
uint8_t pe_mask; /* bits allowed in unit mask */
|
|
};
|
|
|
|
const struct amd_event_code_map amd_event_codes[] = {
|
|
#if __i386__
|
|
{ PMC_EV_K7_DC_ACCESSES, 0x40, 0 },
|
|
{ PMC_EV_K7_DC_MISSES, 0x41, 0 },
|
|
{ PMC_EV_K7_DC_REFILLS_FROM_L2, 0x42, K7_PMC_UNITMASK_MOESI },
|
|
{ PMC_EV_K7_DC_REFILLS_FROM_SYSTEM, 0x43, K7_PMC_UNITMASK_MOESI },
|
|
{ PMC_EV_K7_DC_WRITEBACKS, 0x44, K7_PMC_UNITMASK_MOESI },
|
|
{ PMC_EV_K7_L1_DTLB_MISS_AND_L2_DTLB_HITS, 0x45, 0 },
|
|
{ PMC_EV_K7_L1_AND_L2_DTLB_MISSES, 0x46, 0 },
|
|
{ PMC_EV_K7_MISALIGNED_REFERENCES, 0x47, 0 },
|
|
|
|
{ PMC_EV_K7_IC_FETCHES, 0x80, 0 },
|
|
{ PMC_EV_K7_IC_MISSES, 0x81, 0 },
|
|
|
|
{ PMC_EV_K7_L1_ITLB_MISSES, 0x84, 0 },
|
|
{ PMC_EV_K7_L1_L2_ITLB_MISSES, 0x85, 0 },
|
|
|
|
{ PMC_EV_K7_RETIRED_INSTRUCTIONS, 0xC0, 0 },
|
|
{ PMC_EV_K7_RETIRED_OPS, 0xC1, 0 },
|
|
{ PMC_EV_K7_RETIRED_BRANCHES, 0xC2, 0 },
|
|
{ PMC_EV_K7_RETIRED_BRANCHES_MISPREDICTED, 0xC3, 0 },
|
|
{ PMC_EV_K7_RETIRED_TAKEN_BRANCHES, 0xC4, 0 },
|
|
{ PMC_EV_K7_RETIRED_TAKEN_BRANCHES_MISPREDICTED, 0xC5, 0 },
|
|
{ PMC_EV_K7_RETIRED_FAR_CONTROL_TRANSFERS, 0xC6, 0 },
|
|
{ PMC_EV_K7_RETIRED_RESYNC_BRANCHES, 0xC7, 0 },
|
|
{ PMC_EV_K7_INTERRUPTS_MASKED_CYCLES, 0xCD, 0 },
|
|
{ PMC_EV_K7_INTERRUPTS_MASKED_WHILE_PENDING_CYCLES, 0xCE, 0 },
|
|
{ PMC_EV_K7_HARDWARE_INTERRUPTS, 0xCF, 0 }
|
|
#endif
|
|
|
|
#if __amd64__
|
|
{ PMC_EV_K8_FP_DISPATCHED_FPU_OPS, 0x00, 0x3F },
|
|
{ PMC_EV_K8_FP_CYCLES_WITH_NO_FPU_OPS_RETIRED, 0x01, 0x00 },
|
|
{ PMC_EV_K8_FP_DISPATCHED_FPU_FAST_FLAG_OPS, 0x02, 0x00 },
|
|
|
|
{ PMC_EV_K8_LS_SEGMENT_REGISTER_LOAD, 0x20, 0x7F },
|
|
{ PMC_EV_K8_LS_MICROARCHITECTURAL_RESYNC_BY_SELF_MODIFYING_CODE,
|
|
0x21, 0x00 },
|
|
{ PMC_EV_K8_LS_MICROARCHITECTURAL_RESYNC_BY_SNOOP, 0x22, 0x00 },
|
|
{ PMC_EV_K8_LS_BUFFER2_FULL, 0x23, 0x00 },
|
|
{ PMC_EV_K8_LS_LOCKED_OPERATION, 0x24, 0x07 },
|
|
{ PMC_EV_K8_LS_MICROARCHITECTURAL_LATE_CANCEL, 0x25, 0x00 },
|
|
{ PMC_EV_K8_LS_RETIRED_CFLUSH_INSTRUCTIONS, 0x26, 0x00 },
|
|
{ PMC_EV_K8_LS_RETIRED_CPUID_INSTRUCTIONS, 0x27, 0x00 },
|
|
|
|
{ PMC_EV_K8_DC_ACCESS, 0x40, 0x00 },
|
|
{ PMC_EV_K8_DC_MISS, 0x41, 0x00 },
|
|
{ PMC_EV_K8_DC_REFILL_FROM_L2, 0x42, 0x1F },
|
|
{ PMC_EV_K8_DC_REFILL_FROM_SYSTEM, 0x43, 0x1F },
|
|
{ PMC_EV_K8_DC_COPYBACK, 0x44, 0x1F },
|
|
{ PMC_EV_K8_DC_L1_DTLB_MISS_AND_L2_DTLB_HIT, 0x45, 0x00 },
|
|
{ PMC_EV_K8_DC_L1_DTLB_MISS_AND_L2_DTLB_MISS, 0x46, 0x00 },
|
|
{ PMC_EV_K8_DC_MISALIGNED_DATA_REFERENCE, 0x47, 0x00 },
|
|
{ PMC_EV_K8_DC_MICROARCHITECTURAL_LATE_CANCEL, 0x48, 0x00 },
|
|
{ PMC_EV_K8_DC_MICROARCHITECTURAL_EARLY_CANCEL, 0x49, 0x00 },
|
|
{ PMC_EV_K8_DC_ONE_BIT_ECC_ERROR, 0x4A, 0x03 },
|
|
{ PMC_EV_K8_DC_DISPATCHED_PREFETCH_INSTRUCTIONS, 0x4B, 0x07 },
|
|
{ PMC_EV_K8_DC_DCACHE_ACCESSES_BY_LOCKS, 0x4C, 0x03 },
|
|
|
|
{ PMC_EV_K8_BU_CPU_CLK_UNHALTED, 0x76, 0x00 },
|
|
{ PMC_EV_K8_BU_INTERNAL_L2_REQUEST, 0x7D, 0x1F },
|
|
{ PMC_EV_K8_BU_FILL_REQUEST_L2_MISS, 0x7E, 0x07 },
|
|
{ PMC_EV_K8_BU_FILL_INTO_L2, 0x7F, 0x03 },
|
|
|
|
{ PMC_EV_K8_IC_FETCH, 0x80, 0x00 },
|
|
{ PMC_EV_K8_IC_MISS, 0x81, 0x00 },
|
|
{ PMC_EV_K8_IC_REFILL_FROM_L2, 0x82, 0x00 },
|
|
{ PMC_EV_K8_IC_REFILL_FROM_SYSTEM, 0x83, 0x00 },
|
|
{ PMC_EV_K8_IC_L1_ITLB_MISS_AND_L2_ITLB_HIT, 0x84, 0x00 },
|
|
{ PMC_EV_K8_IC_L1_ITLB_MISS_AND_L2_ITLB_MISS, 0x85, 0x00 },
|
|
{ PMC_EV_K8_IC_MICROARCHITECTURAL_RESYNC_BY_SNOOP, 0x86, 0x00 },
|
|
{ PMC_EV_K8_IC_INSTRUCTION_FETCH_STALL, 0x87, 0x00 },
|
|
{ PMC_EV_K8_IC_RETURN_STACK_HIT, 0x88, 0x00 },
|
|
{ PMC_EV_K8_IC_RETURN_STACK_OVERFLOW, 0x89, 0x00 },
|
|
|
|
{ PMC_EV_K8_FR_RETIRED_X86_INSTRUCTIONS, 0xC0, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_UOPS, 0xC1, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_BRANCHES, 0xC2, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_BRANCHES_MISPREDICTED, 0xC3, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_TAKEN_BRANCHES, 0xC4, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_TAKEN_BRANCHES_MISPREDICTED, 0xC5, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_FAR_CONTROL_TRANSFERS, 0xC6, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_RESYNCS, 0xC7, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_NEAR_RETURNS, 0xC8, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_NEAR_RETURNS_MISPREDICTED, 0xC9, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_TAKEN_BRANCHES_MISPREDICTED_BY_ADDR_MISCOMPARE,
|
|
0xCA, 0x00 },
|
|
{ PMC_EV_K8_FR_RETIRED_FPU_INSTRUCTIONS, 0xCB, 0x0F },
|
|
{ PMC_EV_K8_FR_RETIRED_FASTPATH_DOUBLE_OP_INSTRUCTIONS,
|
|
0xCC, 0x07 },
|
|
{ PMC_EV_K8_FR_INTERRUPTS_MASKED_CYCLES, 0xCD, 0x00 },
|
|
{ PMC_EV_K8_FR_INTERRUPTS_MASKED_WHILE_PENDING_CYCLES, 0xCE, 0x00 },
|
|
{ PMC_EV_K8_FR_TAKEN_HARDWARE_INTERRUPTS, 0xCF, 0x00 },
|
|
|
|
{ PMC_EV_K8_FR_DECODER_EMPTY, 0xD0, 0x00 },
|
|
{ PMC_EV_K8_FR_DISPATCH_STALLS, 0xD1, 0x00 },
|
|
{ PMC_EV_K8_FR_DISPATCH_STALL_FROM_BRANCH_ABORT_TO_RETIRE,
|
|
0xD2, 0x00 },
|
|
{ PMC_EV_K8_FR_DISPATCH_STALL_FOR_SERIALIZATION, 0xD3, 0x00 },
|
|
{ PMC_EV_K8_FR_DISPATCH_STALL_FOR_SEGMENT_LOAD, 0xD4, 0x00 },
|
|
{ PMC_EV_K8_FR_DISPATCH_STALL_WHEN_REORDER_BUFFER_IS_FULL,
|
|
0xD5, 0x00 },
|
|
{ PMC_EV_K8_FR_DISPATCH_STALL_WHEN_RESERVATION_STATIONS_ARE_FULL,
|
|
0xD6, 0x00 },
|
|
{ PMC_EV_K8_FR_DISPATCH_STALL_WHEN_FPU_IS_FULL, 0xD7, 0x00 },
|
|
{ PMC_EV_K8_FR_DISPATCH_STALL_WHEN_LS_IS_FULL, 0xD8, 0x00 },
|
|
{ PMC_EV_K8_FR_DISPATCH_STALL_WHEN_WAITING_FOR_ALL_TO_BE_QUIET,
|
|
0xD9, 0x00 },
|
|
{ PMC_EV_K8_FR_DISPATCH_STALL_WHEN_FAR_XFER_OR_RESYNC_BRANCH_PENDING,
|
|
0xDA, 0x00 },
|
|
{ PMC_EV_K8_FR_FPU_EXCEPTIONS, 0xDB, 0x0F },
|
|
{ PMC_EV_K8_FR_NUMBER_OF_BREAKPOINTS_FOR_DR0, 0xDC, 0x00 },
|
|
{ PMC_EV_K8_FR_NUMBER_OF_BREAKPOINTS_FOR_DR1, 0xDD, 0x00 },
|
|
{ PMC_EV_K8_FR_NUMBER_OF_BREAKPOINTS_FOR_DR2, 0xDE, 0x00 },
|
|
{ PMC_EV_K8_FR_NUMBER_OF_BREAKPOINTS_FOR_DR3, 0xDF, 0x00 },
|
|
|
|
{ PMC_EV_K8_NB_MEMORY_CONTROLLER_PAGE_ACCESS_EVENT, 0xE0, 0x7 },
|
|
{ PMC_EV_K8_NB_MEMORY_CONTROLLER_PAGE_TABLE_OVERFLOW, 0xE1, 0x00 },
|
|
{ PMC_EV_K8_NB_MEMORY_CONTROLLER_DRAM_COMMAND_SLOTS_MISSED,
|
|
0xE2, 0x00 },
|
|
{ PMC_EV_K8_NB_MEMORY_CONTROLLER_TURNAROUND, 0xE3, 0x07 },
|
|
{ PMC_EV_K8_NB_MEMORY_CONTROLLER_BYPASS_SATURATION, 0xE4, 0x0F },
|
|
{ PMC_EV_K8_NB_SIZED_COMMANDS, 0xEB, 0x7F },
|
|
{ PMC_EV_K8_NB_PROBE_RESULT, 0xEC, 0x0F },
|
|
{ PMC_EV_K8_NB_HT_BUS0_BANDWIDTH, 0xF6, 0x0F },
|
|
{ PMC_EV_K8_NB_HT_BUS1_BANDWIDTH, 0xF7, 0x0F },
|
|
{ PMC_EV_K8_NB_HT_BUS2_BANDWIDTH, 0xF8, 0x0F }
|
|
#endif
|
|
|
|
};
|
|
|
|
const int amd_event_codes_size =
|
|
sizeof(amd_event_codes) / sizeof(amd_event_codes[0]);
|
|
|
|
/*
|
|
* read a pmc register
|
|
*/
|
|
|
|
static int
|
|
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 < mp_ncpus,
|
|
("[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;
|
|
|
|
KASSERT(pm != NULL,
|
|
("[amd,%d] No owner for HWPMC [cpu%d,pmc%d]", __LINE__,
|
|
cpu, ri));
|
|
|
|
mode = PMC_TO_MODE(pm);
|
|
|
|
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;
|
|
}
|
|
|
|
KASSERT(pd->pm_descr.pd_class == AMD_PMC_CLASS,
|
|
("[amd,%d] unknown PMC class (%d)", __LINE__,
|
|
pd->pm_descr.pd_class));
|
|
|
|
tmp = rdmsr(pd->pm_perfctr); /* RDMSR serializes */
|
|
if (PMC_IS_SAMPLING_MODE(mode))
|
|
*v = -tmp;
|
|
else
|
|
*v = tmp;
|
|
|
|
PMCDBG(MDP,REA,2,"amd-read id=%d -> %jd", ri, *v);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write a PMC MSR.
|
|
*/
|
|
|
|
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;
|
|
|
|
KASSERT(cpu >= 0 && cpu < mp_ncpus,
|
|
("[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;
|
|
|
|
KASSERT(pm != NULL,
|
|
("[amd,%d] PMC not owned (cpu%d,pmc%d)", __LINE__,
|
|
cpu, ri));
|
|
|
|
mode = PMC_TO_MODE(pm);
|
|
|
|
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
|
|
return 0;
|
|
|
|
KASSERT(pd->pm_descr.pd_class == AMD_PMC_CLASS,
|
|
("[amd,%d] unknown PMC class (%d)", __LINE__,
|
|
pd->pm_descr.pd_class));
|
|
|
|
/* use 2's complement of the count for sampling mode PMCs */
|
|
if (PMC_IS_SAMPLING_MODE(mode))
|
|
v = -v;
|
|
|
|
PMCDBG(MDP,WRI,1,"amd-write cpu=%d ri=%d v=%jx", cpu, ri, v);
|
|
|
|
/* write the PMC value */
|
|
wrmsr(pd->pm_perfctr, v);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* configure hardware pmc according to the configuration recorded in
|
|
* pmc 'pm'.
|
|
*/
|
|
|
|
static int
|
|
amd_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 < mp_ncpus,
|
|
("[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];
|
|
|
|
KASSERT(pm == NULL || phw->phw_pmc == NULL,
|
|
("[amd,%d] pm=%p phw->pm=%p hwpmc not unconfigured",
|
|
__LINE__, pm, phw->phw_pmc));
|
|
|
|
phw->phw_pmc = pm;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Retrieve a configured PMC pointer from hardware state.
|
|
*/
|
|
|
|
static int
|
|
amd_get_config(int cpu, int ri, struct pmc **ppm)
|
|
{
|
|
*ppm = pmc_pcpu[cpu]->pc_hwpmcs[ri]->phw_pmc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Machine dependent actions taken during the context switch in of a
|
|
* thread.
|
|
*/
|
|
|
|
static int
|
|
amd_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 needed */
|
|
if (pp->pp_flags & PMC_PP_ENABLE_MSR_ACCESS)
|
|
load_cr4(rcr4() | CR4_PCE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Machine dependent actions taken during the context switch out of a
|
|
* thread.
|
|
*/
|
|
|
|
static int
|
|
amd_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 enable-msr=%d", pc, pp, pp ?
|
|
(pp->pp_flags & PMC_PP_ENABLE_MSR_ACCESS) == 1 : 0);
|
|
|
|
/* always turn off the RDPMC instruction */
|
|
load_cr4(rcr4() & ~CR4_PCE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check if a given allocation is feasible.
|
|
*/
|
|
|
|
static int
|
|
amd_allocate_pmc(int cpu, int ri, struct pmc *pm,
|
|
const struct pmc_op_pmcallocate *a)
|
|
{
|
|
int i;
|
|
uint32_t allowed_unitmask, caps, config, unitmask;
|
|
enum pmc_event pe;
|
|
const struct pmc_descr *pd;
|
|
|
|
(void) cpu;
|
|
|
|
KASSERT(cpu >= 0 && cpu < mp_ncpus,
|
|
("[amd,%d] illegal CPU value %d", __LINE__, cpu));
|
|
KASSERT(ri >= 0 && ri < AMD_NPMCS,
|
|
("[amd,%d] illegal row index %d", __LINE__, ri));
|
|
|
|
pd = &amd_pmcdesc[ri].pm_descr;
|
|
|
|
/* check class match */
|
|
if (pd->pd_class != a->pm_class)
|
|
return EINVAL;
|
|
|
|
caps = pm->pm_caps;
|
|
|
|
PMCDBG(MDP,ALL,1,"amd-allocate ri=%d caps=0x%x", ri, caps);
|
|
|
|
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;
|
|
}
|
|
|
|
KASSERT(pd->pd_class == AMD_PMC_CLASS,
|
|
("[amd,%d] Unknown PMC class (%d)", __LINE__, pd->pd_class));
|
|
|
|
pe = a->pm_ev;
|
|
|
|
/* map ev to the correct event mask code */
|
|
config = allowed_unitmask = 0;
|
|
for (i = 0; i < amd_event_codes_size; i++)
|
|
if (amd_event_codes[i].pe_ev == pe) {
|
|
config =
|
|
AMD_PMC_TO_EVENTMASK(amd_event_codes[i].pe_code);
|
|
allowed_unitmask =
|
|
AMD_PMC_TO_UNITMASK(amd_event_codes[i].pe_mask);
|
|
break;
|
|
}
|
|
if (i == amd_event_codes_size)
|
|
return EINVAL;
|
|
|
|
unitmask = a->pm_amd_config & AMD_PMC_UNITMASK;
|
|
if (unitmask & ~allowed_unitmask) /* disallow reserved bits */
|
|
return EINVAL;
|
|
|
|
if (unitmask && (caps & PMC_CAP_QUALIFIER))
|
|
config |= unitmask;
|
|
|
|
if (caps & PMC_CAP_THRESHOLD)
|
|
config |= a->pm_amd_config & AMD_PMC_COUNTERMASK;
|
|
|
|
/* set at least one of the 'usr' or 'os' caps */
|
|
if (caps & PMC_CAP_USER)
|
|
config |= AMD_PMC_USR;
|
|
if (caps & PMC_CAP_SYSTEM)
|
|
config |= AMD_PMC_OS;
|
|
if ((caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 0)
|
|
config |= (AMD_PMC_USR|AMD_PMC_OS);
|
|
|
|
if (caps & PMC_CAP_EDGE)
|
|
config |= AMD_PMC_EDGE;
|
|
if (caps & PMC_CAP_INVERT)
|
|
config |= AMD_PMC_INVERT;
|
|
if (caps & PMC_CAP_INTERRUPT)
|
|
config |= AMD_PMC_INT;
|
|
|
|
pm->pm_md.pm_amd.pm_amd_evsel = config; /* save config value */
|
|
|
|
PMCDBG(MDP,ALL,2,"amd-allocate ri=%d -> config=0x%x", ri, config);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Release machine dependent state associated with a PMC. This is a
|
|
* no-op on this architecture.
|
|
*
|
|
*/
|
|
|
|
/* ARGSUSED0 */
|
|
static int
|
|
amd_release_pmc(int cpu, int ri, struct pmc *pmc)
|
|
{
|
|
#if DEBUG
|
|
const struct amd_descr *pd;
|
|
#endif
|
|
struct pmc_hw *phw;
|
|
|
|
(void) pmc;
|
|
|
|
KASSERT(cpu >= 0 && cpu < mp_ncpus,
|
|
("[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];
|
|
|
|
KASSERT(phw->phw_pmc == NULL,
|
|
("[amd,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
|
|
|
|
#if DEBUG
|
|
pd = &amd_pmcdesc[ri];
|
|
if (pd->pm_descr.pd_class == AMD_PMC_CLASS)
|
|
KASSERT(AMD_PMC_IS_STOPPED(pd->pm_evsel),
|
|
("[amd,%d] PMC %d released while active", __LINE__, ri));
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* start a PMC.
|
|
*/
|
|
|
|
static int
|
|
amd_start_pmc(int cpu, int ri)
|
|
{
|
|
uint32_t config;
|
|
struct pmc *pm;
|
|
struct pmc_hw *phw;
|
|
const struct amd_descr *pd;
|
|
|
|
KASSERT(cpu >= 0 && cpu < mp_ncpus,
|
|
("[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];
|
|
pm = phw->phw_pmc;
|
|
pd = &amd_pmcdesc[ri];
|
|
|
|
KASSERT(pm != NULL,
|
|
("[amd,%d] starting cpu%d,pmc%d with null pmc record", __LINE__,
|
|
cpu, 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 */
|
|
|
|
KASSERT(pd->pm_descr.pd_class == AMD_PMC_CLASS,
|
|
("[amd,%d] unknown PMC class (%d)", __LINE__,
|
|
pd->pm_descr.pd_class));
|
|
|
|
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));
|
|
|
|
/* turn on the PMC ENABLE bit */
|
|
config = pm->pm_md.pm_amd.pm_amd_evsel | AMD_PMC_ENABLE;
|
|
|
|
PMCDBG(MDP,STA,2,"amd-start config=0x%x", config);
|
|
|
|
wrmsr(pd->pm_evsel, config);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Stop a PMC.
|
|
*/
|
|
|
|
static int
|
|
amd_stop_pmc(int cpu, int ri)
|
|
{
|
|
struct pmc *pm;
|
|
struct pmc_hw *phw;
|
|
const struct amd_descr *pd;
|
|
uint64_t config;
|
|
|
|
KASSERT(cpu >= 0 && cpu < mp_ncpus,
|
|
("[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];
|
|
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;
|
|
|
|
KASSERT(pd->pm_descr.pd_class == AMD_PMC_CLASS,
|
|
("[amd,%d] unknown PMC class (%d)", __LINE__,
|
|
pd->pm_descr.pd_class));
|
|
|
|
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));
|
|
|
|
PMCDBG(MDP,STO,1,"amd-stop ri=%d", ri);
|
|
|
|
/* turn off the PMC ENABLE bit */
|
|
config = pm->pm_md.pm_amd.pm_amd_evsel & ~AMD_PMC_ENABLE;
|
|
wrmsr(pd->pm_evsel, config);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Interrupt handler. This function needs to return '1' if the
|
|
* interrupt was this CPU's PMCs or '0' otherwise. It is not allowed
|
|
* to sleep or do anything a 'fast' interrupt handler is not allowed
|
|
* to do.
|
|
*/
|
|
|
|
static int
|
|
amd_intr(int cpu, uintptr_t eip)
|
|
{
|
|
int i, retval;
|
|
enum pmc_mode mode;
|
|
uint32_t perfctr;
|
|
struct pmc *pm;
|
|
struct pmc_cpu *pc;
|
|
struct pmc_hw *phw;
|
|
|
|
KASSERT(cpu >= 0 && cpu < mp_ncpus,
|
|
("[amd,%d] out of range CPU %d", __LINE__, cpu));
|
|
|
|
retval = 0;
|
|
|
|
pc = pmc_pcpu[cpu];
|
|
|
|
/*
|
|
* look for all PMCs that have interrupted:
|
|
* - skip over the TSC [PMC#0]
|
|
* - look for a PMC with a valid 'struct pmc' association
|
|
* - look for a PMC in (a) sampling mode and (b) which has
|
|
* overflowed. If found, we update the process's
|
|
* histogram or send it a profiling signal by calling
|
|
* the appropriate helper function.
|
|
*/
|
|
|
|
for (i = 1; i < AMD_NPMCS; i++) {
|
|
|
|
phw = pc->pc_hwpmcs[i];
|
|
perfctr = amd_pmcdesc[i].pm_perfctr;
|
|
KASSERT(phw != NULL, ("[amd,%d] null PHW pointer", __LINE__));
|
|
|
|
if ((pm = phw->phw_pmc) == NULL ||
|
|
pm->pm_state != PMC_STATE_RUNNING) {
|
|
atomic_add_int(&pmc_stats.pm_intr_ignored, 1);
|
|
continue;
|
|
}
|
|
|
|
mode = PMC_TO_MODE(pm);
|
|
if (PMC_IS_SAMPLING_MODE(mode) &&
|
|
AMD_PMC_HAS_OVERFLOWED(perfctr)) {
|
|
atomic_add_int(&pmc_stats.pm_intr_processed, 1);
|
|
if (PMC_IS_SYSTEM_MODE(mode))
|
|
pmc_update_histogram(phw, eip);
|
|
else if (PMC_IS_VIRTUAL_MODE(mode))
|
|
pmc_send_signal(pm);
|
|
retval = 1;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* describe a PMC
|
|
*/
|
|
static int
|
|
amd_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
|
|
{
|
|
int error;
|
|
size_t copied;
|
|
const struct amd_descr *pd;
|
|
struct pmc_hw *phw;
|
|
|
|
KASSERT(cpu >= 0 && cpu < mp_ncpus,
|
|
("[amd,%d] illegal CPU %d", __LINE__, cpu));
|
|
KASSERT(ri >= 0 && ri < AMD_NPMCS,
|
|
("[amd,%d] row-index %d out of range", __LINE__, ri));
|
|
|
|
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
|
|
pd = &amd_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;
|
|
}
|
|
|
|
/*
|
|
* i386 specific entry points
|
|
*/
|
|
|
|
/*
|
|
* return the MSR address of the given PMC.
|
|
*/
|
|
|
|
static int
|
|
amd_get_msr(int ri, uint32_t *msr)
|
|
{
|
|
KASSERT(ri >= 0 && ri < AMD_NPMCS,
|
|
("[amd,%d] ri %d out of range", __LINE__, ri));
|
|
|
|
*msr = amd_pmcdesc[ri].pm_perfctr - AMD_PMC_PERFCTR_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)
|
|
{
|
|
int n;
|
|
struct amd_cpu *pcs;
|
|
struct pmc_hw *phw;
|
|
|
|
KASSERT(cpu >= 0 && cpu < mp_ncpus,
|
|
("[amd,%d] insane cpu number %d", __LINE__, cpu));
|
|
|
|
PMCDBG(MDP,INI,1,"amd-init cpu=%d", cpu);
|
|
|
|
MALLOC(pcs, struct amd_cpu *, sizeof(struct amd_cpu), M_PMC,
|
|
M_WAITOK|M_ZERO);
|
|
|
|
if (pcs == NULL)
|
|
return ENOMEM;
|
|
|
|
phw = &pcs->pc_amdpmcs[0];
|
|
|
|
/*
|
|
* Initialize the per-cpu mutex and set the content of the
|
|
* hardware descriptors to a known state.
|
|
*/
|
|
|
|
for (n = 0; 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
/*
|
|
* processor dependent cleanup prior to the KLD
|
|
* being unloaded
|
|
*/
|
|
|
|
static int
|
|
amd_cleanup(int cpu)
|
|
{
|
|
int i;
|
|
uint32_t evsel;
|
|
struct pmc_cpu *pcs;
|
|
|
|
KASSERT(cpu >= 0 && cpu < mp_ncpus,
|
|
("[amd,%d] insane cpu number (%d)", __LINE__, cpu));
|
|
|
|
PMCDBG(MDP,INI,1,"amd-cleanup cpu=%d", 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;
|
|
wrmsr(AMD_PMC_EVSEL_0 + i, evsel);
|
|
}
|
|
|
|
/*
|
|
* Next, free up allocated space.
|
|
*/
|
|
|
|
pcs = pmc_pcpu[cpu];
|
|
|
|
#if 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,
|
|
("[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
|
|
KASSERT(pcs != NULL,
|
|
("[amd,%d] null per-cpu state pointer (cpu%d)", __LINE__, cpu));
|
|
|
|
pmc_pcpu[cpu] = NULL;
|
|
FREE(pcs, M_PMC);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize ourselves.
|
|
*/
|
|
|
|
struct pmc_mdep *
|
|
pmc_amd_initialize(void)
|
|
{
|
|
|
|
struct pmc_mdep *pmc_mdep;
|
|
|
|
/* The presence of hardware performance counters on the AMD
|
|
Athlon, Duron or later processors, is _not_ indicated by
|
|
any of the processor feature flags set by the 'CPUID'
|
|
instruction, so we only check the 'instruction family'
|
|
field returned by CPUID for instruction family >= 6. This
|
|
test needs to be be refined. */
|
|
|
|
if ((cpu_id & 0xF00) < 0x600)
|
|
return NULL;
|
|
|
|
MALLOC(pmc_mdep, struct pmc_mdep *, sizeof(struct pmc_mdep),
|
|
M_PMC, M_WAITOK|M_ZERO);
|
|
|
|
#if __i386__
|
|
pmc_mdep->pmd_cputype = PMC_CPU_AMD_K7;
|
|
#elif __amd64__
|
|
pmc_mdep->pmd_cputype = PMC_CPU_AMD_K8;
|
|
#else
|
|
#error Unknown AMD CPU type.
|
|
#endif
|
|
|
|
pmc_mdep->pmd_npmc = AMD_NPMCS;
|
|
|
|
/* this processor has two classes of usable PMCs */
|
|
pmc_mdep->pmd_nclass = 2;
|
|
|
|
/* 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;
|
|
|
|
/* AMD K7/K8 PMCs */
|
|
pmc_mdep->pmd_classes[1].pm_class = AMD_PMC_CLASS;
|
|
pmc_mdep->pmd_classes[1].pm_caps = AMD_PMC_CAPS;
|
|
pmc_mdep->pmd_classes[1].pm_width = 48;
|
|
|
|
pmc_mdep->pmd_nclasspmcs[0] = 1;
|
|
pmc_mdep->pmd_nclasspmcs[1] = (AMD_NPMCS-1);
|
|
|
|
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 */
|
|
|
|
PMCDBG(MDP,INI,0,"%s","amd-initialize");
|
|
|
|
return pmc_mdep;
|
|
}
|