1*8803SJonathan.Haslam@Sun.COM /* 2*8803SJonathan.Haslam@Sun.COM * CDDL HEADER START 3*8803SJonathan.Haslam@Sun.COM * 4*8803SJonathan.Haslam@Sun.COM * The contents of this file are subject to the terms of the 5*8803SJonathan.Haslam@Sun.COM * Common Development and Distribution License (the "License"). 6*8803SJonathan.Haslam@Sun.COM * You may not use this file except in compliance with the License. 7*8803SJonathan.Haslam@Sun.COM * 8*8803SJonathan.Haslam@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*8803SJonathan.Haslam@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*8803SJonathan.Haslam@Sun.COM * See the License for the specific language governing permissions 11*8803SJonathan.Haslam@Sun.COM * and limitations under the License. 12*8803SJonathan.Haslam@Sun.COM * 13*8803SJonathan.Haslam@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*8803SJonathan.Haslam@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*8803SJonathan.Haslam@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*8803SJonathan.Haslam@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*8803SJonathan.Haslam@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*8803SJonathan.Haslam@Sun.COM * 19*8803SJonathan.Haslam@Sun.COM * CDDL HEADER END 20*8803SJonathan.Haslam@Sun.COM */ 21*8803SJonathan.Haslam@Sun.COM 22*8803SJonathan.Haslam@Sun.COM /* 23*8803SJonathan.Haslam@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24*8803SJonathan.Haslam@Sun.COM * Use is subject to license terms. 25*8803SJonathan.Haslam@Sun.COM */ 26*8803SJonathan.Haslam@Sun.COM 27*8803SJonathan.Haslam@Sun.COM #include <sys/errno.h> 28*8803SJonathan.Haslam@Sun.COM #include <sys/cpuvar.h> 29*8803SJonathan.Haslam@Sun.COM #include <sys/stat.h> 30*8803SJonathan.Haslam@Sun.COM #include <sys/modctl.h> 31*8803SJonathan.Haslam@Sun.COM #include <sys/cmn_err.h> 32*8803SJonathan.Haslam@Sun.COM #include <sys/ddi.h> 33*8803SJonathan.Haslam@Sun.COM #include <sys/sunddi.h> 34*8803SJonathan.Haslam@Sun.COM #include <sys/ksynch.h> 35*8803SJonathan.Haslam@Sun.COM #include <sys/conf.h> 36*8803SJonathan.Haslam@Sun.COM #include <sys/kmem.h> 37*8803SJonathan.Haslam@Sun.COM #include <sys/kcpc.h> 38*8803SJonathan.Haslam@Sun.COM #include <sys/cpc_pcbe.h> 39*8803SJonathan.Haslam@Sun.COM #include <sys/cpc_impl.h> 40*8803SJonathan.Haslam@Sun.COM #include <sys/dtrace_impl.h> 41*8803SJonathan.Haslam@Sun.COM 42*8803SJonathan.Haslam@Sun.COM /* 43*8803SJonathan.Haslam@Sun.COM * DTrace CPU Performance Counter Provider 44*8803SJonathan.Haslam@Sun.COM * --------------------------------------- 45*8803SJonathan.Haslam@Sun.COM * 46*8803SJonathan.Haslam@Sun.COM * The DTrace cpc provider allows DTrace consumers to access the CPU 47*8803SJonathan.Haslam@Sun.COM * performance counter overflow mechanism of a CPU. The configuration 48*8803SJonathan.Haslam@Sun.COM * presented in a probe specification is programmed into the performance 49*8803SJonathan.Haslam@Sun.COM * counter hardware of all available CPUs on a system. Programming the 50*8803SJonathan.Haslam@Sun.COM * hardware causes a counter on each CPU to begin counting events of the 51*8803SJonathan.Haslam@Sun.COM * given type. When the specified number of events have occurred, an overflow 52*8803SJonathan.Haslam@Sun.COM * interrupt will be generated and the probe is fired. 53*8803SJonathan.Haslam@Sun.COM * 54*8803SJonathan.Haslam@Sun.COM * The required configuration for the performance counter is encoded into 55*8803SJonathan.Haslam@Sun.COM * the probe specification and this includes the performance counter event 56*8803SJonathan.Haslam@Sun.COM * name, processor mode, overflow rate and an optional unit mask. 57*8803SJonathan.Haslam@Sun.COM * 58*8803SJonathan.Haslam@Sun.COM * Most processors provide several counters (PICs) which can count all or a 59*8803SJonathan.Haslam@Sun.COM * subset of the events available for a given CPU. However, when overflow 60*8803SJonathan.Haslam@Sun.COM * profiling is being used, not all CPUs can detect which counter generated the 61*8803SJonathan.Haslam@Sun.COM * overflow interrupt. In this case we cannot reliably determine which counter 62*8803SJonathan.Haslam@Sun.COM * overflowed and we therefore only allow such CPUs to configure one event at 63*8803SJonathan.Haslam@Sun.COM * a time. Processors that can determine the counter which overflowed are 64*8803SJonathan.Haslam@Sun.COM * allowed to program as many events at one time as possible (in theory up to 65*8803SJonathan.Haslam@Sun.COM * the number of instrumentation counters supported by that platform). 66*8803SJonathan.Haslam@Sun.COM * Therefore, multiple consumers can enable multiple probes at the same time 67*8803SJonathan.Haslam@Sun.COM * on such platforms. Platforms which cannot determine the source of an 68*8803SJonathan.Haslam@Sun.COM * overflow interrupt are only allowed to program a single event at one time. 69*8803SJonathan.Haslam@Sun.COM * 70*8803SJonathan.Haslam@Sun.COM * The performance counter hardware is made available to consumers on a 71*8803SJonathan.Haslam@Sun.COM * first-come, first-served basis. Only a finite amount of hardware resource 72*8803SJonathan.Haslam@Sun.COM * is available and, while we make every attempt to accomodate requests from 73*8803SJonathan.Haslam@Sun.COM * consumers, we must deny requests when hardware resources have been exhausted. 74*8803SJonathan.Haslam@Sun.COM * A consumer will fail to enable probes when resources are currently in use. 75*8803SJonathan.Haslam@Sun.COM * 76*8803SJonathan.Haslam@Sun.COM * The cpc provider contends for shared hardware resources along with other 77*8803SJonathan.Haslam@Sun.COM * consumers of the kernel CPU performance counter subsystem (e.g. cpustat(1M)). 78*8803SJonathan.Haslam@Sun.COM * Only one such consumer can use the performance counters at any one time and 79*8803SJonathan.Haslam@Sun.COM * counters are made available on a first-come, first-served basis. As with 80*8803SJonathan.Haslam@Sun.COM * cpustat, the cpc provider has priority over per-LWP libcpc usage (e.g. 81*8803SJonathan.Haslam@Sun.COM * cputrack(1)). Invoking the cpc provider will cause all existing per-LWP 82*8803SJonathan.Haslam@Sun.COM * counter contexts to be invalidated. 83*8803SJonathan.Haslam@Sun.COM */ 84*8803SJonathan.Haslam@Sun.COM 85*8803SJonathan.Haslam@Sun.COM typedef struct dcpc_probe { 86*8803SJonathan.Haslam@Sun.COM char dcpc_event_name[CPC_MAX_EVENT_LEN]; 87*8803SJonathan.Haslam@Sun.COM int dcpc_flag; /* flags (USER/SYS) */ 88*8803SJonathan.Haslam@Sun.COM uint32_t dcpc_ovfval; /* overflow value */ 89*8803SJonathan.Haslam@Sun.COM int64_t dcpc_umask; /* umask/emask for this event */ 90*8803SJonathan.Haslam@Sun.COM int dcpc_picno; /* pic this event is programmed in */ 91*8803SJonathan.Haslam@Sun.COM int dcpc_enabled; /* probe is actually enabled? */ 92*8803SJonathan.Haslam@Sun.COM int dcpc_disabling; /* probe is currently being disabled */ 93*8803SJonathan.Haslam@Sun.COM dtrace_id_t dcpc_id; /* probeid this request is enabling */ 94*8803SJonathan.Haslam@Sun.COM int dcpc_actv_req_idx; /* idx into dcpc_actv_reqs[] */ 95*8803SJonathan.Haslam@Sun.COM } dcpc_probe_t; 96*8803SJonathan.Haslam@Sun.COM 97*8803SJonathan.Haslam@Sun.COM static dev_info_t *dcpc_devi; 98*8803SJonathan.Haslam@Sun.COM static dtrace_provider_id_t dcpc_pid; 99*8803SJonathan.Haslam@Sun.COM static dcpc_probe_t **dcpc_actv_reqs; 100*8803SJonathan.Haslam@Sun.COM static uint32_t dcpc_enablings = 0; 101*8803SJonathan.Haslam@Sun.COM static int dcpc_ovf_mask = 0; 102*8803SJonathan.Haslam@Sun.COM static int dcpc_mult_ovf_cap = 0; 103*8803SJonathan.Haslam@Sun.COM static int dcpc_mask_type = 0; 104*8803SJonathan.Haslam@Sun.COM 105*8803SJonathan.Haslam@Sun.COM /* 106*8803SJonathan.Haslam@Sun.COM * When the dcpc provider is loaded, dcpc_min_overflow is set to either 107*8803SJonathan.Haslam@Sun.COM * DCPC_MIN_OVF_DEFAULT or the value that dcpc-min-overflow is set to in 108*8803SJonathan.Haslam@Sun.COM * the dcpc.conf file. Decrease this value to set probes with smaller 109*8803SJonathan.Haslam@Sun.COM * overflow values. Remember that very small values could render a system 110*8803SJonathan.Haslam@Sun.COM * unusable with frequently occurring events. 111*8803SJonathan.Haslam@Sun.COM */ 112*8803SJonathan.Haslam@Sun.COM #define DCPC_MIN_OVF_DEFAULT 5000 113*8803SJonathan.Haslam@Sun.COM static uint32_t dcpc_min_overflow; 114*8803SJonathan.Haslam@Sun.COM 115*8803SJonathan.Haslam@Sun.COM static int dcpc_aframes = 0; /* override for artificial frame setting */ 116*8803SJonathan.Haslam@Sun.COM #if defined(__x86) 117*8803SJonathan.Haslam@Sun.COM #define DCPC_ARTIFICIAL_FRAMES 8 118*8803SJonathan.Haslam@Sun.COM #elif defined(__sparc) 119*8803SJonathan.Haslam@Sun.COM #define DCPC_ARTIFICIAL_FRAMES 2 120*8803SJonathan.Haslam@Sun.COM #endif 121*8803SJonathan.Haslam@Sun.COM 122*8803SJonathan.Haslam@Sun.COM /* 123*8803SJonathan.Haslam@Sun.COM * Called from the platform overflow interrupt handler. 'bitmap' is a mask 124*8803SJonathan.Haslam@Sun.COM * which contains the pic(s) that have overflowed. 125*8803SJonathan.Haslam@Sun.COM */ 126*8803SJonathan.Haslam@Sun.COM static void 127*8803SJonathan.Haslam@Sun.COM dcpc_fire(uint64_t bitmap) 128*8803SJonathan.Haslam@Sun.COM { 129*8803SJonathan.Haslam@Sun.COM int i; 130*8803SJonathan.Haslam@Sun.COM 131*8803SJonathan.Haslam@Sun.COM /* 132*8803SJonathan.Haslam@Sun.COM * No counter was marked as overflowing. Shout about it and get out. 133*8803SJonathan.Haslam@Sun.COM */ 134*8803SJonathan.Haslam@Sun.COM if ((bitmap & dcpc_ovf_mask) == 0) { 135*8803SJonathan.Haslam@Sun.COM cmn_err(CE_NOTE, "dcpc_fire: no counter overflow found\n"); 136*8803SJonathan.Haslam@Sun.COM return; 137*8803SJonathan.Haslam@Sun.COM } 138*8803SJonathan.Haslam@Sun.COM 139*8803SJonathan.Haslam@Sun.COM /* 140*8803SJonathan.Haslam@Sun.COM * This is the common case of a processor that doesn't support 141*8803SJonathan.Haslam@Sun.COM * multiple overflow events. Such systems are only allowed a single 142*8803SJonathan.Haslam@Sun.COM * enabling and therefore we just look for the first entry in 143*8803SJonathan.Haslam@Sun.COM * the active request array. 144*8803SJonathan.Haslam@Sun.COM */ 145*8803SJonathan.Haslam@Sun.COM if (!dcpc_mult_ovf_cap) { 146*8803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 147*8803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] != NULL) { 148*8803SJonathan.Haslam@Sun.COM dtrace_probe(dcpc_actv_reqs[i]->dcpc_id, 149*8803SJonathan.Haslam@Sun.COM CPU->cpu_cpcprofile_pc, 150*8803SJonathan.Haslam@Sun.COM CPU->cpu_cpcprofile_upc, 0, 0, 0); 151*8803SJonathan.Haslam@Sun.COM return; 152*8803SJonathan.Haslam@Sun.COM } 153*8803SJonathan.Haslam@Sun.COM } 154*8803SJonathan.Haslam@Sun.COM return; 155*8803SJonathan.Haslam@Sun.COM } 156*8803SJonathan.Haslam@Sun.COM 157*8803SJonathan.Haslam@Sun.COM /* 158*8803SJonathan.Haslam@Sun.COM * This is a processor capable of handling multiple overflow events. 159*8803SJonathan.Haslam@Sun.COM * Iterate over the array of active requests and locate the counters 160*8803SJonathan.Haslam@Sun.COM * that overflowed (note: it is possible for more than one counter to 161*8803SJonathan.Haslam@Sun.COM * have overflowed at the same time). 162*8803SJonathan.Haslam@Sun.COM */ 163*8803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 164*8803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] != NULL && 165*8803SJonathan.Haslam@Sun.COM (bitmap & (1ULL << dcpc_actv_reqs[i]->dcpc_picno))) { 166*8803SJonathan.Haslam@Sun.COM dtrace_probe(dcpc_actv_reqs[i]->dcpc_id, 167*8803SJonathan.Haslam@Sun.COM CPU->cpu_cpcprofile_pc, 168*8803SJonathan.Haslam@Sun.COM CPU->cpu_cpcprofile_upc, 0, 0, 0); 169*8803SJonathan.Haslam@Sun.COM } 170*8803SJonathan.Haslam@Sun.COM } 171*8803SJonathan.Haslam@Sun.COM } 172*8803SJonathan.Haslam@Sun.COM 173*8803SJonathan.Haslam@Sun.COM static void 174*8803SJonathan.Haslam@Sun.COM dcpc_create_probe(dtrace_provider_id_t id, const char *probename, 175*8803SJonathan.Haslam@Sun.COM char *eventname, int64_t umask, uint32_t ovfval, char flag) 176*8803SJonathan.Haslam@Sun.COM { 177*8803SJonathan.Haslam@Sun.COM dcpc_probe_t *pp; 178*8803SJonathan.Haslam@Sun.COM int nr_frames = DCPC_ARTIFICIAL_FRAMES + dtrace_mach_aframes(); 179*8803SJonathan.Haslam@Sun.COM 180*8803SJonathan.Haslam@Sun.COM if (dcpc_aframes) 181*8803SJonathan.Haslam@Sun.COM nr_frames = dcpc_aframes; 182*8803SJonathan.Haslam@Sun.COM 183*8803SJonathan.Haslam@Sun.COM if (dtrace_probe_lookup(id, NULL, NULL, probename) != 0) 184*8803SJonathan.Haslam@Sun.COM return; 185*8803SJonathan.Haslam@Sun.COM 186*8803SJonathan.Haslam@Sun.COM pp = kmem_zalloc(sizeof (dcpc_probe_t), KM_SLEEP); 187*8803SJonathan.Haslam@Sun.COM (void) strncpy(pp->dcpc_event_name, eventname, 188*8803SJonathan.Haslam@Sun.COM sizeof (pp->dcpc_event_name) - 1); 189*8803SJonathan.Haslam@Sun.COM pp->dcpc_event_name[sizeof (pp->dcpc_event_name) - 1] = '\0'; 190*8803SJonathan.Haslam@Sun.COM pp->dcpc_flag = flag | CPC_OVF_NOTIFY_EMT; 191*8803SJonathan.Haslam@Sun.COM pp->dcpc_ovfval = ovfval; 192*8803SJonathan.Haslam@Sun.COM pp->dcpc_umask = umask; 193*8803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1; 194*8803SJonathan.Haslam@Sun.COM 195*8803SJonathan.Haslam@Sun.COM pp->dcpc_id = dtrace_probe_create(id, NULL, NULL, probename, 196*8803SJonathan.Haslam@Sun.COM nr_frames, pp); 197*8803SJonathan.Haslam@Sun.COM } 198*8803SJonathan.Haslam@Sun.COM 199*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 200*8803SJonathan.Haslam@Sun.COM static void 201*8803SJonathan.Haslam@Sun.COM dcpc_provide(void *arg, const dtrace_probedesc_t *desc) 202*8803SJonathan.Haslam@Sun.COM { 203*8803SJonathan.Haslam@Sun.COM /* 204*8803SJonathan.Haslam@Sun.COM * The format of a probe is: 205*8803SJonathan.Haslam@Sun.COM * 206*8803SJonathan.Haslam@Sun.COM * event_name-mode-{optional_umask}-overflow_rate 207*8803SJonathan.Haslam@Sun.COM * e.g. 208*8803SJonathan.Haslam@Sun.COM * DC_refill_from_system-user-0x1e-50000, or, 209*8803SJonathan.Haslam@Sun.COM * DC_refill_from_system-all-10000 210*8803SJonathan.Haslam@Sun.COM * 211*8803SJonathan.Haslam@Sun.COM */ 212*8803SJonathan.Haslam@Sun.COM char *str, *end, *p; 213*8803SJonathan.Haslam@Sun.COM int i, flag = 0; 214*8803SJonathan.Haslam@Sun.COM char event[CPC_MAX_EVENT_LEN]; 215*8803SJonathan.Haslam@Sun.COM long umask = -1, val = 0; 216*8803SJonathan.Haslam@Sun.COM size_t evlen, len; 217*8803SJonathan.Haslam@Sun.COM 218*8803SJonathan.Haslam@Sun.COM /* 219*8803SJonathan.Haslam@Sun.COM * The 'cpc' provider offers no probes by default. 220*8803SJonathan.Haslam@Sun.COM */ 221*8803SJonathan.Haslam@Sun.COM if (desc == NULL) 222*8803SJonathan.Haslam@Sun.COM return; 223*8803SJonathan.Haslam@Sun.COM 224*8803SJonathan.Haslam@Sun.COM len = strlen(desc->dtpd_name); 225*8803SJonathan.Haslam@Sun.COM p = str = kmem_alloc(len + 1, KM_SLEEP); 226*8803SJonathan.Haslam@Sun.COM (void) strcpy(str, desc->dtpd_name); 227*8803SJonathan.Haslam@Sun.COM 228*8803SJonathan.Haslam@Sun.COM /* 229*8803SJonathan.Haslam@Sun.COM * We have a poor man's strtok() going on here. Replace any hyphens 230*8803SJonathan.Haslam@Sun.COM * in the the probe name with NULL characters in order to make it 231*8803SJonathan.Haslam@Sun.COM * easy to parse the string with regular string functions. 232*8803SJonathan.Haslam@Sun.COM */ 233*8803SJonathan.Haslam@Sun.COM for (i = 0; i < len; i++) { 234*8803SJonathan.Haslam@Sun.COM if (str[i] == '-') 235*8803SJonathan.Haslam@Sun.COM str[i] = '\0'; 236*8803SJonathan.Haslam@Sun.COM } 237*8803SJonathan.Haslam@Sun.COM 238*8803SJonathan.Haslam@Sun.COM /* 239*8803SJonathan.Haslam@Sun.COM * The first part of the string must be either a platform event 240*8803SJonathan.Haslam@Sun.COM * name or a generic event name. 241*8803SJonathan.Haslam@Sun.COM */ 242*8803SJonathan.Haslam@Sun.COM evlen = strlen(p); 243*8803SJonathan.Haslam@Sun.COM (void) strncpy(event, p, CPC_MAX_EVENT_LEN - 1); 244*8803SJonathan.Haslam@Sun.COM event[CPC_MAX_EVENT_LEN - 1] = '\0'; 245*8803SJonathan.Haslam@Sun.COM 246*8803SJonathan.Haslam@Sun.COM /* 247*8803SJonathan.Haslam@Sun.COM * The next part of the name is the mode specification. Valid 248*8803SJonathan.Haslam@Sun.COM * settings are "user", "kernel" or "all". 249*8803SJonathan.Haslam@Sun.COM */ 250*8803SJonathan.Haslam@Sun.COM p += evlen + 1; 251*8803SJonathan.Haslam@Sun.COM 252*8803SJonathan.Haslam@Sun.COM if (strcmp(p, "user") == 0) 253*8803SJonathan.Haslam@Sun.COM flag |= CPC_COUNT_USER; 254*8803SJonathan.Haslam@Sun.COM else if (strcmp(p, "kernel") == 0) 255*8803SJonathan.Haslam@Sun.COM flag |= CPC_COUNT_SYSTEM; 256*8803SJonathan.Haslam@Sun.COM else if (strcmp(p, "all") == 0) 257*8803SJonathan.Haslam@Sun.COM flag |= CPC_COUNT_USER | CPC_COUNT_SYSTEM; 258*8803SJonathan.Haslam@Sun.COM else 259*8803SJonathan.Haslam@Sun.COM goto err; 260*8803SJonathan.Haslam@Sun.COM 261*8803SJonathan.Haslam@Sun.COM /* 262*8803SJonathan.Haslam@Sun.COM * Next we either have a mask specification followed by an overflow 263*8803SJonathan.Haslam@Sun.COM * rate or just an overflow rate on its own. 264*8803SJonathan.Haslam@Sun.COM */ 265*8803SJonathan.Haslam@Sun.COM p += strlen(p) + 1; 266*8803SJonathan.Haslam@Sun.COM if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { 267*8803SJonathan.Haslam@Sun.COM /* 268*8803SJonathan.Haslam@Sun.COM * A unit mask can only be specified if: 269*8803SJonathan.Haslam@Sun.COM * 1) this performance counter back end supports masks. 270*8803SJonathan.Haslam@Sun.COM * 2) the specified event is platform specific. 271*8803SJonathan.Haslam@Sun.COM * 3) a valid hex number is converted. 272*8803SJonathan.Haslam@Sun.COM * 4) no extraneous characters follow the mask specification. 273*8803SJonathan.Haslam@Sun.COM */ 274*8803SJonathan.Haslam@Sun.COM if (dcpc_mask_type != 0 && strncmp(event, "PAPI", 4) != 0 && 275*8803SJonathan.Haslam@Sun.COM ddi_strtol(p, &end, 16, &umask) == 0 && 276*8803SJonathan.Haslam@Sun.COM end == p + strlen(p)) { 277*8803SJonathan.Haslam@Sun.COM p += strlen(p) + 1; 278*8803SJonathan.Haslam@Sun.COM } else { 279*8803SJonathan.Haslam@Sun.COM goto err; 280*8803SJonathan.Haslam@Sun.COM } 281*8803SJonathan.Haslam@Sun.COM } 282*8803SJonathan.Haslam@Sun.COM 283*8803SJonathan.Haslam@Sun.COM /* 284*8803SJonathan.Haslam@Sun.COM * This final part must be an overflow value which has to be greater 285*8803SJonathan.Haslam@Sun.COM * than the minimum permissible overflow rate. 286*8803SJonathan.Haslam@Sun.COM */ 287*8803SJonathan.Haslam@Sun.COM if ((ddi_strtol(p, &end, 10, &val) != 0) || end != p + strlen(p) || 288*8803SJonathan.Haslam@Sun.COM val < dcpc_min_overflow) 289*8803SJonathan.Haslam@Sun.COM goto err; 290*8803SJonathan.Haslam@Sun.COM 291*8803SJonathan.Haslam@Sun.COM /* 292*8803SJonathan.Haslam@Sun.COM * Validate the event and create the probe. 293*8803SJonathan.Haslam@Sun.COM */ 294*8803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 295*8803SJonathan.Haslam@Sun.COM if (strstr(kcpc_list_events(i), event) != NULL) 296*8803SJonathan.Haslam@Sun.COM dcpc_create_probe(dcpc_pid, desc->dtpd_name, event, 297*8803SJonathan.Haslam@Sun.COM umask, (uint32_t)val, flag); 298*8803SJonathan.Haslam@Sun.COM } 299*8803SJonathan.Haslam@Sun.COM 300*8803SJonathan.Haslam@Sun.COM err: 301*8803SJonathan.Haslam@Sun.COM kmem_free(str, len + 1); 302*8803SJonathan.Haslam@Sun.COM } 303*8803SJonathan.Haslam@Sun.COM 304*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 305*8803SJonathan.Haslam@Sun.COM static void 306*8803SJonathan.Haslam@Sun.COM dcpc_destroy(void *arg, dtrace_id_t id, void *parg) 307*8803SJonathan.Haslam@Sun.COM { 308*8803SJonathan.Haslam@Sun.COM dcpc_probe_t *pp = parg; 309*8803SJonathan.Haslam@Sun.COM 310*8803SJonathan.Haslam@Sun.COM ASSERT(pp->dcpc_enabled == 0); 311*8803SJonathan.Haslam@Sun.COM kmem_free(pp, sizeof (dcpc_probe_t)); 312*8803SJonathan.Haslam@Sun.COM } 313*8803SJonathan.Haslam@Sun.COM 314*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 315*8803SJonathan.Haslam@Sun.COM static int 316*8803SJonathan.Haslam@Sun.COM dcpc_usermode(void *arg, dtrace_id_t id, void *parg) 317*8803SJonathan.Haslam@Sun.COM { 318*8803SJonathan.Haslam@Sun.COM return (CPU->cpu_cpcprofile_pc == 0); 319*8803SJonathan.Haslam@Sun.COM } 320*8803SJonathan.Haslam@Sun.COM 321*8803SJonathan.Haslam@Sun.COM static void 322*8803SJonathan.Haslam@Sun.COM dcpc_populate_set(cpu_t *c, dcpc_probe_t *pp, kcpc_set_t *set, int reqno) 323*8803SJonathan.Haslam@Sun.COM { 324*8803SJonathan.Haslam@Sun.COM kcpc_set_t *oset; 325*8803SJonathan.Haslam@Sun.COM int i; 326*8803SJonathan.Haslam@Sun.COM 327*8803SJonathan.Haslam@Sun.COM (void) strncpy(set->ks_req[reqno].kr_event, pp->dcpc_event_name, 328*8803SJonathan.Haslam@Sun.COM CPC_MAX_EVENT_LEN); 329*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_config = NULL; 330*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_index = reqno; 331*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_picnum = -1; 332*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_flags = pp->dcpc_flag; 333*8803SJonathan.Haslam@Sun.COM 334*8803SJonathan.Haslam@Sun.COM /* 335*8803SJonathan.Haslam@Sun.COM * If a unit mask has been specified then detect which attribute 336*8803SJonathan.Haslam@Sun.COM * the platform needs. For now, it's either "umask" or "emask". 337*8803SJonathan.Haslam@Sun.COM */ 338*8803SJonathan.Haslam@Sun.COM if (pp->dcpc_umask >= 0) { 339*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_attr = 340*8803SJonathan.Haslam@Sun.COM kmem_zalloc(sizeof (kcpc_attr_t), KM_SLEEP); 341*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_nattrs = 1; 342*8803SJonathan.Haslam@Sun.COM if (dcpc_mask_type & DCPC_UMASK) 343*8803SJonathan.Haslam@Sun.COM (void) strncpy(set->ks_req[reqno].kr_attr->ka_name, 344*8803SJonathan.Haslam@Sun.COM "umask", 5); 345*8803SJonathan.Haslam@Sun.COM else 346*8803SJonathan.Haslam@Sun.COM (void) strncpy(set->ks_req[reqno].kr_attr->ka_name, 347*8803SJonathan.Haslam@Sun.COM "emask", 5); 348*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_attr->ka_val = pp->dcpc_umask; 349*8803SJonathan.Haslam@Sun.COM } else { 350*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_attr = NULL; 351*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_nattrs = 0; 352*8803SJonathan.Haslam@Sun.COM } 353*8803SJonathan.Haslam@Sun.COM 354*8803SJonathan.Haslam@Sun.COM /* 355*8803SJonathan.Haslam@Sun.COM * If this probe is enabled, obtain its current countdown value 356*8803SJonathan.Haslam@Sun.COM * and use that. The CPUs cpc context might not exist yet if we 357*8803SJonathan.Haslam@Sun.COM * are dealing with a CPU that is just coming online. 358*8803SJonathan.Haslam@Sun.COM */ 359*8803SJonathan.Haslam@Sun.COM if (pp->dcpc_enabled && (c->cpu_cpc_ctx != NULL)) { 360*8803SJonathan.Haslam@Sun.COM oset = c->cpu_cpc_ctx->kc_set; 361*8803SJonathan.Haslam@Sun.COM 362*8803SJonathan.Haslam@Sun.COM for (i = 0; i < oset->ks_nreqs; i++) { 363*8803SJonathan.Haslam@Sun.COM if (strcmp(oset->ks_req[i].kr_event, 364*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_event) == 0) { 365*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_preset = 366*8803SJonathan.Haslam@Sun.COM *(oset->ks_req[i].kr_data); 367*8803SJonathan.Haslam@Sun.COM } 368*8803SJonathan.Haslam@Sun.COM } 369*8803SJonathan.Haslam@Sun.COM } else { 370*8803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_preset = UINT64_MAX - pp->dcpc_ovfval; 371*8803SJonathan.Haslam@Sun.COM } 372*8803SJonathan.Haslam@Sun.COM 373*8803SJonathan.Haslam@Sun.COM set->ks_nreqs++; 374*8803SJonathan.Haslam@Sun.COM } 375*8803SJonathan.Haslam@Sun.COM 376*8803SJonathan.Haslam@Sun.COM 377*8803SJonathan.Haslam@Sun.COM /* 378*8803SJonathan.Haslam@Sun.COM * Create a fresh request set for the enablings represented in the 379*8803SJonathan.Haslam@Sun.COM * 'dcpc_actv_reqs' array which contains the probes we want to be 380*8803SJonathan.Haslam@Sun.COM * in the set. This can be called for several reasons: 381*8803SJonathan.Haslam@Sun.COM * 382*8803SJonathan.Haslam@Sun.COM * 1) We are on a single or multi overflow platform and we have no 383*8803SJonathan.Haslam@Sun.COM * current events so we can just create the set and initialize it. 384*8803SJonathan.Haslam@Sun.COM * 2) We are on a multi-overflow platform and we already have one or 385*8803SJonathan.Haslam@Sun.COM * more existing events and we are adding a new enabling. Create a 386*8803SJonathan.Haslam@Sun.COM * new set and copy old requests in and then add the new request. 387*8803SJonathan.Haslam@Sun.COM * 3) We are on a multi-overflow platform and we have just removed an 388*8803SJonathan.Haslam@Sun.COM * enabling but we still have enablings whch are valid. Create a new 389*8803SJonathan.Haslam@Sun.COM * set and copy in still valid requests. 390*8803SJonathan.Haslam@Sun.COM */ 391*8803SJonathan.Haslam@Sun.COM static kcpc_set_t * 392*8803SJonathan.Haslam@Sun.COM dcpc_create_set(cpu_t *c) 393*8803SJonathan.Haslam@Sun.COM { 394*8803SJonathan.Haslam@Sun.COM int i, reqno = 0; 395*8803SJonathan.Haslam@Sun.COM int active_requests = 0; 396*8803SJonathan.Haslam@Sun.COM kcpc_set_t *set; 397*8803SJonathan.Haslam@Sun.COM 398*8803SJonathan.Haslam@Sun.COM /* 399*8803SJonathan.Haslam@Sun.COM * First get a count of the number of currently active requests. 400*8803SJonathan.Haslam@Sun.COM * Note that dcpc_actv_reqs[] should always reflect which requests 401*8803SJonathan.Haslam@Sun.COM * we want to be in the set that is to be created. It is the 402*8803SJonathan.Haslam@Sun.COM * responsibility of the caller of dcpc_create_set() to adjust that 403*8803SJonathan.Haslam@Sun.COM * array accordingly beforehand. 404*8803SJonathan.Haslam@Sun.COM */ 405*8803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 406*8803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] != NULL) 407*8803SJonathan.Haslam@Sun.COM active_requests++; 408*8803SJonathan.Haslam@Sun.COM } 409*8803SJonathan.Haslam@Sun.COM 410*8803SJonathan.Haslam@Sun.COM set = kmem_zalloc(sizeof (kcpc_set_t), KM_SLEEP); 411*8803SJonathan.Haslam@Sun.COM 412*8803SJonathan.Haslam@Sun.COM set->ks_req = 413*8803SJonathan.Haslam@Sun.COM kmem_zalloc(sizeof (kcpc_request_t) * active_requests, KM_SLEEP); 414*8803SJonathan.Haslam@Sun.COM 415*8803SJonathan.Haslam@Sun.COM set->ks_data = 416*8803SJonathan.Haslam@Sun.COM kmem_zalloc(active_requests * sizeof (uint64_t), KM_SLEEP); 417*8803SJonathan.Haslam@Sun.COM 418*8803SJonathan.Haslam@Sun.COM /* 419*8803SJonathan.Haslam@Sun.COM * Look for valid entries in the active requests array and populate 420*8803SJonathan.Haslam@Sun.COM * the request set for any entries found. 421*8803SJonathan.Haslam@Sun.COM */ 422*8803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 423*8803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] != NULL) { 424*8803SJonathan.Haslam@Sun.COM dcpc_populate_set(c, dcpc_actv_reqs[i], set, reqno); 425*8803SJonathan.Haslam@Sun.COM reqno++; 426*8803SJonathan.Haslam@Sun.COM } 427*8803SJonathan.Haslam@Sun.COM } 428*8803SJonathan.Haslam@Sun.COM 429*8803SJonathan.Haslam@Sun.COM return (set); 430*8803SJonathan.Haslam@Sun.COM } 431*8803SJonathan.Haslam@Sun.COM 432*8803SJonathan.Haslam@Sun.COM static int 433*8803SJonathan.Haslam@Sun.COM dcpc_program_cpu_event(cpu_t *c) 434*8803SJonathan.Haslam@Sun.COM { 435*8803SJonathan.Haslam@Sun.COM int i, j, subcode; 436*8803SJonathan.Haslam@Sun.COM kcpc_ctx_t *ctx, *octx; 437*8803SJonathan.Haslam@Sun.COM kcpc_set_t *set; 438*8803SJonathan.Haslam@Sun.COM 439*8803SJonathan.Haslam@Sun.COM set = dcpc_create_set(c); 440*8803SJonathan.Haslam@Sun.COM 441*8803SJonathan.Haslam@Sun.COM octx = NULL; 442*8803SJonathan.Haslam@Sun.COM set->ks_ctx = ctx = kcpc_ctx_alloc(); 443*8803SJonathan.Haslam@Sun.COM ctx->kc_set = set; 444*8803SJonathan.Haslam@Sun.COM ctx->kc_cpuid = c->cpu_id; 445*8803SJonathan.Haslam@Sun.COM 446*8803SJonathan.Haslam@Sun.COM if (kcpc_assign_reqs(set, ctx) != 0) 447*8803SJonathan.Haslam@Sun.COM goto err; 448*8803SJonathan.Haslam@Sun.COM 449*8803SJonathan.Haslam@Sun.COM if (kcpc_configure_reqs(ctx, set, &subcode) != 0) 450*8803SJonathan.Haslam@Sun.COM goto err; 451*8803SJonathan.Haslam@Sun.COM 452*8803SJonathan.Haslam@Sun.COM for (i = 0; i < set->ks_nreqs; i++) { 453*8803SJonathan.Haslam@Sun.COM for (j = 0; j < cpc_ncounters; j++) { 454*8803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[j] != NULL && 455*8803SJonathan.Haslam@Sun.COM strcmp(set->ks_req[i].kr_event, 456*8803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[j]->dcpc_event_name) == 0) { 457*8803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[j]->dcpc_picno = 458*8803SJonathan.Haslam@Sun.COM set->ks_req[i].kr_picnum; 459*8803SJonathan.Haslam@Sun.COM } 460*8803SJonathan.Haslam@Sun.COM } 461*8803SJonathan.Haslam@Sun.COM } 462*8803SJonathan.Haslam@Sun.COM 463*8803SJonathan.Haslam@Sun.COM /* 464*8803SJonathan.Haslam@Sun.COM * If we already have an active enabling then save the current cpc 465*8803SJonathan.Haslam@Sun.COM * context away. 466*8803SJonathan.Haslam@Sun.COM */ 467*8803SJonathan.Haslam@Sun.COM if (c->cpu_cpc_ctx != NULL) 468*8803SJonathan.Haslam@Sun.COM octx = c->cpu_cpc_ctx; 469*8803SJonathan.Haslam@Sun.COM 470*8803SJonathan.Haslam@Sun.COM c->cpu_cpc_ctx = ctx; 471*8803SJonathan.Haslam@Sun.COM kcpc_remote_program(c); 472*8803SJonathan.Haslam@Sun.COM 473*8803SJonathan.Haslam@Sun.COM if (octx != NULL) { 474*8803SJonathan.Haslam@Sun.COM kcpc_set_t *oset = octx->kc_set; 475*8803SJonathan.Haslam@Sun.COM kmem_free(oset->ks_data, oset->ks_nreqs * sizeof (uint64_t)); 476*8803SJonathan.Haslam@Sun.COM kcpc_free_set(oset); 477*8803SJonathan.Haslam@Sun.COM kcpc_ctx_free(octx); 478*8803SJonathan.Haslam@Sun.COM } 479*8803SJonathan.Haslam@Sun.COM 480*8803SJonathan.Haslam@Sun.COM return (0); 481*8803SJonathan.Haslam@Sun.COM 482*8803SJonathan.Haslam@Sun.COM err: 483*8803SJonathan.Haslam@Sun.COM /* 484*8803SJonathan.Haslam@Sun.COM * We failed to configure this request up so free things up and 485*8803SJonathan.Haslam@Sun.COM * get out. 486*8803SJonathan.Haslam@Sun.COM */ 487*8803SJonathan.Haslam@Sun.COM kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t)); 488*8803SJonathan.Haslam@Sun.COM kcpc_free_set(set); 489*8803SJonathan.Haslam@Sun.COM kcpc_ctx_free(ctx); 490*8803SJonathan.Haslam@Sun.COM 491*8803SJonathan.Haslam@Sun.COM return (-1); 492*8803SJonathan.Haslam@Sun.COM } 493*8803SJonathan.Haslam@Sun.COM 494*8803SJonathan.Haslam@Sun.COM static void 495*8803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(cpu_t *c) 496*8803SJonathan.Haslam@Sun.COM { 497*8803SJonathan.Haslam@Sun.COM kcpc_ctx_t *ctx; 498*8803SJonathan.Haslam@Sun.COM kcpc_set_t *set; 499*8803SJonathan.Haslam@Sun.COM 500*8803SJonathan.Haslam@Sun.COM /* 501*8803SJonathan.Haslam@Sun.COM * Leave this CPU alone if it's already offline. 502*8803SJonathan.Haslam@Sun.COM */ 503*8803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE) 504*8803SJonathan.Haslam@Sun.COM return; 505*8803SJonathan.Haslam@Sun.COM 506*8803SJonathan.Haslam@Sun.COM kcpc_remote_stop(c); 507*8803SJonathan.Haslam@Sun.COM 508*8803SJonathan.Haslam@Sun.COM ctx = c->cpu_cpc_ctx; 509*8803SJonathan.Haslam@Sun.COM set = ctx->kc_set; 510*8803SJonathan.Haslam@Sun.COM 511*8803SJonathan.Haslam@Sun.COM kcpc_free_configs(set); 512*8803SJonathan.Haslam@Sun.COM 513*8803SJonathan.Haslam@Sun.COM kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t)); 514*8803SJonathan.Haslam@Sun.COM kcpc_free_set(set); 515*8803SJonathan.Haslam@Sun.COM kcpc_ctx_free(ctx); 516*8803SJonathan.Haslam@Sun.COM c->cpu_cpc_ctx = NULL; 517*8803SJonathan.Haslam@Sun.COM } 518*8803SJonathan.Haslam@Sun.COM 519*8803SJonathan.Haslam@Sun.COM /* 520*8803SJonathan.Haslam@Sun.COM * Stop overflow interrupts being actively processed so that per-CPU 521*8803SJonathan.Haslam@Sun.COM * configuration state can be changed safely and correctly. Each CPU has a 522*8803SJonathan.Haslam@Sun.COM * dcpc interrupt state byte which is transitioned from DCPC_INTR_FREE (the 523*8803SJonathan.Haslam@Sun.COM * "free" state) to DCPC_INTR_CONFIG (the "configuration in process" state) 524*8803SJonathan.Haslam@Sun.COM * before any configuration state is changed on any CPUs. The hardware overflow 525*8803SJonathan.Haslam@Sun.COM * handler, kcpc_hw_overflow_intr(), will only process an interrupt when a 526*8803SJonathan.Haslam@Sun.COM * configuration is not in process (i.e. the state is marked as free). During 527*8803SJonathan.Haslam@Sun.COM * interrupt processing the state is set to DCPC_INTR_PROCESSING by the 528*8803SJonathan.Haslam@Sun.COM * overflow handler. 529*8803SJonathan.Haslam@Sun.COM */ 530*8803SJonathan.Haslam@Sun.COM static void 531*8803SJonathan.Haslam@Sun.COM dcpc_block_interrupts(void) 532*8803SJonathan.Haslam@Sun.COM { 533*8803SJonathan.Haslam@Sun.COM cpu_t *c; 534*8803SJonathan.Haslam@Sun.COM uint8_t *state; 535*8803SJonathan.Haslam@Sun.COM 536*8803SJonathan.Haslam@Sun.COM c = cpu_list; 537*8803SJonathan.Haslam@Sun.COM 538*8803SJonathan.Haslam@Sun.COM do { 539*8803SJonathan.Haslam@Sun.COM state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state; 540*8803SJonathan.Haslam@Sun.COM 541*8803SJonathan.Haslam@Sun.COM while (atomic_cas_8(state, DCPC_INTR_FREE, 542*8803SJonathan.Haslam@Sun.COM DCPC_INTR_CONFIG) != DCPC_INTR_FREE) 543*8803SJonathan.Haslam@Sun.COM continue; 544*8803SJonathan.Haslam@Sun.COM 545*8803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list); 546*8803SJonathan.Haslam@Sun.COM } 547*8803SJonathan.Haslam@Sun.COM 548*8803SJonathan.Haslam@Sun.COM /* 549*8803SJonathan.Haslam@Sun.COM * Set all CPUs dcpc interrupt state to DCPC_INTR_FREE to indicate that 550*8803SJonathan.Haslam@Sun.COM * overflow interrupts can be processed safely. 551*8803SJonathan.Haslam@Sun.COM */ 552*8803SJonathan.Haslam@Sun.COM static void 553*8803SJonathan.Haslam@Sun.COM dcpc_release_interrupts(void) 554*8803SJonathan.Haslam@Sun.COM { 555*8803SJonathan.Haslam@Sun.COM cpu_t *c = cpu_list; 556*8803SJonathan.Haslam@Sun.COM 557*8803SJonathan.Haslam@Sun.COM do { 558*8803SJonathan.Haslam@Sun.COM cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_FREE; 559*8803SJonathan.Haslam@Sun.COM membar_producer(); 560*8803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list); 561*8803SJonathan.Haslam@Sun.COM } 562*8803SJonathan.Haslam@Sun.COM 563*8803SJonathan.Haslam@Sun.COM /* 564*8803SJonathan.Haslam@Sun.COM * dcpc_program_event() can be called owing to a new enabling or if a multi 565*8803SJonathan.Haslam@Sun.COM * overflow platform has disabled a request but needs to program the requests 566*8803SJonathan.Haslam@Sun.COM * that are still valid. 567*8803SJonathan.Haslam@Sun.COM * 568*8803SJonathan.Haslam@Sun.COM * Every invocation of dcpc_program_event() will create a new kcpc_ctx_t 569*8803SJonathan.Haslam@Sun.COM * and a new request set which contains the new enabling and any old enablings 570*8803SJonathan.Haslam@Sun.COM * which are still valid (possible with multi-overflow platforms). 571*8803SJonathan.Haslam@Sun.COM */ 572*8803SJonathan.Haslam@Sun.COM static int 573*8803SJonathan.Haslam@Sun.COM dcpc_program_event(dcpc_probe_t *pp) 574*8803SJonathan.Haslam@Sun.COM { 575*8803SJonathan.Haslam@Sun.COM cpu_t *c; 576*8803SJonathan.Haslam@Sun.COM int ret = 0; 577*8803SJonathan.Haslam@Sun.COM 578*8803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 579*8803SJonathan.Haslam@Sun.COM 580*8803SJonathan.Haslam@Sun.COM kpreempt_disable(); 581*8803SJonathan.Haslam@Sun.COM 582*8803SJonathan.Haslam@Sun.COM dcpc_block_interrupts(); 583*8803SJonathan.Haslam@Sun.COM 584*8803SJonathan.Haslam@Sun.COM c = cpu_list; 585*8803SJonathan.Haslam@Sun.COM 586*8803SJonathan.Haslam@Sun.COM do { 587*8803SJonathan.Haslam@Sun.COM /* 588*8803SJonathan.Haslam@Sun.COM * Skip CPUs that are currently offline. 589*8803SJonathan.Haslam@Sun.COM */ 590*8803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE) 591*8803SJonathan.Haslam@Sun.COM continue; 592*8803SJonathan.Haslam@Sun.COM 593*8803SJonathan.Haslam@Sun.COM if (c->cpu_cpc_ctx != NULL) 594*8803SJonathan.Haslam@Sun.COM kcpc_remote_stop(c); 595*8803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list); 596*8803SJonathan.Haslam@Sun.COM 597*8803SJonathan.Haslam@Sun.COM dcpc_release_interrupts(); 598*8803SJonathan.Haslam@Sun.COM 599*8803SJonathan.Haslam@Sun.COM /* 600*8803SJonathan.Haslam@Sun.COM * If this enabling is being removed (in the case of a multi event 601*8803SJonathan.Haslam@Sun.COM * capable system with more than one active enabling), we can now 602*8803SJonathan.Haslam@Sun.COM * update the active request array to reflect the enablings that need 603*8803SJonathan.Haslam@Sun.COM * to be reprogrammed. 604*8803SJonathan.Haslam@Sun.COM */ 605*8803SJonathan.Haslam@Sun.COM if (pp->dcpc_disabling == 1) 606*8803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; 607*8803SJonathan.Haslam@Sun.COM 608*8803SJonathan.Haslam@Sun.COM do { 609*8803SJonathan.Haslam@Sun.COM /* 610*8803SJonathan.Haslam@Sun.COM * Skip CPUs that are currently offline. 611*8803SJonathan.Haslam@Sun.COM */ 612*8803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE) 613*8803SJonathan.Haslam@Sun.COM continue; 614*8803SJonathan.Haslam@Sun.COM 615*8803SJonathan.Haslam@Sun.COM ret = dcpc_program_cpu_event(c); 616*8803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list && ret == 0); 617*8803SJonathan.Haslam@Sun.COM 618*8803SJonathan.Haslam@Sun.COM /* 619*8803SJonathan.Haslam@Sun.COM * If dcpc_program_cpu_event() fails then it is because we couldn't 620*8803SJonathan.Haslam@Sun.COM * configure the requests in the set for the CPU and not because of 621*8803SJonathan.Haslam@Sun.COM * an error programming the hardware. If we have a failure here then 622*8803SJonathan.Haslam@Sun.COM * we assume no CPUs have been programmed in the above step as they 623*8803SJonathan.Haslam@Sun.COM * are all configured identically. 624*8803SJonathan.Haslam@Sun.COM */ 625*8803SJonathan.Haslam@Sun.COM if (ret != 0) { 626*8803SJonathan.Haslam@Sun.COM pp->dcpc_enabled = 0; 627*8803SJonathan.Haslam@Sun.COM kpreempt_enable(); 628*8803SJonathan.Haslam@Sun.COM return (-1); 629*8803SJonathan.Haslam@Sun.COM } 630*8803SJonathan.Haslam@Sun.COM 631*8803SJonathan.Haslam@Sun.COM if (pp->dcpc_disabling != 1) 632*8803SJonathan.Haslam@Sun.COM pp->dcpc_enabled = 1; 633*8803SJonathan.Haslam@Sun.COM 634*8803SJonathan.Haslam@Sun.COM kpreempt_enable(); 635*8803SJonathan.Haslam@Sun.COM 636*8803SJonathan.Haslam@Sun.COM return (0); 637*8803SJonathan.Haslam@Sun.COM } 638*8803SJonathan.Haslam@Sun.COM 639*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 640*8803SJonathan.Haslam@Sun.COM static int 641*8803SJonathan.Haslam@Sun.COM dcpc_enable(void *arg, dtrace_id_t id, void *parg) 642*8803SJonathan.Haslam@Sun.COM { 643*8803SJonathan.Haslam@Sun.COM dcpc_probe_t *pp = parg; 644*8803SJonathan.Haslam@Sun.COM int i, found = 0; 645*8803SJonathan.Haslam@Sun.COM cpu_t *c; 646*8803SJonathan.Haslam@Sun.COM 647*8803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 648*8803SJonathan.Haslam@Sun.COM 649*8803SJonathan.Haslam@Sun.COM /* 650*8803SJonathan.Haslam@Sun.COM * Bail out if the counters are being used by a libcpc consumer. 651*8803SJonathan.Haslam@Sun.COM */ 652*8803SJonathan.Haslam@Sun.COM rw_enter(&kcpc_cpuctx_lock, RW_READER); 653*8803SJonathan.Haslam@Sun.COM if (kcpc_cpuctx > 0) { 654*8803SJonathan.Haslam@Sun.COM rw_exit(&kcpc_cpuctx_lock); 655*8803SJonathan.Haslam@Sun.COM return (-1); 656*8803SJonathan.Haslam@Sun.COM } 657*8803SJonathan.Haslam@Sun.COM 658*8803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use++; 659*8803SJonathan.Haslam@Sun.COM rw_exit(&kcpc_cpuctx_lock); 660*8803SJonathan.Haslam@Sun.COM 661*8803SJonathan.Haslam@Sun.COM /* 662*8803SJonathan.Haslam@Sun.COM * Locate this enabling in the first free entry of the active 663*8803SJonathan.Haslam@Sun.COM * request array. 664*8803SJonathan.Haslam@Sun.COM */ 665*8803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 666*8803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] == NULL) { 667*8803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[i] = pp; 668*8803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = i; 669*8803SJonathan.Haslam@Sun.COM found = 1; 670*8803SJonathan.Haslam@Sun.COM break; 671*8803SJonathan.Haslam@Sun.COM } 672*8803SJonathan.Haslam@Sun.COM } 673*8803SJonathan.Haslam@Sun.COM 674*8803SJonathan.Haslam@Sun.COM /* 675*8803SJonathan.Haslam@Sun.COM * If we couldn't find a slot for this probe then there is no 676*8803SJonathan.Haslam@Sun.COM * room at the inn. 677*8803SJonathan.Haslam@Sun.COM */ 678*8803SJonathan.Haslam@Sun.COM if (!found) { 679*8803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use--; 680*8803SJonathan.Haslam@Sun.COM return (-1); 681*8803SJonathan.Haslam@Sun.COM } 682*8803SJonathan.Haslam@Sun.COM 683*8803SJonathan.Haslam@Sun.COM ASSERT(pp->dcpc_actv_req_idx >= 0); 684*8803SJonathan.Haslam@Sun.COM 685*8803SJonathan.Haslam@Sun.COM /* 686*8803SJonathan.Haslam@Sun.COM * The following must hold true if we are to (attempt to) enable 687*8803SJonathan.Haslam@Sun.COM * this request: 688*8803SJonathan.Haslam@Sun.COM * 689*8803SJonathan.Haslam@Sun.COM * 1) No enablings currently exist. We allow all platforms to 690*8803SJonathan.Haslam@Sun.COM * proceed if this is true. 691*8803SJonathan.Haslam@Sun.COM * 692*8803SJonathan.Haslam@Sun.COM * OR 693*8803SJonathan.Haslam@Sun.COM * 694*8803SJonathan.Haslam@Sun.COM * 2) If the platform is multi overflow capable and there are 695*8803SJonathan.Haslam@Sun.COM * less valid enablings than there are counters. There is no 696*8803SJonathan.Haslam@Sun.COM * guarantee that a platform can accommodate as many events as 697*8803SJonathan.Haslam@Sun.COM * it has counters for but we will at least try to program 698*8803SJonathan.Haslam@Sun.COM * up to that many requests. 699*8803SJonathan.Haslam@Sun.COM * 700*8803SJonathan.Haslam@Sun.COM * The 'dcpc_enablings' variable is implictly protected by locking 701*8803SJonathan.Haslam@Sun.COM * provided by the DTrace framework and the cpu management framework. 702*8803SJonathan.Haslam@Sun.COM */ 703*8803SJonathan.Haslam@Sun.COM if (dcpc_enablings == 0 || (dcpc_mult_ovf_cap && 704*8803SJonathan.Haslam@Sun.COM dcpc_enablings < cpc_ncounters)) { 705*8803SJonathan.Haslam@Sun.COM /* 706*8803SJonathan.Haslam@Sun.COM * Before attempting to program the first enabling we need to 707*8803SJonathan.Haslam@Sun.COM * invalidate any lwp-based contexts. 708*8803SJonathan.Haslam@Sun.COM */ 709*8803SJonathan.Haslam@Sun.COM if (dcpc_enablings == 0) 710*8803SJonathan.Haslam@Sun.COM kcpc_invalidate_all(); 711*8803SJonathan.Haslam@Sun.COM 712*8803SJonathan.Haslam@Sun.COM if (dcpc_program_event(pp) == 0) { 713*8803SJonathan.Haslam@Sun.COM dcpc_enablings++; 714*8803SJonathan.Haslam@Sun.COM return (0); 715*8803SJonathan.Haslam@Sun.COM } 716*8803SJonathan.Haslam@Sun.COM } 717*8803SJonathan.Haslam@Sun.COM 718*8803SJonathan.Haslam@Sun.COM /* 719*8803SJonathan.Haslam@Sun.COM * If active enablings existed before we failed to enable this probe 720*8803SJonathan.Haslam@Sun.COM * on a multi event capable platform then we need to restart counters 721*8803SJonathan.Haslam@Sun.COM * as they will have been stopped in the attempted configuration. The 722*8803SJonathan.Haslam@Sun.COM * context should now just contain the request prior to this failed 723*8803SJonathan.Haslam@Sun.COM * enabling. 724*8803SJonathan.Haslam@Sun.COM */ 725*8803SJonathan.Haslam@Sun.COM if (dcpc_enablings > 0 && dcpc_mult_ovf_cap) { 726*8803SJonathan.Haslam@Sun.COM c = cpu_list; 727*8803SJonathan.Haslam@Sun.COM 728*8803SJonathan.Haslam@Sun.COM ASSERT(dcpc_mult_ovf_cap == 1); 729*8803SJonathan.Haslam@Sun.COM do { 730*8803SJonathan.Haslam@Sun.COM /* 731*8803SJonathan.Haslam@Sun.COM * Skip CPUs that are currently offline. 732*8803SJonathan.Haslam@Sun.COM */ 733*8803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE) 734*8803SJonathan.Haslam@Sun.COM continue; 735*8803SJonathan.Haslam@Sun.COM 736*8803SJonathan.Haslam@Sun.COM kcpc_remote_program(c); 737*8803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list); 738*8803SJonathan.Haslam@Sun.COM } 739*8803SJonathan.Haslam@Sun.COM 740*8803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use--; 741*8803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; 742*8803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno = -1; 743*8803SJonathan.Haslam@Sun.COM 744*8803SJonathan.Haslam@Sun.COM return (-1); 745*8803SJonathan.Haslam@Sun.COM } 746*8803SJonathan.Haslam@Sun.COM 747*8803SJonathan.Haslam@Sun.COM /* 748*8803SJonathan.Haslam@Sun.COM * If only one enabling is active then remove the context and free 749*8803SJonathan.Haslam@Sun.COM * everything up. If there are multiple enablings active then remove this 750*8803SJonathan.Haslam@Sun.COM * one, its associated meta-data and re-program the hardware. 751*8803SJonathan.Haslam@Sun.COM */ 752*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 753*8803SJonathan.Haslam@Sun.COM static void 754*8803SJonathan.Haslam@Sun.COM dcpc_disable(void *arg, dtrace_id_t id, void *parg) 755*8803SJonathan.Haslam@Sun.COM { 756*8803SJonathan.Haslam@Sun.COM cpu_t *c; 757*8803SJonathan.Haslam@Sun.COM dcpc_probe_t *pp = parg; 758*8803SJonathan.Haslam@Sun.COM 759*8803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 760*8803SJonathan.Haslam@Sun.COM 761*8803SJonathan.Haslam@Sun.COM kpreempt_disable(); 762*8803SJonathan.Haslam@Sun.COM 763*8803SJonathan.Haslam@Sun.COM /* 764*8803SJonathan.Haslam@Sun.COM * This probe didn't actually make it as far as being fully enabled 765*8803SJonathan.Haslam@Sun.COM * so we needn't do anything with it. 766*8803SJonathan.Haslam@Sun.COM */ 767*8803SJonathan.Haslam@Sun.COM if (pp->dcpc_enabled == 0) { 768*8803SJonathan.Haslam@Sun.COM /* 769*8803SJonathan.Haslam@Sun.COM * If we actually allocated this request a slot in the 770*8803SJonathan.Haslam@Sun.COM * request array but failed to enabled it then remove the 771*8803SJonathan.Haslam@Sun.COM * entry in the array. 772*8803SJonathan.Haslam@Sun.COM */ 773*8803SJonathan.Haslam@Sun.COM if (pp->dcpc_actv_req_idx >= 0) { 774*8803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; 775*8803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno = 776*8803SJonathan.Haslam@Sun.COM pp->dcpc_disabling = -1; 777*8803SJonathan.Haslam@Sun.COM } 778*8803SJonathan.Haslam@Sun.COM 779*8803SJonathan.Haslam@Sun.COM kpreempt_enable(); 780*8803SJonathan.Haslam@Sun.COM return; 781*8803SJonathan.Haslam@Sun.COM } 782*8803SJonathan.Haslam@Sun.COM 783*8803SJonathan.Haslam@Sun.COM /* 784*8803SJonathan.Haslam@Sun.COM * If this is the only enabling then stop all the counters and 785*8803SJonathan.Haslam@Sun.COM * free up the meta-data. 786*8803SJonathan.Haslam@Sun.COM */ 787*8803SJonathan.Haslam@Sun.COM if (dcpc_enablings == 1) { 788*8803SJonathan.Haslam@Sun.COM ASSERT(dtrace_cpc_in_use == 1); 789*8803SJonathan.Haslam@Sun.COM 790*8803SJonathan.Haslam@Sun.COM dcpc_block_interrupts(); 791*8803SJonathan.Haslam@Sun.COM 792*8803SJonathan.Haslam@Sun.COM c = cpu_list; 793*8803SJonathan.Haslam@Sun.COM 794*8803SJonathan.Haslam@Sun.COM do { 795*8803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(c); 796*8803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list); 797*8803SJonathan.Haslam@Sun.COM 798*8803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; 799*8803SJonathan.Haslam@Sun.COM dcpc_release_interrupts(); 800*8803SJonathan.Haslam@Sun.COM } else { 801*8803SJonathan.Haslam@Sun.COM /* 802*8803SJonathan.Haslam@Sun.COM * This platform can support multiple overflow events and 803*8803SJonathan.Haslam@Sun.COM * the enabling being disabled is not the last one. Remove this 804*8803SJonathan.Haslam@Sun.COM * enabling and re-program the hardware with the new config. 805*8803SJonathan.Haslam@Sun.COM */ 806*8803SJonathan.Haslam@Sun.COM ASSERT(dcpc_mult_ovf_cap); 807*8803SJonathan.Haslam@Sun.COM ASSERT(dcpc_enablings > 1); 808*8803SJonathan.Haslam@Sun.COM 809*8803SJonathan.Haslam@Sun.COM pp->dcpc_disabling = 1; 810*8803SJonathan.Haslam@Sun.COM (void) dcpc_program_event(pp); 811*8803SJonathan.Haslam@Sun.COM } 812*8803SJonathan.Haslam@Sun.COM 813*8803SJonathan.Haslam@Sun.COM kpreempt_enable(); 814*8803SJonathan.Haslam@Sun.COM 815*8803SJonathan.Haslam@Sun.COM dcpc_enablings--; 816*8803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use--; 817*8803SJonathan.Haslam@Sun.COM pp->dcpc_enabled = 0; 818*8803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1; 819*8803SJonathan.Haslam@Sun.COM } 820*8803SJonathan.Haslam@Sun.COM 821*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 822*8803SJonathan.Haslam@Sun.COM static int 823*8803SJonathan.Haslam@Sun.COM dcpc_cpu_setup(cpu_setup_t what, processorid_t cpu, void *arg) 824*8803SJonathan.Haslam@Sun.COM { 825*8803SJonathan.Haslam@Sun.COM cpu_t *c; 826*8803SJonathan.Haslam@Sun.COM uint8_t *state; 827*8803SJonathan.Haslam@Sun.COM 828*8803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 829*8803SJonathan.Haslam@Sun.COM 830*8803SJonathan.Haslam@Sun.COM switch (what) { 831*8803SJonathan.Haslam@Sun.COM case CPU_OFF: 832*8803SJonathan.Haslam@Sun.COM /* 833*8803SJonathan.Haslam@Sun.COM * Offline CPUs are not allowed to take part so remove this 834*8803SJonathan.Haslam@Sun.COM * CPU if we are actively tracing. 835*8803SJonathan.Haslam@Sun.COM */ 836*8803SJonathan.Haslam@Sun.COM if (dtrace_cpc_in_use) { 837*8803SJonathan.Haslam@Sun.COM c = cpu_get(cpu); 838*8803SJonathan.Haslam@Sun.COM state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state; 839*8803SJonathan.Haslam@Sun.COM 840*8803SJonathan.Haslam@Sun.COM /* 841*8803SJonathan.Haslam@Sun.COM * Indicate that a configuration is in process in 842*8803SJonathan.Haslam@Sun.COM * order to stop overflow interrupts being processed 843*8803SJonathan.Haslam@Sun.COM * on this CPU while we disable it. 844*8803SJonathan.Haslam@Sun.COM */ 845*8803SJonathan.Haslam@Sun.COM while (atomic_cas_8(state, DCPC_INTR_FREE, 846*8803SJonathan.Haslam@Sun.COM DCPC_INTR_CONFIG) != DCPC_INTR_FREE) 847*8803SJonathan.Haslam@Sun.COM continue; 848*8803SJonathan.Haslam@Sun.COM 849*8803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(c); 850*8803SJonathan.Haslam@Sun.COM 851*8803SJonathan.Haslam@Sun.COM /* 852*8803SJonathan.Haslam@Sun.COM * Reset this CPUs interrupt state as the configuration 853*8803SJonathan.Haslam@Sun.COM * has ended. 854*8803SJonathan.Haslam@Sun.COM */ 855*8803SJonathan.Haslam@Sun.COM cpu_core[c->cpu_id].cpuc_dcpc_intr_state = 856*8803SJonathan.Haslam@Sun.COM DCPC_INTR_FREE; 857*8803SJonathan.Haslam@Sun.COM membar_producer(); 858*8803SJonathan.Haslam@Sun.COM } 859*8803SJonathan.Haslam@Sun.COM break; 860*8803SJonathan.Haslam@Sun.COM 861*8803SJonathan.Haslam@Sun.COM case CPU_ON: 862*8803SJonathan.Haslam@Sun.COM case CPU_SETUP: 863*8803SJonathan.Haslam@Sun.COM /* 864*8803SJonathan.Haslam@Sun.COM * This CPU is being initialized or brought online so program 865*8803SJonathan.Haslam@Sun.COM * it with the current request set if we are actively tracing. 866*8803SJonathan.Haslam@Sun.COM */ 867*8803SJonathan.Haslam@Sun.COM if (dtrace_cpc_in_use) { 868*8803SJonathan.Haslam@Sun.COM c = cpu_get(cpu); 869*8803SJonathan.Haslam@Sun.COM 870*8803SJonathan.Haslam@Sun.COM (void) dcpc_program_cpu_event(c); 871*8803SJonathan.Haslam@Sun.COM } 872*8803SJonathan.Haslam@Sun.COM break; 873*8803SJonathan.Haslam@Sun.COM 874*8803SJonathan.Haslam@Sun.COM default: 875*8803SJonathan.Haslam@Sun.COM break; 876*8803SJonathan.Haslam@Sun.COM } 877*8803SJonathan.Haslam@Sun.COM 878*8803SJonathan.Haslam@Sun.COM return (0); 879*8803SJonathan.Haslam@Sun.COM } 880*8803SJonathan.Haslam@Sun.COM 881*8803SJonathan.Haslam@Sun.COM static dtrace_pattr_t dcpc_attr = { 882*8803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 883*8803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 884*8803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 885*8803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_CPU }, 886*8803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 887*8803SJonathan.Haslam@Sun.COM }; 888*8803SJonathan.Haslam@Sun.COM 889*8803SJonathan.Haslam@Sun.COM static dtrace_pops_t dcpc_pops = { 890*8803SJonathan.Haslam@Sun.COM dcpc_provide, 891*8803SJonathan.Haslam@Sun.COM NULL, 892*8803SJonathan.Haslam@Sun.COM dcpc_enable, 893*8803SJonathan.Haslam@Sun.COM dcpc_disable, 894*8803SJonathan.Haslam@Sun.COM NULL, 895*8803SJonathan.Haslam@Sun.COM NULL, 896*8803SJonathan.Haslam@Sun.COM NULL, 897*8803SJonathan.Haslam@Sun.COM NULL, 898*8803SJonathan.Haslam@Sun.COM dcpc_usermode, 899*8803SJonathan.Haslam@Sun.COM dcpc_destroy 900*8803SJonathan.Haslam@Sun.COM }; 901*8803SJonathan.Haslam@Sun.COM 902*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 903*8803SJonathan.Haslam@Sun.COM static int 904*8803SJonathan.Haslam@Sun.COM dcpc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 905*8803SJonathan.Haslam@Sun.COM { 906*8803SJonathan.Haslam@Sun.COM return (0); 907*8803SJonathan.Haslam@Sun.COM } 908*8803SJonathan.Haslam@Sun.COM 909*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 910*8803SJonathan.Haslam@Sun.COM static int 911*8803SJonathan.Haslam@Sun.COM dcpc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 912*8803SJonathan.Haslam@Sun.COM { 913*8803SJonathan.Haslam@Sun.COM int error; 914*8803SJonathan.Haslam@Sun.COM 915*8803SJonathan.Haslam@Sun.COM switch (infocmd) { 916*8803SJonathan.Haslam@Sun.COM case DDI_INFO_DEVT2DEVINFO: 917*8803SJonathan.Haslam@Sun.COM *result = (void *)dcpc_devi; 918*8803SJonathan.Haslam@Sun.COM error = DDI_SUCCESS; 919*8803SJonathan.Haslam@Sun.COM break; 920*8803SJonathan.Haslam@Sun.COM case DDI_INFO_DEVT2INSTANCE: 921*8803SJonathan.Haslam@Sun.COM *result = (void *)0; 922*8803SJonathan.Haslam@Sun.COM error = DDI_SUCCESS; 923*8803SJonathan.Haslam@Sun.COM break; 924*8803SJonathan.Haslam@Sun.COM default: 925*8803SJonathan.Haslam@Sun.COM error = DDI_FAILURE; 926*8803SJonathan.Haslam@Sun.COM } 927*8803SJonathan.Haslam@Sun.COM return (error); 928*8803SJonathan.Haslam@Sun.COM } 929*8803SJonathan.Haslam@Sun.COM 930*8803SJonathan.Haslam@Sun.COM static int 931*8803SJonathan.Haslam@Sun.COM dcpc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 932*8803SJonathan.Haslam@Sun.COM { 933*8803SJonathan.Haslam@Sun.COM switch (cmd) { 934*8803SJonathan.Haslam@Sun.COM case DDI_DETACH: 935*8803SJonathan.Haslam@Sun.COM break; 936*8803SJonathan.Haslam@Sun.COM case DDI_SUSPEND: 937*8803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS); 938*8803SJonathan.Haslam@Sun.COM default: 939*8803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 940*8803SJonathan.Haslam@Sun.COM } 941*8803SJonathan.Haslam@Sun.COM 942*8803SJonathan.Haslam@Sun.COM if (dtrace_unregister(dcpc_pid) != 0) 943*8803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 944*8803SJonathan.Haslam@Sun.COM 945*8803SJonathan.Haslam@Sun.COM ddi_remove_minor_node(devi, NULL); 946*8803SJonathan.Haslam@Sun.COM 947*8803SJonathan.Haslam@Sun.COM mutex_enter(&cpu_lock); 948*8803SJonathan.Haslam@Sun.COM unregister_cpu_setup_func(dcpc_cpu_setup, NULL); 949*8803SJonathan.Haslam@Sun.COM mutex_exit(&cpu_lock); 950*8803SJonathan.Haslam@Sun.COM 951*8803SJonathan.Haslam@Sun.COM kmem_free(dcpc_actv_reqs, cpc_ncounters * sizeof (dcpc_probe_t *)); 952*8803SJonathan.Haslam@Sun.COM 953*8803SJonathan.Haslam@Sun.COM kcpc_unregister_dcpc(); 954*8803SJonathan.Haslam@Sun.COM 955*8803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS); 956*8803SJonathan.Haslam@Sun.COM } 957*8803SJonathan.Haslam@Sun.COM 958*8803SJonathan.Haslam@Sun.COM static int 959*8803SJonathan.Haslam@Sun.COM dcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 960*8803SJonathan.Haslam@Sun.COM { 961*8803SJonathan.Haslam@Sun.COM uint_t caps; 962*8803SJonathan.Haslam@Sun.COM char *attrs; 963*8803SJonathan.Haslam@Sun.COM 964*8803SJonathan.Haslam@Sun.COM switch (cmd) { 965*8803SJonathan.Haslam@Sun.COM case DDI_ATTACH: 966*8803SJonathan.Haslam@Sun.COM break; 967*8803SJonathan.Haslam@Sun.COM case DDI_RESUME: 968*8803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS); 969*8803SJonathan.Haslam@Sun.COM default: 970*8803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 971*8803SJonathan.Haslam@Sun.COM } 972*8803SJonathan.Haslam@Sun.COM 973*8803SJonathan.Haslam@Sun.COM if (kcpc_pcbe_loaded() == -1) 974*8803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 975*8803SJonathan.Haslam@Sun.COM 976*8803SJonathan.Haslam@Sun.COM caps = kcpc_pcbe_capabilities(); 977*8803SJonathan.Haslam@Sun.COM 978*8803SJonathan.Haslam@Sun.COM if (!(caps & CPC_CAP_OVERFLOW_INTERRUPT)) { 979*8803SJonathan.Haslam@Sun.COM cmn_err(CE_WARN, "dcpc: Counter Overflow not supported"\ 980*8803SJonathan.Haslam@Sun.COM " on this processor\n"); 981*8803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 982*8803SJonathan.Haslam@Sun.COM } 983*8803SJonathan.Haslam@Sun.COM 984*8803SJonathan.Haslam@Sun.COM if (ddi_create_minor_node(devi, "dcpc", S_IFCHR, 0, 985*8803SJonathan.Haslam@Sun.COM DDI_PSEUDO, NULL) == DDI_FAILURE || 986*8803SJonathan.Haslam@Sun.COM dtrace_register("cpc", &dcpc_attr, DTRACE_PRIV_KERNEL, 987*8803SJonathan.Haslam@Sun.COM NULL, &dcpc_pops, NULL, &dcpc_pid) != 0) { 988*8803SJonathan.Haslam@Sun.COM ddi_remove_minor_node(devi, NULL); 989*8803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 990*8803SJonathan.Haslam@Sun.COM } 991*8803SJonathan.Haslam@Sun.COM 992*8803SJonathan.Haslam@Sun.COM mutex_enter(&cpu_lock); 993*8803SJonathan.Haslam@Sun.COM register_cpu_setup_func(dcpc_cpu_setup, NULL); 994*8803SJonathan.Haslam@Sun.COM mutex_exit(&cpu_lock); 995*8803SJonathan.Haslam@Sun.COM 996*8803SJonathan.Haslam@Sun.COM dcpc_ovf_mask = (1 << cpc_ncounters) - 1; 997*8803SJonathan.Haslam@Sun.COM ASSERT(dcpc_ovf_mask != 0); 998*8803SJonathan.Haslam@Sun.COM 999*8803SJonathan.Haslam@Sun.COM if (caps & CPC_CAP_OVERFLOW_PRECISE) 1000*8803SJonathan.Haslam@Sun.COM dcpc_mult_ovf_cap = 1; 1001*8803SJonathan.Haslam@Sun.COM 1002*8803SJonathan.Haslam@Sun.COM /* 1003*8803SJonathan.Haslam@Sun.COM * Determine which, if any, mask attribute the back-end can use. 1004*8803SJonathan.Haslam@Sun.COM */ 1005*8803SJonathan.Haslam@Sun.COM attrs = kcpc_list_attrs(); 1006*8803SJonathan.Haslam@Sun.COM if (strstr(attrs, "umask") != NULL) 1007*8803SJonathan.Haslam@Sun.COM dcpc_mask_type |= DCPC_UMASK; 1008*8803SJonathan.Haslam@Sun.COM else if (strstr(attrs, "emask") != NULL) 1009*8803SJonathan.Haslam@Sun.COM dcpc_mask_type |= DCPC_EMASK; 1010*8803SJonathan.Haslam@Sun.COM 1011*8803SJonathan.Haslam@Sun.COM /* 1012*8803SJonathan.Haslam@Sun.COM * The dcpc_actv_reqs array is used to store the requests that 1013*8803SJonathan.Haslam@Sun.COM * we currently have programmed. The order of requests in this 1014*8803SJonathan.Haslam@Sun.COM * array is not necessarily the order that the event appears in 1015*8803SJonathan.Haslam@Sun.COM * the kcpc_request_t array. Once entered into a slot in the array 1016*8803SJonathan.Haslam@Sun.COM * the entry is not moved until it's removed. 1017*8803SJonathan.Haslam@Sun.COM */ 1018*8803SJonathan.Haslam@Sun.COM dcpc_actv_reqs = 1019*8803SJonathan.Haslam@Sun.COM kmem_zalloc(cpc_ncounters * sizeof (dcpc_probe_t *), KM_SLEEP); 1020*8803SJonathan.Haslam@Sun.COM 1021*8803SJonathan.Haslam@Sun.COM dcpc_min_overflow = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 1022*8803SJonathan.Haslam@Sun.COM DDI_PROP_DONTPASS, "dcpc-min-overflow", DCPC_MIN_OVF_DEFAULT); 1023*8803SJonathan.Haslam@Sun.COM 1024*8803SJonathan.Haslam@Sun.COM kcpc_register_dcpc(dcpc_fire); 1025*8803SJonathan.Haslam@Sun.COM 1026*8803SJonathan.Haslam@Sun.COM ddi_report_dev(devi); 1027*8803SJonathan.Haslam@Sun.COM dcpc_devi = devi; 1028*8803SJonathan.Haslam@Sun.COM 1029*8803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS); 1030*8803SJonathan.Haslam@Sun.COM } 1031*8803SJonathan.Haslam@Sun.COM 1032*8803SJonathan.Haslam@Sun.COM static struct cb_ops dcpc_cb_ops = { 1033*8803SJonathan.Haslam@Sun.COM dcpc_open, /* open */ 1034*8803SJonathan.Haslam@Sun.COM nodev, /* close */ 1035*8803SJonathan.Haslam@Sun.COM nulldev, /* strategy */ 1036*8803SJonathan.Haslam@Sun.COM nulldev, /* print */ 1037*8803SJonathan.Haslam@Sun.COM nodev, /* dump */ 1038*8803SJonathan.Haslam@Sun.COM nodev, /* read */ 1039*8803SJonathan.Haslam@Sun.COM nodev, /* write */ 1040*8803SJonathan.Haslam@Sun.COM nodev, /* ioctl */ 1041*8803SJonathan.Haslam@Sun.COM nodev, /* devmap */ 1042*8803SJonathan.Haslam@Sun.COM nodev, /* mmap */ 1043*8803SJonathan.Haslam@Sun.COM nodev, /* segmap */ 1044*8803SJonathan.Haslam@Sun.COM nochpoll, /* poll */ 1045*8803SJonathan.Haslam@Sun.COM ddi_prop_op, /* cb_prop_op */ 1046*8803SJonathan.Haslam@Sun.COM 0, /* streamtab */ 1047*8803SJonathan.Haslam@Sun.COM D_NEW | D_MP /* Driver compatibility flag */ 1048*8803SJonathan.Haslam@Sun.COM }; 1049*8803SJonathan.Haslam@Sun.COM 1050*8803SJonathan.Haslam@Sun.COM static struct dev_ops dcpc_ops = { 1051*8803SJonathan.Haslam@Sun.COM DEVO_REV, /* devo_rev, */ 1052*8803SJonathan.Haslam@Sun.COM 0, /* refcnt */ 1053*8803SJonathan.Haslam@Sun.COM dcpc_info, /* get_dev_info */ 1054*8803SJonathan.Haslam@Sun.COM nulldev, /* identify */ 1055*8803SJonathan.Haslam@Sun.COM nulldev, /* probe */ 1056*8803SJonathan.Haslam@Sun.COM dcpc_attach, /* attach */ 1057*8803SJonathan.Haslam@Sun.COM dcpc_detach, /* detach */ 1058*8803SJonathan.Haslam@Sun.COM nodev, /* reset */ 1059*8803SJonathan.Haslam@Sun.COM &dcpc_cb_ops, /* driver operations */ 1060*8803SJonathan.Haslam@Sun.COM NULL, /* bus operations */ 1061*8803SJonathan.Haslam@Sun.COM nodev, /* dev power */ 1062*8803SJonathan.Haslam@Sun.COM ddi_quiesce_not_needed /* quiesce */ 1063*8803SJonathan.Haslam@Sun.COM }; 1064*8803SJonathan.Haslam@Sun.COM 1065*8803SJonathan.Haslam@Sun.COM /* 1066*8803SJonathan.Haslam@Sun.COM * Module linkage information for the kernel. 1067*8803SJonathan.Haslam@Sun.COM */ 1068*8803SJonathan.Haslam@Sun.COM static struct modldrv modldrv = { 1069*8803SJonathan.Haslam@Sun.COM &mod_driverops, /* module type */ 1070*8803SJonathan.Haslam@Sun.COM "DTrace CPC Module", /* name of module */ 1071*8803SJonathan.Haslam@Sun.COM &dcpc_ops, /* driver ops */ 1072*8803SJonathan.Haslam@Sun.COM }; 1073*8803SJonathan.Haslam@Sun.COM 1074*8803SJonathan.Haslam@Sun.COM static struct modlinkage modlinkage = { 1075*8803SJonathan.Haslam@Sun.COM MODREV_1, 1076*8803SJonathan.Haslam@Sun.COM (void *)&modldrv, 1077*8803SJonathan.Haslam@Sun.COM NULL 1078*8803SJonathan.Haslam@Sun.COM }; 1079*8803SJonathan.Haslam@Sun.COM 1080*8803SJonathan.Haslam@Sun.COM int 1081*8803SJonathan.Haslam@Sun.COM _init(void) 1082*8803SJonathan.Haslam@Sun.COM { 1083*8803SJonathan.Haslam@Sun.COM return (mod_install(&modlinkage)); 1084*8803SJonathan.Haslam@Sun.COM } 1085*8803SJonathan.Haslam@Sun.COM 1086*8803SJonathan.Haslam@Sun.COM int 1087*8803SJonathan.Haslam@Sun.COM _info(struct modinfo *modinfop) 1088*8803SJonathan.Haslam@Sun.COM { 1089*8803SJonathan.Haslam@Sun.COM return (mod_info(&modlinkage, modinfop)); 1090*8803SJonathan.Haslam@Sun.COM } 1091*8803SJonathan.Haslam@Sun.COM 1092*8803SJonathan.Haslam@Sun.COM int 1093*8803SJonathan.Haslam@Sun.COM _fini(void) 1094*8803SJonathan.Haslam@Sun.COM { 1095*8803SJonathan.Haslam@Sun.COM return (mod_remove(&modlinkage)); 1096*8803SJonathan.Haslam@Sun.COM } 1097