rock_pcbe.c revision 2f0fcb93196badcdd803715656c809058d9f3114
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Rock Performance Counter Back End
*/
#include <sys/cpc_impl.h>
#include <sys/cpc_pcbe.h>
#include <sys/machsystm.h>
#include <sys/hypervisor_api.h>
#include <sys/rock_hypervisor_api.h>
#define NT_END 0xFF
#define CPC_COUNT_HPRIV 0x8
/* Counter Types */
#define NUM_PCBE_COUNTERS 7
#define RK_PERF_CYC 0x0100
#define RK_PERF_INSTR 0x0200
#define RK_PERF_L2 0x0400
#define RK_PERF_MMU 0x0800
#define RK_PERF_YANK 0x2000
#define RK_PERF_SIBLK 0x4000
#define RK_PERF_LVLK 0x8000
#define NORMAL_COUNTER 0x1
#define SYNTHETIC_COUNTER 0x2
/* ASI_PERF_MMU_CNT_FILTER TXN bits */
#define ASI_PERF_MMU_CNT_FILTER_UTLB_HITS 0x1
#define ASI_PERF_MMU_CNT_FILTER_UTLB_MISS 0x2
#define ASI_PERF_MMU_CNT_FILTER_DATA_ACCESS 0x8
#define ASI_PERF_MMU_CNT_FILTER_INSTR_ACCESS 0x10
#define ASI_PERF_MMU_CNT_FILTER_EA_VIRTUAL 0x20
#define ASI_PERF_MMU_CNT_FILTER_EA_REAL 0x40
#define MMU_ALL_TXNS (ASI_PERF_MMU_CNT_FILTER_UTLB_HITS | \
#define MMU_ITLB_MISS (ASI_PERF_MMU_CNT_FILTER_EA_REAL | \
#define MMU_DTLB_MISS (ASI_PERF_MMU_CNT_FILTER_EA_REAL | \
#define MMU_UTLB_MISS (ASI_PERF_MMU_CNT_FILTER_EA_REAL | \
#define MMU_UTLB_HIT (ASI_PERF_MMU_CNT_FILTER_EA_REAL | \
#define MMU_ITLB_MISS_UTLB_HIT (ASI_PERF_MMU_CNT_FILTER_EA_REAL | \
#define MMU_ITLB_MISS_UTLB_MISS (ASI_PERF_MMU_CNT_FILTER_EA_REAL | \
#define MMU_DTLB_MISS_UTLB_HIT (ASI_PERF_MMU_CNT_FILTER_EA_REAL | \
#define MMU_DTLB_MISS_UTLB_MISS (ASI_PERF_MMU_CNT_FILTER_EA_REAL | \
/*
* These values will be loaded to nametable.bits which is a 32 bit number.
* Please see the description of bits in nametable struct. If the counters
* are a part of different pic, then we can re-use GROUP and TYPE.
*/
/* Synthetic counter types */
#define L2_GROUP_DS ID_TO_GROUP(0)
#define DS_DRAM 0x0
#define DS_L3 0x1
#define DS_OTHER_L2 0x2
#define DS_LOCAL_L2 0x3
#define DS_MISS 0x4
#define TXN_LD 0x3
#define TXN_ST 0x18
#define EVT_L2_NOEVENTS 0
#define EVT_L2_PEND_ST 2
#define EVT_L2_HIT 0
/* Instruction types. Corresponds to ASI_PERF_IS_INFO.TYP */
#define I_GROUP_TYPE ID_TO_GROUP(0)
#define TYPE_HELPER (1<<0)
/* Execution modes. Corresponds to ASI_PERF_IS_INFO.MODE */
/* Instruction events. Corresponds to ASI_PERF_IS_INFO.EVT */
/* Bit numbers from PRM */
#define EVT_DC_MISS (1<<0)
/*
* Synthetic counters to count MCCDESR error events
* All the events are mutually exclusive therefore can be counted
* simultaneously. Hence each one is a different pic. Therefore
* there is no need to have GROUP or TYPE for these counters.
*/
#define MCCDESR_YANK (SYN_BIT)
#define MCCDESR_SIBLK (SYN_BIT)
#define MCCDESR_LVLK (SYN_BIT)
/* Number of samples to be taken before Performance Event Trap is generated */
/* Maximum frequencies that can be configured */
/* Minimum frequencies that should be configured to prevent DOS */
#define INSTR_SAM_MIN_FREQ 50
#define L2_SAM_MIN_FREQ 250
#define MMU_SAM_MIN_FREQ 250
/* Default frequencies that are configured */
#define INSTR_MODE_FREQ 100
#define L2_DS_FREQ 10000
#define L2_LOAD_FRM_OTH_L2_FREQ 1000
#define L2_MISS_FREQ 1000
#define L2_HIT_FREQ 5000
/* Number of bits in the hardware for the counter */
#define CYC_COUNTER_BITS 39
#define INSTR_COUNTER_BITS 39
#define L2_COUNTER_BITS 48
#define MMU_COUNTER_BITS 48
#define YANK_COUNTER_BITS 64
#define SIBLK_COUNTER_BITS 64
#define LVLK_COUNTER_BITS 64
#define RK_PERF_COUNT_TOE_SHIFT (63)
#define STATE_CONFIGURED 0x1
#define STATE_PROGRAMMED 0x2
#define STATE_STOPPED 0x4
#define STATE_RELEASED 0x8
#define TLNZ 2
#define CPU_REF_URL " Documentation for Sun processors can be found at: " \
#define MIN_RINGBUF_ENTRIES 100
#define RINGBUF_GET_HEAD(RB) \
#define RINGBUF_SET_HEAD(RB) \
/* Global Structures and typedefs */
struct _rk_pcbe_ringbuf { /* INIT-ER WRITTER READER */
};
typedef struct _rk_pcbe_ringbuf rk_pcbe_ringbuf_t;
typedef struct _sampler {
} sampler_t;
typedef struct _rk_pcbe_config {
#ifdef RKPCBE_DBG
#endif
/* Function Prototypes for those that are invoked using rk_pcbe_ops */
static int rk_pcbe_init(void);
static int rk_pcbe_fini(void);
static uint_t rk_pcbe_ncounters(void);
static const char *rk_pcbe_impl_name(void);
static const char *rk_pcbe_cpuref(void);
static char *rk_pcbe_list_attrs(void);
static uint64_t rk_pcbe_overflow_bitmap(void);
void *token);
static void rk_pcbe_program(void *token);
static void rk_pcbe_allstop(void);
static void rk_pcbe_sample(void *token);
static void rk_pcbe_free(void *config);
};
/*
* bits:
*
* | 31 |30 24|23 12|11 0
*/
struct nametable {
const char *name;
};
/* Cycle Counter. picno: 0 */
static const struct nametable Rock_names0[] = {
{0x1, "Cycles"},
{NT_END, ""}
};
/* Instruction Counter. picno: 1 */
static const struct nametable Rock_names1[] = {
{0x1, "Instr_All"},
/* Synthetic counters */
{INSTR_MODE_NOR, "Instr_Normal"},
{INSTR_MODE_OOO, "Instr_Out_Of_Order"},
{INSTR_MODE_EXE, "Instr_Execute_Ahead"},
{INSTR_MODE_DLY, "Instr_Delay"},
{INSTR_MODE_DEF, "Instr_Deferred"},
{INSTR_MODE_HWS, "Instr_Scout"},
{INSTR_TYPE_LD, "Instr_Load"},
{INSTR_TYPE_ST, "Instr_Store"},
{INSTR_TYPE_CTI, "Instr_Branch"},
{INSTR_TYPE_FP, "Instr_Float"},
{INSTR_EVT_DC_MISS, "Instr_Dcache_Miss"},
{INSTR_EVT_PRIOR_MISS, "Instr_Prior_Miss"},
{INSTR_EVT_DTLB_MISS, "Instr_Dtlb_Miss"},
{INSTR_EVT_LDB_FULL, "Instr_Loadbuf_Full"},
{INSTR_EVT_STB_FULL, "Instr_Storebuf_Full"},
{INSTR_EVT_FE_STALL, "Instr_Stall"},
{INSTR_EVT_FROM_DQ, "Instr_DQ"},
{INSTR_EVT_CORRECT_BP, "Instr_Correct_Branch_Predict"},
{INSTR_EVT_BYPASS_RAW, "Instr_Bypass_Raw"},
{INSTR_EVT_NONBYPASS_RAW, "Instr_Nonbypass_Raw"},
{INSTR_EVT_CTI_TAKEN, "Instr_Branch_Taken"},
{INSTR_EVT_FAILED_SPEC, "Instr_Failed_Spec"},
{NT_END, ""}
};
/* L2 Counters. picno: 2 */
static const struct nametable Rock_names2[] = {
{0x1, "L2_Icache_Load"},
{0x2, "L2_Dcache_Load"},
{0x4, "L2_Instr_Prefetch"},
{0x8, "L2_Store_Prefetch"},
{0x10, "L2_Store"},
{0x20, "L2_Atomic_Ops"},
{0x40, "L2_Flush"},
/* Synthetic counters */
{L2_DS_L3, "L2_Load_From_L3"},
{L2_DS_DRAM, "L2_Load_From_Dram"},
{L2_DS_OTHER_L2, "L2_Load_From_Other_L2"},
{L2_DS_MISS, "L2_Miss"},
{L2_TXN_LD_MISS, "L2_Load_Miss"},
{L2_TXN_ST_MISS, "L2_Store_Miss"},
{L2_TXN_LD_HIT, "L2_Load_Hit"},
{L2_TXN_ST_HIT, "L2_Store_Hit"},
{L2_EVT_HIT, "L2_Hit"},
{NT_END, ""}
};
/* MMU Counters. picno: 3 */
static const struct nametable Rock_names3[] = {
{MMU_ALL_TXNS, "MMU_All"},
{MMU_ITLB_MISS, "MMU_Itlb_Miss"},
{MMU_DTLB_MISS, "MMU_Dtlb_Miss"},
{MMU_UTLB_MISS, "MMU_Utlb_Miss"},
{MMU_UTLB_HIT, "MMU_Utlb_Hit"},
{MMU_ITLB_MISS_UTLB_MISS, "MMU_I_Utlb_Miss"},
{MMU_ITLB_MISS_UTLB_HIT, "MMU_I_Utlb_Hit"},
{MMU_DTLB_MISS_UTLB_MISS, "MMU_D_Utlb_Miss"},
{MMU_DTLB_MISS_UTLB_HIT, "MMU_D_Utlb_Hit"},
{NT_END, ""}
};
/* YANK Counter. picno: 4 */
static const struct nametable Rock_names4[] = {
{MCCDESR_YANK, "Yank"},
{NT_END, ""}
};
/* SIBLK Counter. picno: 5 */
static const struct nametable Rock_names5[] = {
{MCCDESR_SIBLK, "Siblk"},
{NT_END, ""}
};
/* LVLK Counter. picno: 6 */
static const struct nametable Rock_names6[] = {
{MCCDESR_LVLK, "Lvlk"},
{NT_END, ""}
};
};
extern char cpu_module_name[];
static char *pic_events[NUM_PCBE_COUNTERS];
static char *rock_name;
static char rock_cpuref[256];
static hsvc_info_t rock_pcbe_hsvc = {
HSVC_REV_1, /* HSVC rev num */
NULL, /* Private */
HSVC_GROUP_RKPERF, /* Requested API Group */
ROCK_HSVC_MAJOR, /* Requested Major */
ROCK_HSVC_MINOR, /* Requested Minor */
pcbe_module_name /* Module name */
};
/* Function Definitions */
"Perf Counters v1.1",
};
static struct modlinkage modl = {
&modlpcbe,
};
/* Local Function prototypes */
static void set_string_constants(void);
#ifdef RKPCBE_DBG
/* lock for print clarity */
static kmutex_t print_pic_lock;
#else
#endif
int
_init(void)
{
if (rk_pcbe_init() != 0)
return (ENOTSUP);
return (mod_install(&modl));
}
int
_fini(void)
{
if (rk_pcbe_fini() != 0)
return (EBUSY);
return (mod_remove(&modl));
}
int
{
}
static int
rk_pcbe_init(void)
{
const struct nametable *n;
int i, status, j;
/*
* Validate API version for Rock pcbe hypervisor services
*/
if ((status != 0) || (rock_pcbe_hsvc_sup_minor <
(uint64_t)ROCK_HSVC_MINOR)) {
"major: 0x%lx minor: 0x%lx group: 0x%x errno: %d",
return (-1);
}
events = Rock_names;
/*
* Initialize the list of events for each PIC.
* Do two passes: one to compute the size necessary and another
* to copy the strings. Need room for event, comma, and NULL terminator.
*/
for (i = 0; i < NUM_PCBE_COUNTERS; i++) {
size = 0;
*pic_events[i] = '\0';
}
/*
* Remove trailing comma.
*/
/* Initialize all active pics as NULL */
for (j = 0; j < NCPU; j++)
active_pics[i][j] = NULL;
}
#ifdef RKPCBE_DBG
#endif
return (0);
}
static int
rk_pcbe_fini(void)
{
return (0);
}
static uint_t
rk_pcbe_ncounters(void)
{
return (NUM_PCBE_COUNTERS);
}
static const char *
rk_pcbe_impl_name(void)
{
return (rock_name);
}
static const char *
rk_pcbe_cpuref(void)
{
return (rock_cpuref);
}
static char *
{
return (pic_events[picnum]);
}
static char *
rk_pcbe_list_attrs(void)
{
/*
* If no value is spcified in the command line for the
* attributes then, a default value of 1 is passed into
* pcbe from cpc. Specifying a value as zero is as good as
* not specifying it.
* 'source' attribute is equivallent of 'single, shared,
* siu, mmu' all put together. 'source' will take precedence
* over others.
* Valid 'source' values are defined in rock_hypervisor_api.h.
* If multiple flags need to be specified then user has to
* populate_pic_config validates the correctness of the flags
* specified.
* tl is little odd. To consider instructions at
* tl == 0, specify tl = TLZ in command line
* tl > 0, specify tl = TLNZ in command line
* The reason for this oddness: attr = 0 means, neglect
* that attr.
*/
return ("freq,source,single,shared,siu,mmu,nohws,tl,hpriv");
}
static const struct nametable *
{
const struct nametable *n;
return (n);
return (NULL);
}
static uint64_t
rk_pcbe_event_coverage(char *event)
{
int i;
/* There is no intersection of events between different PICs */
for (i = 0; i < NUM_PCBE_COUNTERS; i++) {
bitmap = 1 << i;
break;
}
}
return (bitmap);
}
static uint64_t
rk_pcbe_overflow_bitmap(void)
{
int i;
for (i = 0; i < NUM_PCBE_COUNTERS; i++) {
continue;
DBG_PRINT(("CPU-%d: Pic %s (#%d, cntr %X) overflowed\n",
/* Check if any of the active pics overflowed */
} else {
/*
* Synthetic counters don't overflow, so we must have gotten
* here because the ringbuffer is getting half-full or
* one of the normal counter which is a part of synthetic
* counter did overflow. Force cpc to call
* rk_pcbe_sample_synthetic by setting ovf_cnt to 1. If
* returned 0, then cpc prints a WARNING message:
* "WARNING: interrupt 0x80c at level 15 not serviced"
*/
}
if (ovf_cnt > 0)
}
return (ovf_bitmask);
}
/*
* populate_pic_config
*
* Checks the validity of all the attributes and then updates flags
* to reflect priv bits for Cycle and Instruction counters and
* transaction bits for L2 and makes sure that flags is 0 for MMU.
*
* Along with validating the inputs, pic is populated with appropriate
* values.
*
* Returns 0 on success and CPC_INVALID_ATTRIBUTE on failure.
*/
static int
{
int i;
/*
* Initialized to 0. If a valid source attribute is specified, then
* src_type field gets populated later, else will be defaulted to
* HV_RK_PERF_SRC_STRAND
*/
/*
* Initialized to zero. In all the fallthrough case, this
* is checked to determine if certain fields needs to be
* populated or not
*/
switch (picnum) {
#define PRIV_BITS_MASK 0x7
#define PRIV_BIT0_MASK 0x1
#define PRIV_BIT1_MASK 0x2
#define PRIV_BIT2_MASK 0x4
case 0: /* Cycle counter */
/* FALLTHROUGH */
case 1: /* Instruction Counter */
}
for (i = 0; i < nattrs; i++) {
return (CPC_INVALID_ATTRIBUTE);
}
*flagsp |= CPC_COUNT_HPRIV;
return (CPC_INVALID_ATTRIBUTE);
return (CPC_INVALID_ATTRIBUTE);
} else {
return (CPC_INVALID_ATTRIBUTE);
}
}
if (source) {
if (source & (HV_RK_PERF_SRC_SIU |
return (CPC_INVALID_ATTRIBUTE);
}
/*
* hpriv, sys, user are sent as bits 3, 2, 1 from kcpc.
* They are maintained by PCBE as bits 2, 1, & 0.
*/
*flagsp >>= 1;
*flagsp &= PRIV_BITS_MASK;
if (freq > INSTR_SAM_MAX_FREQ) {
"> MAX. Resetting to %d",
}
if (freq < INSTR_SAM_MIN_FREQ) {
"< MIN. Resetting to %d",
}
}
/*
* When programming counter priv bits should be
* 0, 1, & 2, i.e., in reverse order. Therefore swap
* bits 2 & 0.
*/
(*flagsp & PRIV_BIT1_MASK);
break;
case 2: /* L2 counter */
/*
* nouser and sys are also invalid attributes for L2
* and MMU counters. If user has not specified any
* attributes then *flagsp contains CPC_COUNT_USER.
* Any priv attrs are not applicable for L2 counters.
*/
if (*flagsp != CPC_COUNT_USER)
return (CPC_INVALID_ATTRIBUTE);
/*
* Normal counter:
* Find the attibutes for L2 Counter.
*/
for (i = 0; i < nattrs; i++) {
return (CPC_INVALID_ATTRIBUTE);
}
if (source)
/* At least one hot Xn flag for L2 counters */
} else {
/*
* Synthetic Counter
*/
/*
* Load default frequency and if freq attribute
* is specified, over write with that value
*/
case L2_DS_DRAM:
/* FALLTHROUGH */
case L2_DS_L3:
/* FALLTHROUGH */
case L2_DS_MISS:
freq = L2_DS_FREQ;
break;
case L2_DS_OTHER_L2:
break;
case L2_TXN_LD_MISS:
/* FALLTHROUGH */
case L2_TXN_ST_MISS:
freq = L2_MISS_FREQ;
break;
case L2_TXN_LD_HIT:
/* FALLTHROUGH */
case L2_TXN_ST_HIT:
/* FALLTHROUGH */
case L2_EVT_HIT:
freq = L2_HIT_FREQ;
break;
}
/*
* Find the attibutes for L2 Sampler.
*/
for (i = 0; i < nattrs; i++) {
return (CPC_INVALID_ATTRIBUTE);
}
if (source)
/* Range check to avoid DOS */
if (freq > L2_SAM_MAX_FREQ) {
"> MAX. Resetting to %d",
}
if (freq < L2_SAM_MIN_FREQ) {
"< MIN. Resetting to %d",
}
*flagsp = 0;
}
break;
case 3: /* MMU Counter */
if (*flagsp != CPC_COUNT_USER)
return (CPC_INVALID_ATTRIBUTE);
for (i = 0; i < nattrs; i++) {
else if
return (CPC_INVALID_ATTRIBUTE);
}
if (source) {
if (source & (HV_RK_PERF_SRC_SIU |
return (CPC_INVALID_ATTRIBUTE);
}
break;
case 4: /* YANK Counter */
/* FALLTHROUGH */
case 5: /* SIBLK Counter */
}
/* FALLTHROUGH */
case 6: /* LVLK Counter */
}
if (*flagsp != CPC_COUNT_USER)
return (CPC_INVALID_ATTRIBUTE);
for (i = 0; i < nattrs; i++) {
else if
return (CPC_INVALID_ATTRIBUTE);
}
if (source) {
if (source & (HV_RK_PERF_SRC_SIU |
return (CPC_INVALID_ATTRIBUTE);
}
*flagsp = 0;
break;
}
#ifdef RKPCBE_DBG
#endif
return (0);
}
/*ARGSUSED7*/
static int
{
const struct nametable *n;
int rc;
/* Is API version for Rock pcbe hypervisor services negotiated? */
if (rock_pcbe_hsvc_available == B_FALSE)
return (CPC_RESOURCE_UNAVAIL);
/*
* If we've been handed an existing configuration, we need only preset
* the counter value.
*/
return (0);
}
return (CPC_INVALID_PICNUM);
/*
* Find other requests that will be programmed with this one, and ensure
* they don't conflict.
* Any other counter in this pic group is active?
*/
return (CPC_CONFLICTING_REQS);
return (CPC_INVALID_EVENT);
/* Check for supported attributes and populate pic */
return (rc);
}
/*
* num_ringbuf_entries should be always even. Since this
*/
if (num_ringbuf_entries & 1) {
}
if (num_ringbuf_entries < MIN_RINGBUF_ENTRIES) {
"%u. Changing %u to %u\n", MIN_RINGBUF_ENTRIES,
}
return (0);
}
static void
rk_pcbe_program(void *token)
{
int rc;
!= NULL) {
continue;
/*
* If in thread context, pic should get an exclusive
* lock. If it cannot then make the current thread
* passive.
*/
continue;
}
} else {
/* Must be cpu context */
if (rc == H_EWOULDBLOCK &&
/* pic in use by a cpu of current guest */
continue;
/*
* Either the counter is in use by a different
* guest or another cpu in the current guest is
* already using it in single source mode. In
* either case, invalidate the pic.
*/
continue;
}
}
/*
* rc = H_EOK, hence current cpu was successful in
* obtaining exclusive access to the counter, Set this
* pic as active.
*/
}
else
continue;
}
}
}
static void
rk_pcbe_allstop(void)
{
int i;
for (i = 0; i < NUM_PCBE_COUNTERS; i++) {
continue;
/* Stop all active pics */
DBG_PRINT(("CPU-%d: Counter %s(%X) stopped.\n",
} else {
DBG_PRINT(("CPU-%d: Stopping counter %s(%lX)\n",
}
/* Mark pic as stopped */
/*
* If running in lwp context, and context is invalid or
* if we get here when both cpu as well as lwp contexts
* are invalid, then release the counter. This is can happen
* when the lwp that pcbe is monitoring is terminated. In
* this situation, pcbe_free is called directly after allstop
* without calling pcbe_sample. pcbe_free may get executed on
* a differnt strand. If the free-ing strand is not the one that
* programmed this pic, HV does not allow that operaion and will
* return H_ENOACCESS. To prevent this, the counter is released
* if the lwp that pcbe is minitoring disappears.
*/
}
}
}
static void
rk_pcbe_sample(void *token)
{
int rc;
while ((pic = (rk_pcbe_config_t *)
continue;
}
if (diff < 0)
}
} else {
/*
* Difference returned by synthetic counters will
* be always +ve
*/
}
continue;
}
}
}
static void
rk_pcbe_free(void *config)
{
/* Release counter */
}
}
/* Mark pic as inactive */
}
static void
{
int rc = 0;
DBG_PRINT(("CPU-%d: Releasing Pic %s (#%d, cntr %X) %p",
(void *)pic));
if (rc != 0) {
}
if (rc != 0) {
return;
}
}
}
static int
{
return ((int)rc);
}
/* Preset the counter value if non zero */
DBG_PRINT(("CPU-%d: Counter getting preset to %lu (%lX)\n",
}
return ((int)rc);
}
/* Configure and start counter */
}
return ((int)rc);
}
static int
{
int rc;
case RK_PERF_INSTR:
break;
case RK_PERF_L2:
break;
case RK_PERF_YANK:
/* FALLTHROUGH */
case RK_PERF_SIBLK:
/* FALLTHROUGH */
case RK_PERF_LVLK:
break;
default:
ASSERT(0);
break;
}
return (rc);
}
static void
{
case RK_PERF_INSTR:
/* FALLTHROUGH */
case RK_PERF_L2:
break;
case RK_PERF_YANK:
/* FALLTHROUGH */
case RK_PERF_SIBLK:
/* FALLTHROUGH */
case RK_PERF_LVLK:
/* Do nothing */
break;
default:
ASSERT(0);
break;
}
}
/* All sample_synthetic code may be executed at TL=1 */
static int
{
int rc;
case RK_PERF_INSTR:
break;
case RK_PERF_L2:
break;
case RK_PERF_YANK:
/* FALLTHROUGH */
case RK_PERF_SIBLK:
/* FALLTHROUGH */
case RK_PERF_LVLK:
break;
default:
ASSERT(0);
break;
}
return (rc);
}
static void
{
case RK_PERF_INSTR:
/* FALLTHROUGH */
case RK_PERF_L2:
break;
case RK_PERF_YANK:
/* FALLTHROUGH */
case RK_PERF_SIBLK:
/* FALLTHROUGH */
case RK_PERF_LVLK:
break;
default:
ASSERT(0);
break;
}
}
static int
{
#define ASI_PERF_L2_TXN_INFO 0xF10010
#define ASI_PERF_L2_EA_MASK 0xF10018
#define ASI_PERF_L2_EA_MATCH 0xF10020
#define ASI_PERF_L2_TXN_INFO_FILTER 0xF10030
#define ASI_PERF_L2_CC 0xF10038
#define TXN_ICACHE_LOAD 0x1
#define TXN_DCACHE_LOAD 0x2
#define TXN_INSTR_PREFETCH 0x4
#define TXN_STORE_PREFETCH 0x8
#define TXN_DCACHE_STORE 0x10
#define TXN_ATOMIC_LOAD_STORE 0x20
#define TXN_FLUSH 0x40
#define L2_TXN_SHIFT 3
#define L2_ALL_EVT 0x3
#define L2_ALL_EVT_SHIFT 10
(L2_ALL_TXNS << L2_TXN_SHIFT)
char *funcname = "program_l2_sampler";
}
/*
* If (((Reported EA ^ MATCH) & MASK) == 0) then sample is taken
*/
sizeof (l2_load_valist), l2_load_valist_pa);
return (ret);
}
static int
{
#define DS_SHIFT 34
#define EVT_SHIFT 22
#define TXN_SHIFT 7
char *funcname = "sample_l2_sampler";
DBG_PRINT(("CPU-%d: Head is NULL to start with\n",
}
while (head) {
if (*head != 0) {
DBG_PRINT(("CPU-%d: rawvalue=0x%lX\n",
case L2_GROUP_DS:
DBG_PRINT(("CPU-%d: value=0x%X, target=0x%X\n",
switch (target) {
case DS_DRAM: /* FALLTHROUGH */
case DS_L3: /* FALLTHROUGH */
case DS_OTHER_L2: /* FALLTHROUGH */
break;
case DS_MISS:
if (value != DS_LOCAL_L2)
break;
}
break;
case L2_GROUP_TXN_MISS:
& DS_MASK);
DBG_PRINT(("CPU-%d: value=0x%X, target=0x%X, "
break;
case L2_GROUP_TXN_HIT:
& DS_MASK);
& EVT_MASK);
DBG_PRINT(("CPU-%d: value=0x%X, target=0x%X, "
(ds == DS_LOCAL_L2))
break;
case L2_GROUP_EVT:
& DS_MASK);
DBG_PRINT(("CPU-%d: value=0x%X, target=0x%X, "
break;
}
}
sample_count++;
}
/*
* Since ring buffer is consumed, clear pending sample count.
* Sample count is discarded, therefore reusing a variable.
*/
if (sample_count != total_count) {
}
/* Check if the counter overflowed */
if (rc != 0)
ovf_count = 0;
if (rc != 0)
total_count = 0;
/*
* Reset it to zero so that we need not maintain old value
*/
if (ovf_count > 0) {
DBG_PRINT(("CPU-%d: L2 counter overflowed: ovf_count: %lu\n",
/*
* ovf_count > 0 means, counter has hit max, ovf_count times
* before counting total_count of l2 transactions. Therefore
* add total_count to ovf_count times max count value.
*/
while (ovf_count--)
}
if (sample_count > 0)
DBG_PRINT(("CPU-%d: sample_l2_sampler. hit_count: %lu, *diffp: %ld\n",
if (*diffp < 0) {
}
return (ret);
}
static int
{
#define ASI_PERF_IS_PC_MASK 0x10
#define ASI_PERF_IS_PC_MATCH 0x18
#define ASI_PERF_IS_CC_LATENCY_MASK 0x160
#define ASI_PERF_IS_CONTEXT_FILTER 0x168
#define ASI_PERF_IS_INFO_MASK 0x170
#define ASI_PERF_IS_INFO_MATCH 0x178
#define ASI_PERF_IS_CONTEXT 0x108
#define ASI_PERF_IS_INFO 0x148
#define IS_BHR_LATENCY_CLAT_MASK 0xFFF
#define IS_CC_FILTER_TGTF_MASK 0x10
#define IS_CC_FILTER_TOF_MASK 0x8
#define IS_CC_LATENCY_FREQ_SHIFT 22
char *funcname = "program_instr_sampler";
}
/*
* If (((Reported Value ^ MATCH) & MASK) == 0) then sample is taken;
*/
/*
* Set CLAT_MASK to 0xFFF, meaning, drop instruction samples
* whose latency is zero, means, sample all of them, because
* all instructions has at least a latency of 1 cycle.
*/
/*
* Even though frequency is set when started, it has to be
* specified here, because, if left zero, then a PET is
* immediately generated since the candidate counter is zero.
*/
/* Start sampling */
sizeof (instr_sampler_valist), instr_sampler_valist_pa);
return (ret);
}
static int
{
#define I_MODE_SHIFT 34
#define I_TYPE_SHIFT 0
#define I_EVT_SHIFT 7
char *funcname = "sample_instr_sampler";
case I_GROUP_MODE:
mask = I_MODE_MASK;
break;
case I_GROUP_TYPE:
mask = I_TYPE_MASK;
break;
case I_GROUP_EVT:
mask = I_EVT_MASK;
shift = I_EVT_SHIFT;
break;
default:
ASSERT(0);
break;
}
DBG_PRINT(("CPU-%d: Head is NULL to start with\n",
}
while (head) {
int drop_sample = B_FALSE;
if (rawvalue != 0) {
DBG_PRINT(("CPU-%d: rawvalue=0x%lX, value=0x%X,"
target));
/*
* Several EVT fields are only valid for certain
* instruction types. Need to check TYP field
* before trusting what's in EVT.
*/
switch (target) {
case EVT_DC_MISS:
case EVT_PRIOR_MISS:
case EVT_LDB_FULL:
case EVT_BYPASS_RAW:
case EVT_NONBYPASS_RAW:
break;
case EVT_STB_FULL:
break;
case EVT_DTLB_MISS:
break;
case EVT_CORRECT_BP:
case EVT_CTI_TAKEN:
break;
}
DBG_PRINT(("CPU-%d: rawvalue=%lX, cleaned value"
}
/*
* If user does not want to count instructions in scout
* mode, and if the instruction sampled was in scout
* mode, drop the sample.
*/
}
/*
* If user wants to count instructions at a particular
* trap level (0 or >0), and the samples are in
* different trap level, drop the sample.
*/
case TLZ: /* Sample ONLY instr at TL == 0 */
if (tl != 0)
break;
case TLNZ: /* Sample ONLY instr at TL > 0 */
if (tl == 0)
break;
}
case I_GROUP_MODE:
/* Fields that are integers */
break;
case I_GROUP_EVT:
case I_GROUP_TYPE:
/* Fields that are bit vectors */
break;
default:
ASSERT(0); /* missing case statement */
}
}
sample_count++;
DBG_PRINT(("CPU-%d: Target %X, sample_count: %d\n",
}
/*
* Since ring buffer is consumed, clear pending sample count.
* Sample count is discarded, therefore reusing a variable.
*/
if (sample_count != total_count) {
}
/* Check if the counter overflowed */
ovf_count = 0;
total_count = 0;
/*
* Reset it to zero so that we need not maintain old value
*/
if (ovf_count > 0) {
/*
* ovf_count > 0 means, counter has hit max, ovf_count times
* before counting total_count of instructions. Therefore
* add total_count to ovf_count times max count value.
*/
while (ovf_count--)
}
if (sample_count > 0)
DBG_PRINT(("CPU-%d: sample_instr_load. hit_count: %lu, *diffp: %ld\n",
if (*diffp < 0) {
}
return (ret);
}
/*
* mccdesr counters are synthetic counters. Hypervisor maintains
* a 64 bit memory based counter. Therefore we can assume that
* this counter never overflows.
*/
static int
{
if (*diffp < 0) {
}
} else {
}
return ((int)rc);
}
static void
{
}
static void
{
/*
* When multiple pics are used and one of the pics was not configurable
* (eg: Bad attribute), then cpc calls rk_pcbe_free for the pics that
* were already configured. This results in calling this routine with
* NULL ringbuf, since ringbuf is allocated when the first sample is
* taken. To protect against this condition, we need do the following
* check before calling kmem_free since it uses ringbuf->size.
*/
if (ringbuf) {
DBG_PRINT(("CPU-%d: free_ringbuffer freeing %d bytes\n",
} else {
DBG_PRINT(("CPU-%d: free_ringbuffer: Ringbuffer not "
}
}
static void
{
}
(*cntp)++;
}
static void
set_string_constants(void)
{
else
"these events. "CPU_REF_URL);
}
#ifdef RKPCBE_DBG
static void
{
const struct nametable *n;
/*
* For normal cycle and instruction counters, the 'bits' value
* is not saved.
*/
return;
}
return;
}
}
else
break;
}
}
}
static void
{
/*
* On multi strand system, the print gets clobberd. Therefore
* grab a lock so that the output is legible.
*/
printf("Synthetic counter:\n");
printf("\tRingbuffer:\n");
}
}
printf("-----------------\n");
}
#endif