18803SJonathan.Haslam@Sun.COM /* 28803SJonathan.Haslam@Sun.COM * CDDL HEADER START 38803SJonathan.Haslam@Sun.COM * 48803SJonathan.Haslam@Sun.COM * The contents of this file are subject to the terms of the 58803SJonathan.Haslam@Sun.COM * Common Development and Distribution License (the "License"). 68803SJonathan.Haslam@Sun.COM * You may not use this file except in compliance with the License. 78803SJonathan.Haslam@Sun.COM * 88803SJonathan.Haslam@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98803SJonathan.Haslam@Sun.COM * or http://www.opensolaris.org/os/licensing. 108803SJonathan.Haslam@Sun.COM * See the License for the specific language governing permissions 118803SJonathan.Haslam@Sun.COM * and limitations under the License. 128803SJonathan.Haslam@Sun.COM * 138803SJonathan.Haslam@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 148803SJonathan.Haslam@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158803SJonathan.Haslam@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 168803SJonathan.Haslam@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 178803SJonathan.Haslam@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 188803SJonathan.Haslam@Sun.COM * 198803SJonathan.Haslam@Sun.COM * CDDL HEADER END 208803SJonathan.Haslam@Sun.COM */ 218803SJonathan.Haslam@Sun.COM 228803SJonathan.Haslam@Sun.COM /* 238803SJonathan.Haslam@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 248803SJonathan.Haslam@Sun.COM * Use is subject to license terms. 258803SJonathan.Haslam@Sun.COM */ 268803SJonathan.Haslam@Sun.COM 278803SJonathan.Haslam@Sun.COM #include <sys/errno.h> 288803SJonathan.Haslam@Sun.COM #include <sys/cpuvar.h> 298803SJonathan.Haslam@Sun.COM #include <sys/stat.h> 308803SJonathan.Haslam@Sun.COM #include <sys/modctl.h> 318803SJonathan.Haslam@Sun.COM #include <sys/cmn_err.h> 328803SJonathan.Haslam@Sun.COM #include <sys/ddi.h> 338803SJonathan.Haslam@Sun.COM #include <sys/sunddi.h> 348803SJonathan.Haslam@Sun.COM #include <sys/ksynch.h> 358803SJonathan.Haslam@Sun.COM #include <sys/conf.h> 368803SJonathan.Haslam@Sun.COM #include <sys/kmem.h> 378803SJonathan.Haslam@Sun.COM #include <sys/kcpc.h> 38*11389SAlexander.Kolbasov@Sun.COM #include <sys/cap_util.h> 398803SJonathan.Haslam@Sun.COM #include <sys/cpc_pcbe.h> 408803SJonathan.Haslam@Sun.COM #include <sys/cpc_impl.h> 418803SJonathan.Haslam@Sun.COM #include <sys/dtrace_impl.h> 428803SJonathan.Haslam@Sun.COM 438803SJonathan.Haslam@Sun.COM /* 448803SJonathan.Haslam@Sun.COM * DTrace CPU Performance Counter Provider 458803SJonathan.Haslam@Sun.COM * --------------------------------------- 468803SJonathan.Haslam@Sun.COM * 478803SJonathan.Haslam@Sun.COM * The DTrace cpc provider allows DTrace consumers to access the CPU 488803SJonathan.Haslam@Sun.COM * performance counter overflow mechanism of a CPU. The configuration 498803SJonathan.Haslam@Sun.COM * presented in a probe specification is programmed into the performance 508803SJonathan.Haslam@Sun.COM * counter hardware of all available CPUs on a system. Programming the 518803SJonathan.Haslam@Sun.COM * hardware causes a counter on each CPU to begin counting events of the 528803SJonathan.Haslam@Sun.COM * given type. When the specified number of events have occurred, an overflow 538803SJonathan.Haslam@Sun.COM * interrupt will be generated and the probe is fired. 548803SJonathan.Haslam@Sun.COM * 558803SJonathan.Haslam@Sun.COM * The required configuration for the performance counter is encoded into 568803SJonathan.Haslam@Sun.COM * the probe specification and this includes the performance counter event 578803SJonathan.Haslam@Sun.COM * name, processor mode, overflow rate and an optional unit mask. 588803SJonathan.Haslam@Sun.COM * 598803SJonathan.Haslam@Sun.COM * Most processors provide several counters (PICs) which can count all or a 608803SJonathan.Haslam@Sun.COM * subset of the events available for a given CPU. However, when overflow 618803SJonathan.Haslam@Sun.COM * profiling is being used, not all CPUs can detect which counter generated the 628803SJonathan.Haslam@Sun.COM * overflow interrupt. In this case we cannot reliably determine which counter 638803SJonathan.Haslam@Sun.COM * overflowed and we therefore only allow such CPUs to configure one event at 648803SJonathan.Haslam@Sun.COM * a time. Processors that can determine the counter which overflowed are 658803SJonathan.Haslam@Sun.COM * allowed to program as many events at one time as possible (in theory up to 668803SJonathan.Haslam@Sun.COM * the number of instrumentation counters supported by that platform). 678803SJonathan.Haslam@Sun.COM * Therefore, multiple consumers can enable multiple probes at the same time 688803SJonathan.Haslam@Sun.COM * on such platforms. Platforms which cannot determine the source of an 698803SJonathan.Haslam@Sun.COM * overflow interrupt are only allowed to program a single event at one time. 708803SJonathan.Haslam@Sun.COM * 718803SJonathan.Haslam@Sun.COM * The performance counter hardware is made available to consumers on a 728803SJonathan.Haslam@Sun.COM * first-come, first-served basis. Only a finite amount of hardware resource 738803SJonathan.Haslam@Sun.COM * is available and, while we make every attempt to accomodate requests from 748803SJonathan.Haslam@Sun.COM * consumers, we must deny requests when hardware resources have been exhausted. 758803SJonathan.Haslam@Sun.COM * A consumer will fail to enable probes when resources are currently in use. 768803SJonathan.Haslam@Sun.COM * 778803SJonathan.Haslam@Sun.COM * The cpc provider contends for shared hardware resources along with other 788803SJonathan.Haslam@Sun.COM * consumers of the kernel CPU performance counter subsystem (e.g. cpustat(1M)). 798803SJonathan.Haslam@Sun.COM * Only one such consumer can use the performance counters at any one time and 808803SJonathan.Haslam@Sun.COM * counters are made available on a first-come, first-served basis. As with 818803SJonathan.Haslam@Sun.COM * cpustat, the cpc provider has priority over per-LWP libcpc usage (e.g. 828803SJonathan.Haslam@Sun.COM * cputrack(1)). Invoking the cpc provider will cause all existing per-LWP 838803SJonathan.Haslam@Sun.COM * counter contexts to be invalidated. 848803SJonathan.Haslam@Sun.COM */ 858803SJonathan.Haslam@Sun.COM 868803SJonathan.Haslam@Sun.COM typedef struct dcpc_probe { 878803SJonathan.Haslam@Sun.COM char dcpc_event_name[CPC_MAX_EVENT_LEN]; 888803SJonathan.Haslam@Sun.COM int dcpc_flag; /* flags (USER/SYS) */ 898803SJonathan.Haslam@Sun.COM uint32_t dcpc_ovfval; /* overflow value */ 908803SJonathan.Haslam@Sun.COM int64_t dcpc_umask; /* umask/emask for this event */ 918803SJonathan.Haslam@Sun.COM int dcpc_picno; /* pic this event is programmed in */ 928803SJonathan.Haslam@Sun.COM int dcpc_enabled; /* probe is actually enabled? */ 938803SJonathan.Haslam@Sun.COM int dcpc_disabling; /* probe is currently being disabled */ 948803SJonathan.Haslam@Sun.COM dtrace_id_t dcpc_id; /* probeid this request is enabling */ 958803SJonathan.Haslam@Sun.COM int dcpc_actv_req_idx; /* idx into dcpc_actv_reqs[] */ 968803SJonathan.Haslam@Sun.COM } dcpc_probe_t; 978803SJonathan.Haslam@Sun.COM 988803SJonathan.Haslam@Sun.COM static dev_info_t *dcpc_devi; 998803SJonathan.Haslam@Sun.COM static dtrace_provider_id_t dcpc_pid; 1008803SJonathan.Haslam@Sun.COM static dcpc_probe_t **dcpc_actv_reqs; 1018803SJonathan.Haslam@Sun.COM static uint32_t dcpc_enablings = 0; 1028803SJonathan.Haslam@Sun.COM static int dcpc_ovf_mask = 0; 1038803SJonathan.Haslam@Sun.COM static int dcpc_mult_ovf_cap = 0; 1048803SJonathan.Haslam@Sun.COM static int dcpc_mask_type = 0; 1058803SJonathan.Haslam@Sun.COM 1068803SJonathan.Haslam@Sun.COM /* 1078803SJonathan.Haslam@Sun.COM * When the dcpc provider is loaded, dcpc_min_overflow is set to either 1088803SJonathan.Haslam@Sun.COM * DCPC_MIN_OVF_DEFAULT or the value that dcpc-min-overflow is set to in 1098803SJonathan.Haslam@Sun.COM * the dcpc.conf file. Decrease this value to set probes with smaller 1108803SJonathan.Haslam@Sun.COM * overflow values. Remember that very small values could render a system 1118803SJonathan.Haslam@Sun.COM * unusable with frequently occurring events. 1128803SJonathan.Haslam@Sun.COM */ 1138803SJonathan.Haslam@Sun.COM #define DCPC_MIN_OVF_DEFAULT 5000 1148803SJonathan.Haslam@Sun.COM static uint32_t dcpc_min_overflow; 1158803SJonathan.Haslam@Sun.COM 1168803SJonathan.Haslam@Sun.COM static int dcpc_aframes = 0; /* override for artificial frame setting */ 1178803SJonathan.Haslam@Sun.COM #if defined(__x86) 1188803SJonathan.Haslam@Sun.COM #define DCPC_ARTIFICIAL_FRAMES 8 1198803SJonathan.Haslam@Sun.COM #elif defined(__sparc) 1208803SJonathan.Haslam@Sun.COM #define DCPC_ARTIFICIAL_FRAMES 2 1218803SJonathan.Haslam@Sun.COM #endif 1228803SJonathan.Haslam@Sun.COM 1238803SJonathan.Haslam@Sun.COM /* 1248803SJonathan.Haslam@Sun.COM * Called from the platform overflow interrupt handler. 'bitmap' is a mask 1258803SJonathan.Haslam@Sun.COM * which contains the pic(s) that have overflowed. 1268803SJonathan.Haslam@Sun.COM */ 1278803SJonathan.Haslam@Sun.COM static void 1288803SJonathan.Haslam@Sun.COM dcpc_fire(uint64_t bitmap) 1298803SJonathan.Haslam@Sun.COM { 1308803SJonathan.Haslam@Sun.COM int i; 1318803SJonathan.Haslam@Sun.COM 1328803SJonathan.Haslam@Sun.COM /* 1338803SJonathan.Haslam@Sun.COM * No counter was marked as overflowing. Shout about it and get out. 1348803SJonathan.Haslam@Sun.COM */ 1358803SJonathan.Haslam@Sun.COM if ((bitmap & dcpc_ovf_mask) == 0) { 1368803SJonathan.Haslam@Sun.COM cmn_err(CE_NOTE, "dcpc_fire: no counter overflow found\n"); 1378803SJonathan.Haslam@Sun.COM return; 1388803SJonathan.Haslam@Sun.COM } 1398803SJonathan.Haslam@Sun.COM 1408803SJonathan.Haslam@Sun.COM /* 1418803SJonathan.Haslam@Sun.COM * This is the common case of a processor that doesn't support 1428803SJonathan.Haslam@Sun.COM * multiple overflow events. Such systems are only allowed a single 1438803SJonathan.Haslam@Sun.COM * enabling and therefore we just look for the first entry in 1448803SJonathan.Haslam@Sun.COM * the active request array. 1458803SJonathan.Haslam@Sun.COM */ 1468803SJonathan.Haslam@Sun.COM if (!dcpc_mult_ovf_cap) { 1478803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 1488803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] != NULL) { 1498803SJonathan.Haslam@Sun.COM dtrace_probe(dcpc_actv_reqs[i]->dcpc_id, 1508803SJonathan.Haslam@Sun.COM CPU->cpu_cpcprofile_pc, 1518803SJonathan.Haslam@Sun.COM CPU->cpu_cpcprofile_upc, 0, 0, 0); 1528803SJonathan.Haslam@Sun.COM return; 1538803SJonathan.Haslam@Sun.COM } 1548803SJonathan.Haslam@Sun.COM } 1558803SJonathan.Haslam@Sun.COM return; 1568803SJonathan.Haslam@Sun.COM } 1578803SJonathan.Haslam@Sun.COM 1588803SJonathan.Haslam@Sun.COM /* 1598803SJonathan.Haslam@Sun.COM * This is a processor capable of handling multiple overflow events. 1608803SJonathan.Haslam@Sun.COM * Iterate over the array of active requests and locate the counters 1618803SJonathan.Haslam@Sun.COM * that overflowed (note: it is possible for more than one counter to 1628803SJonathan.Haslam@Sun.COM * have overflowed at the same time). 1638803SJonathan.Haslam@Sun.COM */ 1648803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 1658803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] != NULL && 1668803SJonathan.Haslam@Sun.COM (bitmap & (1ULL << dcpc_actv_reqs[i]->dcpc_picno))) { 1678803SJonathan.Haslam@Sun.COM dtrace_probe(dcpc_actv_reqs[i]->dcpc_id, 1688803SJonathan.Haslam@Sun.COM CPU->cpu_cpcprofile_pc, 1698803SJonathan.Haslam@Sun.COM CPU->cpu_cpcprofile_upc, 0, 0, 0); 1708803SJonathan.Haslam@Sun.COM } 1718803SJonathan.Haslam@Sun.COM } 1728803SJonathan.Haslam@Sun.COM } 1738803SJonathan.Haslam@Sun.COM 1748803SJonathan.Haslam@Sun.COM static void 1758803SJonathan.Haslam@Sun.COM dcpc_create_probe(dtrace_provider_id_t id, const char *probename, 1768803SJonathan.Haslam@Sun.COM char *eventname, int64_t umask, uint32_t ovfval, char flag) 1778803SJonathan.Haslam@Sun.COM { 1788803SJonathan.Haslam@Sun.COM dcpc_probe_t *pp; 1798803SJonathan.Haslam@Sun.COM int nr_frames = DCPC_ARTIFICIAL_FRAMES + dtrace_mach_aframes(); 1808803SJonathan.Haslam@Sun.COM 1818803SJonathan.Haslam@Sun.COM if (dcpc_aframes) 1828803SJonathan.Haslam@Sun.COM nr_frames = dcpc_aframes; 1838803SJonathan.Haslam@Sun.COM 1848803SJonathan.Haslam@Sun.COM if (dtrace_probe_lookup(id, NULL, NULL, probename) != 0) 1858803SJonathan.Haslam@Sun.COM return; 1868803SJonathan.Haslam@Sun.COM 1878803SJonathan.Haslam@Sun.COM pp = kmem_zalloc(sizeof (dcpc_probe_t), KM_SLEEP); 1888803SJonathan.Haslam@Sun.COM (void) strncpy(pp->dcpc_event_name, eventname, 1898803SJonathan.Haslam@Sun.COM sizeof (pp->dcpc_event_name) - 1); 1908803SJonathan.Haslam@Sun.COM pp->dcpc_event_name[sizeof (pp->dcpc_event_name) - 1] = '\0'; 1918803SJonathan.Haslam@Sun.COM pp->dcpc_flag = flag | CPC_OVF_NOTIFY_EMT; 1928803SJonathan.Haslam@Sun.COM pp->dcpc_ovfval = ovfval; 1938803SJonathan.Haslam@Sun.COM pp->dcpc_umask = umask; 1948803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1; 1958803SJonathan.Haslam@Sun.COM 1968803SJonathan.Haslam@Sun.COM pp->dcpc_id = dtrace_probe_create(id, NULL, NULL, probename, 1978803SJonathan.Haslam@Sun.COM nr_frames, pp); 1988803SJonathan.Haslam@Sun.COM } 1998803SJonathan.Haslam@Sun.COM 2008803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 2018803SJonathan.Haslam@Sun.COM static void 2028803SJonathan.Haslam@Sun.COM dcpc_provide(void *arg, const dtrace_probedesc_t *desc) 2038803SJonathan.Haslam@Sun.COM { 2048803SJonathan.Haslam@Sun.COM /* 2058803SJonathan.Haslam@Sun.COM * The format of a probe is: 2068803SJonathan.Haslam@Sun.COM * 2078803SJonathan.Haslam@Sun.COM * event_name-mode-{optional_umask}-overflow_rate 2088803SJonathan.Haslam@Sun.COM * e.g. 2098803SJonathan.Haslam@Sun.COM * DC_refill_from_system-user-0x1e-50000, or, 2108803SJonathan.Haslam@Sun.COM * DC_refill_from_system-all-10000 2118803SJonathan.Haslam@Sun.COM * 2128803SJonathan.Haslam@Sun.COM */ 2138803SJonathan.Haslam@Sun.COM char *str, *end, *p; 2148803SJonathan.Haslam@Sun.COM int i, flag = 0; 2158803SJonathan.Haslam@Sun.COM char event[CPC_MAX_EVENT_LEN]; 2168803SJonathan.Haslam@Sun.COM long umask = -1, val = 0; 2178803SJonathan.Haslam@Sun.COM size_t evlen, len; 2188803SJonathan.Haslam@Sun.COM 2198803SJonathan.Haslam@Sun.COM /* 2208803SJonathan.Haslam@Sun.COM * The 'cpc' provider offers no probes by default. 2218803SJonathan.Haslam@Sun.COM */ 2228803SJonathan.Haslam@Sun.COM if (desc == NULL) 2238803SJonathan.Haslam@Sun.COM return; 2248803SJonathan.Haslam@Sun.COM 2258803SJonathan.Haslam@Sun.COM len = strlen(desc->dtpd_name); 2268803SJonathan.Haslam@Sun.COM p = str = kmem_alloc(len + 1, KM_SLEEP); 2278803SJonathan.Haslam@Sun.COM (void) strcpy(str, desc->dtpd_name); 2288803SJonathan.Haslam@Sun.COM 2298803SJonathan.Haslam@Sun.COM /* 2308803SJonathan.Haslam@Sun.COM * We have a poor man's strtok() going on here. Replace any hyphens 2318803SJonathan.Haslam@Sun.COM * in the the probe name with NULL characters in order to make it 2328803SJonathan.Haslam@Sun.COM * easy to parse the string with regular string functions. 2338803SJonathan.Haslam@Sun.COM */ 2348803SJonathan.Haslam@Sun.COM for (i = 0; i < len; i++) { 2358803SJonathan.Haslam@Sun.COM if (str[i] == '-') 2368803SJonathan.Haslam@Sun.COM str[i] = '\0'; 2378803SJonathan.Haslam@Sun.COM } 2388803SJonathan.Haslam@Sun.COM 2398803SJonathan.Haslam@Sun.COM /* 2408803SJonathan.Haslam@Sun.COM * The first part of the string must be either a platform event 2418803SJonathan.Haslam@Sun.COM * name or a generic event name. 2428803SJonathan.Haslam@Sun.COM */ 2438803SJonathan.Haslam@Sun.COM evlen = strlen(p); 2448803SJonathan.Haslam@Sun.COM (void) strncpy(event, p, CPC_MAX_EVENT_LEN - 1); 2458803SJonathan.Haslam@Sun.COM event[CPC_MAX_EVENT_LEN - 1] = '\0'; 2468803SJonathan.Haslam@Sun.COM 2478803SJonathan.Haslam@Sun.COM /* 2488803SJonathan.Haslam@Sun.COM * The next part of the name is the mode specification. Valid 2498803SJonathan.Haslam@Sun.COM * settings are "user", "kernel" or "all". 2508803SJonathan.Haslam@Sun.COM */ 2518803SJonathan.Haslam@Sun.COM p += evlen + 1; 2528803SJonathan.Haslam@Sun.COM 2538803SJonathan.Haslam@Sun.COM if (strcmp(p, "user") == 0) 2548803SJonathan.Haslam@Sun.COM flag |= CPC_COUNT_USER; 2558803SJonathan.Haslam@Sun.COM else if (strcmp(p, "kernel") == 0) 2568803SJonathan.Haslam@Sun.COM flag |= CPC_COUNT_SYSTEM; 2578803SJonathan.Haslam@Sun.COM else if (strcmp(p, "all") == 0) 2588803SJonathan.Haslam@Sun.COM flag |= CPC_COUNT_USER | CPC_COUNT_SYSTEM; 2598803SJonathan.Haslam@Sun.COM else 2608803SJonathan.Haslam@Sun.COM goto err; 2618803SJonathan.Haslam@Sun.COM 2628803SJonathan.Haslam@Sun.COM /* 2638803SJonathan.Haslam@Sun.COM * Next we either have a mask specification followed by an overflow 2648803SJonathan.Haslam@Sun.COM * rate or just an overflow rate on its own. 2658803SJonathan.Haslam@Sun.COM */ 2668803SJonathan.Haslam@Sun.COM p += strlen(p) + 1; 2678803SJonathan.Haslam@Sun.COM if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { 2688803SJonathan.Haslam@Sun.COM /* 2698803SJonathan.Haslam@Sun.COM * A unit mask can only be specified if: 2708803SJonathan.Haslam@Sun.COM * 1) this performance counter back end supports masks. 2718803SJonathan.Haslam@Sun.COM * 2) the specified event is platform specific. 2728803SJonathan.Haslam@Sun.COM * 3) a valid hex number is converted. 2738803SJonathan.Haslam@Sun.COM * 4) no extraneous characters follow the mask specification. 2748803SJonathan.Haslam@Sun.COM */ 2758803SJonathan.Haslam@Sun.COM if (dcpc_mask_type != 0 && strncmp(event, "PAPI", 4) != 0 && 2768803SJonathan.Haslam@Sun.COM ddi_strtol(p, &end, 16, &umask) == 0 && 2778803SJonathan.Haslam@Sun.COM end == p + strlen(p)) { 2788803SJonathan.Haslam@Sun.COM p += strlen(p) + 1; 2798803SJonathan.Haslam@Sun.COM } else { 2808803SJonathan.Haslam@Sun.COM goto err; 2818803SJonathan.Haslam@Sun.COM } 2828803SJonathan.Haslam@Sun.COM } 2838803SJonathan.Haslam@Sun.COM 2848803SJonathan.Haslam@Sun.COM /* 2858803SJonathan.Haslam@Sun.COM * This final part must be an overflow value which has to be greater 2868803SJonathan.Haslam@Sun.COM * than the minimum permissible overflow rate. 2878803SJonathan.Haslam@Sun.COM */ 2888803SJonathan.Haslam@Sun.COM if ((ddi_strtol(p, &end, 10, &val) != 0) || end != p + strlen(p) || 2898803SJonathan.Haslam@Sun.COM val < dcpc_min_overflow) 2908803SJonathan.Haslam@Sun.COM goto err; 2918803SJonathan.Haslam@Sun.COM 2928803SJonathan.Haslam@Sun.COM /* 2938803SJonathan.Haslam@Sun.COM * Validate the event and create the probe. 2948803SJonathan.Haslam@Sun.COM */ 2958803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 2969397SJonathan.Haslam@Sun.COM char *events, *cp, *p, *end; 2979397SJonathan.Haslam@Sun.COM int found = 0, j; 2989397SJonathan.Haslam@Sun.COM size_t llen; 2999397SJonathan.Haslam@Sun.COM 3009397SJonathan.Haslam@Sun.COM if ((events = kcpc_list_events(i)) == NULL) 3019397SJonathan.Haslam@Sun.COM goto err; 3029397SJonathan.Haslam@Sun.COM 3039397SJonathan.Haslam@Sun.COM llen = strlen(events); 3049397SJonathan.Haslam@Sun.COM p = cp = ddi_strdup(events, KM_NOSLEEP); 3059397SJonathan.Haslam@Sun.COM end = cp + llen; 3069397SJonathan.Haslam@Sun.COM 3079397SJonathan.Haslam@Sun.COM for (j = 0; j < llen; j++) { 3089397SJonathan.Haslam@Sun.COM if (cp[j] == ',') 3099397SJonathan.Haslam@Sun.COM cp[j] = '\0'; 3109397SJonathan.Haslam@Sun.COM } 3119397SJonathan.Haslam@Sun.COM 3129397SJonathan.Haslam@Sun.COM while (p < end && found == 0) { 3139397SJonathan.Haslam@Sun.COM if (strcmp(p, event) == 0) { 3149397SJonathan.Haslam@Sun.COM dcpc_create_probe(dcpc_pid, desc->dtpd_name, 3159397SJonathan.Haslam@Sun.COM event, umask, (uint32_t)val, flag); 3169397SJonathan.Haslam@Sun.COM found = 1; 3179397SJonathan.Haslam@Sun.COM } 3189397SJonathan.Haslam@Sun.COM p += strlen(p) + 1; 3199397SJonathan.Haslam@Sun.COM } 3209397SJonathan.Haslam@Sun.COM kmem_free(cp, llen + 1); 3219397SJonathan.Haslam@Sun.COM 3229397SJonathan.Haslam@Sun.COM if (found) 3239397SJonathan.Haslam@Sun.COM break; 3248803SJonathan.Haslam@Sun.COM } 3258803SJonathan.Haslam@Sun.COM 3268803SJonathan.Haslam@Sun.COM err: 3278803SJonathan.Haslam@Sun.COM kmem_free(str, len + 1); 3288803SJonathan.Haslam@Sun.COM } 3298803SJonathan.Haslam@Sun.COM 3308803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 3318803SJonathan.Haslam@Sun.COM static void 3328803SJonathan.Haslam@Sun.COM dcpc_destroy(void *arg, dtrace_id_t id, void *parg) 3338803SJonathan.Haslam@Sun.COM { 3348803SJonathan.Haslam@Sun.COM dcpc_probe_t *pp = parg; 3358803SJonathan.Haslam@Sun.COM 3368803SJonathan.Haslam@Sun.COM ASSERT(pp->dcpc_enabled == 0); 3378803SJonathan.Haslam@Sun.COM kmem_free(pp, sizeof (dcpc_probe_t)); 3388803SJonathan.Haslam@Sun.COM } 3398803SJonathan.Haslam@Sun.COM 3408803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 3418803SJonathan.Haslam@Sun.COM static int 3428803SJonathan.Haslam@Sun.COM dcpc_usermode(void *arg, dtrace_id_t id, void *parg) 3438803SJonathan.Haslam@Sun.COM { 3448803SJonathan.Haslam@Sun.COM return (CPU->cpu_cpcprofile_pc == 0); 3458803SJonathan.Haslam@Sun.COM } 3468803SJonathan.Haslam@Sun.COM 3478803SJonathan.Haslam@Sun.COM static void 3488803SJonathan.Haslam@Sun.COM dcpc_populate_set(cpu_t *c, dcpc_probe_t *pp, kcpc_set_t *set, int reqno) 3498803SJonathan.Haslam@Sun.COM { 3508803SJonathan.Haslam@Sun.COM kcpc_set_t *oset; 3518803SJonathan.Haslam@Sun.COM int i; 3528803SJonathan.Haslam@Sun.COM 3538803SJonathan.Haslam@Sun.COM (void) strncpy(set->ks_req[reqno].kr_event, pp->dcpc_event_name, 3548803SJonathan.Haslam@Sun.COM CPC_MAX_EVENT_LEN); 3558803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_config = NULL; 3568803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_index = reqno; 3578803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_picnum = -1; 3588803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_flags = pp->dcpc_flag; 3598803SJonathan.Haslam@Sun.COM 3608803SJonathan.Haslam@Sun.COM /* 3618803SJonathan.Haslam@Sun.COM * If a unit mask has been specified then detect which attribute 3628803SJonathan.Haslam@Sun.COM * the platform needs. For now, it's either "umask" or "emask". 3638803SJonathan.Haslam@Sun.COM */ 3648803SJonathan.Haslam@Sun.COM if (pp->dcpc_umask >= 0) { 3658803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_attr = 3668803SJonathan.Haslam@Sun.COM kmem_zalloc(sizeof (kcpc_attr_t), KM_SLEEP); 3678803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_nattrs = 1; 3688803SJonathan.Haslam@Sun.COM if (dcpc_mask_type & DCPC_UMASK) 3698803SJonathan.Haslam@Sun.COM (void) strncpy(set->ks_req[reqno].kr_attr->ka_name, 3708803SJonathan.Haslam@Sun.COM "umask", 5); 3718803SJonathan.Haslam@Sun.COM else 3728803SJonathan.Haslam@Sun.COM (void) strncpy(set->ks_req[reqno].kr_attr->ka_name, 3738803SJonathan.Haslam@Sun.COM "emask", 5); 3748803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_attr->ka_val = pp->dcpc_umask; 3758803SJonathan.Haslam@Sun.COM } else { 3768803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_attr = NULL; 3778803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_nattrs = 0; 3788803SJonathan.Haslam@Sun.COM } 3798803SJonathan.Haslam@Sun.COM 3808803SJonathan.Haslam@Sun.COM /* 3818803SJonathan.Haslam@Sun.COM * If this probe is enabled, obtain its current countdown value 3828803SJonathan.Haslam@Sun.COM * and use that. The CPUs cpc context might not exist yet if we 3838803SJonathan.Haslam@Sun.COM * are dealing with a CPU that is just coming online. 3848803SJonathan.Haslam@Sun.COM */ 3858803SJonathan.Haslam@Sun.COM if (pp->dcpc_enabled && (c->cpu_cpc_ctx != NULL)) { 3868803SJonathan.Haslam@Sun.COM oset = c->cpu_cpc_ctx->kc_set; 3878803SJonathan.Haslam@Sun.COM 3888803SJonathan.Haslam@Sun.COM for (i = 0; i < oset->ks_nreqs; i++) { 3898803SJonathan.Haslam@Sun.COM if (strcmp(oset->ks_req[i].kr_event, 3908803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_event) == 0) { 3918803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_preset = 3928803SJonathan.Haslam@Sun.COM *(oset->ks_req[i].kr_data); 3938803SJonathan.Haslam@Sun.COM } 3948803SJonathan.Haslam@Sun.COM } 3958803SJonathan.Haslam@Sun.COM } else { 3968803SJonathan.Haslam@Sun.COM set->ks_req[reqno].kr_preset = UINT64_MAX - pp->dcpc_ovfval; 3978803SJonathan.Haslam@Sun.COM } 3988803SJonathan.Haslam@Sun.COM 3998803SJonathan.Haslam@Sun.COM set->ks_nreqs++; 4008803SJonathan.Haslam@Sun.COM } 4018803SJonathan.Haslam@Sun.COM 4028803SJonathan.Haslam@Sun.COM 4038803SJonathan.Haslam@Sun.COM /* 4048803SJonathan.Haslam@Sun.COM * Create a fresh request set for the enablings represented in the 4058803SJonathan.Haslam@Sun.COM * 'dcpc_actv_reqs' array which contains the probes we want to be 4068803SJonathan.Haslam@Sun.COM * in the set. This can be called for several reasons: 4078803SJonathan.Haslam@Sun.COM * 4088803SJonathan.Haslam@Sun.COM * 1) We are on a single or multi overflow platform and we have no 4098803SJonathan.Haslam@Sun.COM * current events so we can just create the set and initialize it. 4108803SJonathan.Haslam@Sun.COM * 2) We are on a multi-overflow platform and we already have one or 4118803SJonathan.Haslam@Sun.COM * more existing events and we are adding a new enabling. Create a 4128803SJonathan.Haslam@Sun.COM * new set and copy old requests in and then add the new request. 4138803SJonathan.Haslam@Sun.COM * 3) We are on a multi-overflow platform and we have just removed an 4148803SJonathan.Haslam@Sun.COM * enabling but we still have enablings whch are valid. Create a new 4158803SJonathan.Haslam@Sun.COM * set and copy in still valid requests. 4168803SJonathan.Haslam@Sun.COM */ 4178803SJonathan.Haslam@Sun.COM static kcpc_set_t * 4188803SJonathan.Haslam@Sun.COM dcpc_create_set(cpu_t *c) 4198803SJonathan.Haslam@Sun.COM { 4208803SJonathan.Haslam@Sun.COM int i, reqno = 0; 4218803SJonathan.Haslam@Sun.COM int active_requests = 0; 4228803SJonathan.Haslam@Sun.COM kcpc_set_t *set; 4238803SJonathan.Haslam@Sun.COM 4248803SJonathan.Haslam@Sun.COM /* 4258803SJonathan.Haslam@Sun.COM * First get a count of the number of currently active requests. 4268803SJonathan.Haslam@Sun.COM * Note that dcpc_actv_reqs[] should always reflect which requests 4278803SJonathan.Haslam@Sun.COM * we want to be in the set that is to be created. It is the 4288803SJonathan.Haslam@Sun.COM * responsibility of the caller of dcpc_create_set() to adjust that 4298803SJonathan.Haslam@Sun.COM * array accordingly beforehand. 4308803SJonathan.Haslam@Sun.COM */ 4318803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 4328803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] != NULL) 4338803SJonathan.Haslam@Sun.COM active_requests++; 4348803SJonathan.Haslam@Sun.COM } 4358803SJonathan.Haslam@Sun.COM 4368803SJonathan.Haslam@Sun.COM set = kmem_zalloc(sizeof (kcpc_set_t), KM_SLEEP); 4378803SJonathan.Haslam@Sun.COM 4388803SJonathan.Haslam@Sun.COM set->ks_req = 4398803SJonathan.Haslam@Sun.COM kmem_zalloc(sizeof (kcpc_request_t) * active_requests, KM_SLEEP); 4408803SJonathan.Haslam@Sun.COM 4418803SJonathan.Haslam@Sun.COM set->ks_data = 4428803SJonathan.Haslam@Sun.COM kmem_zalloc(active_requests * sizeof (uint64_t), KM_SLEEP); 4438803SJonathan.Haslam@Sun.COM 4448803SJonathan.Haslam@Sun.COM /* 4458803SJonathan.Haslam@Sun.COM * Look for valid entries in the active requests array and populate 4468803SJonathan.Haslam@Sun.COM * the request set for any entries found. 4478803SJonathan.Haslam@Sun.COM */ 4488803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 4498803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] != NULL) { 4508803SJonathan.Haslam@Sun.COM dcpc_populate_set(c, dcpc_actv_reqs[i], set, reqno); 4518803SJonathan.Haslam@Sun.COM reqno++; 4528803SJonathan.Haslam@Sun.COM } 4538803SJonathan.Haslam@Sun.COM } 4548803SJonathan.Haslam@Sun.COM 4558803SJonathan.Haslam@Sun.COM return (set); 4568803SJonathan.Haslam@Sun.COM } 4578803SJonathan.Haslam@Sun.COM 4588803SJonathan.Haslam@Sun.COM static int 4598803SJonathan.Haslam@Sun.COM dcpc_program_cpu_event(cpu_t *c) 4608803SJonathan.Haslam@Sun.COM { 4618803SJonathan.Haslam@Sun.COM int i, j, subcode; 4628803SJonathan.Haslam@Sun.COM kcpc_ctx_t *ctx, *octx; 4638803SJonathan.Haslam@Sun.COM kcpc_set_t *set; 4648803SJonathan.Haslam@Sun.COM 4658803SJonathan.Haslam@Sun.COM set = dcpc_create_set(c); 4668803SJonathan.Haslam@Sun.COM 467*11389SAlexander.Kolbasov@Sun.COM set->ks_ctx = ctx = kcpc_ctx_alloc(KM_SLEEP); 4688803SJonathan.Haslam@Sun.COM ctx->kc_set = set; 4698803SJonathan.Haslam@Sun.COM ctx->kc_cpuid = c->cpu_id; 4708803SJonathan.Haslam@Sun.COM 4718803SJonathan.Haslam@Sun.COM if (kcpc_assign_reqs(set, ctx) != 0) 4728803SJonathan.Haslam@Sun.COM goto err; 4738803SJonathan.Haslam@Sun.COM 4748803SJonathan.Haslam@Sun.COM if (kcpc_configure_reqs(ctx, set, &subcode) != 0) 4758803SJonathan.Haslam@Sun.COM goto err; 4768803SJonathan.Haslam@Sun.COM 4778803SJonathan.Haslam@Sun.COM for (i = 0; i < set->ks_nreqs; i++) { 4788803SJonathan.Haslam@Sun.COM for (j = 0; j < cpc_ncounters; j++) { 4798803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[j] != NULL && 4808803SJonathan.Haslam@Sun.COM strcmp(set->ks_req[i].kr_event, 4818803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[j]->dcpc_event_name) == 0) { 4828803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[j]->dcpc_picno = 4838803SJonathan.Haslam@Sun.COM set->ks_req[i].kr_picnum; 4848803SJonathan.Haslam@Sun.COM } 4858803SJonathan.Haslam@Sun.COM } 4868803SJonathan.Haslam@Sun.COM } 4878803SJonathan.Haslam@Sun.COM 4888803SJonathan.Haslam@Sun.COM /* 4898803SJonathan.Haslam@Sun.COM * If we already have an active enabling then save the current cpc 4908803SJonathan.Haslam@Sun.COM * context away. 4918803SJonathan.Haslam@Sun.COM */ 492*11389SAlexander.Kolbasov@Sun.COM octx = c->cpu_cpc_ctx; 4938803SJonathan.Haslam@Sun.COM 494*11389SAlexander.Kolbasov@Sun.COM kcpc_cpu_program(c, ctx); 4958803SJonathan.Haslam@Sun.COM 4968803SJonathan.Haslam@Sun.COM if (octx != NULL) { 4978803SJonathan.Haslam@Sun.COM kcpc_set_t *oset = octx->kc_set; 4988803SJonathan.Haslam@Sun.COM kmem_free(oset->ks_data, oset->ks_nreqs * sizeof (uint64_t)); 4998803SJonathan.Haslam@Sun.COM kcpc_free_set(oset); 5008803SJonathan.Haslam@Sun.COM kcpc_ctx_free(octx); 5018803SJonathan.Haslam@Sun.COM } 5028803SJonathan.Haslam@Sun.COM 5038803SJonathan.Haslam@Sun.COM return (0); 5048803SJonathan.Haslam@Sun.COM 5058803SJonathan.Haslam@Sun.COM err: 5068803SJonathan.Haslam@Sun.COM /* 5078803SJonathan.Haslam@Sun.COM * We failed to configure this request up so free things up and 5088803SJonathan.Haslam@Sun.COM * get out. 5098803SJonathan.Haslam@Sun.COM */ 5108803SJonathan.Haslam@Sun.COM kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t)); 5118803SJonathan.Haslam@Sun.COM kcpc_free_set(set); 5128803SJonathan.Haslam@Sun.COM kcpc_ctx_free(ctx); 5138803SJonathan.Haslam@Sun.COM 5148803SJonathan.Haslam@Sun.COM return (-1); 5158803SJonathan.Haslam@Sun.COM } 5168803SJonathan.Haslam@Sun.COM 5178803SJonathan.Haslam@Sun.COM static void 5188803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(cpu_t *c) 5198803SJonathan.Haslam@Sun.COM { 5208803SJonathan.Haslam@Sun.COM kcpc_ctx_t *ctx; 5218803SJonathan.Haslam@Sun.COM kcpc_set_t *set; 5228803SJonathan.Haslam@Sun.COM 5238803SJonathan.Haslam@Sun.COM /* 5248803SJonathan.Haslam@Sun.COM * Leave this CPU alone if it's already offline. 5258803SJonathan.Haslam@Sun.COM */ 5268803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE) 5278803SJonathan.Haslam@Sun.COM return; 5288803SJonathan.Haslam@Sun.COM 529*11389SAlexander.Kolbasov@Sun.COM /* 530*11389SAlexander.Kolbasov@Sun.COM * Grab CPUs CPC context before kcpc_cpu_stop() stops counters and 531*11389SAlexander.Kolbasov@Sun.COM * changes it. 532*11389SAlexander.Kolbasov@Sun.COM */ 533*11389SAlexander.Kolbasov@Sun.COM ctx = c->cpu_cpc_ctx; 5348803SJonathan.Haslam@Sun.COM 535*11389SAlexander.Kolbasov@Sun.COM kcpc_cpu_stop(c, B_FALSE); 536*11389SAlexander.Kolbasov@Sun.COM 5378803SJonathan.Haslam@Sun.COM set = ctx->kc_set; 5388803SJonathan.Haslam@Sun.COM 5398803SJonathan.Haslam@Sun.COM kcpc_free_configs(set); 5408803SJonathan.Haslam@Sun.COM 5418803SJonathan.Haslam@Sun.COM kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t)); 5428803SJonathan.Haslam@Sun.COM kcpc_free_set(set); 5438803SJonathan.Haslam@Sun.COM kcpc_ctx_free(ctx); 5448803SJonathan.Haslam@Sun.COM } 5458803SJonathan.Haslam@Sun.COM 5468803SJonathan.Haslam@Sun.COM /* 5478803SJonathan.Haslam@Sun.COM * Stop overflow interrupts being actively processed so that per-CPU 5488803SJonathan.Haslam@Sun.COM * configuration state can be changed safely and correctly. Each CPU has a 5498803SJonathan.Haslam@Sun.COM * dcpc interrupt state byte which is transitioned from DCPC_INTR_FREE (the 5508803SJonathan.Haslam@Sun.COM * "free" state) to DCPC_INTR_CONFIG (the "configuration in process" state) 5518803SJonathan.Haslam@Sun.COM * before any configuration state is changed on any CPUs. The hardware overflow 5528803SJonathan.Haslam@Sun.COM * handler, kcpc_hw_overflow_intr(), will only process an interrupt when a 5538803SJonathan.Haslam@Sun.COM * configuration is not in process (i.e. the state is marked as free). During 5548803SJonathan.Haslam@Sun.COM * interrupt processing the state is set to DCPC_INTR_PROCESSING by the 5558803SJonathan.Haslam@Sun.COM * overflow handler. 5568803SJonathan.Haslam@Sun.COM */ 5578803SJonathan.Haslam@Sun.COM static void 5588803SJonathan.Haslam@Sun.COM dcpc_block_interrupts(void) 5598803SJonathan.Haslam@Sun.COM { 5608803SJonathan.Haslam@Sun.COM cpu_t *c; 5618803SJonathan.Haslam@Sun.COM uint8_t *state; 5628803SJonathan.Haslam@Sun.COM 5638803SJonathan.Haslam@Sun.COM c = cpu_list; 5648803SJonathan.Haslam@Sun.COM 5658803SJonathan.Haslam@Sun.COM do { 5668803SJonathan.Haslam@Sun.COM state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state; 5678803SJonathan.Haslam@Sun.COM 5688803SJonathan.Haslam@Sun.COM while (atomic_cas_8(state, DCPC_INTR_FREE, 5698803SJonathan.Haslam@Sun.COM DCPC_INTR_CONFIG) != DCPC_INTR_FREE) 5708803SJonathan.Haslam@Sun.COM continue; 5718803SJonathan.Haslam@Sun.COM 5728803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list); 5738803SJonathan.Haslam@Sun.COM } 5748803SJonathan.Haslam@Sun.COM 5758803SJonathan.Haslam@Sun.COM /* 5768803SJonathan.Haslam@Sun.COM * Set all CPUs dcpc interrupt state to DCPC_INTR_FREE to indicate that 5778803SJonathan.Haslam@Sun.COM * overflow interrupts can be processed safely. 5788803SJonathan.Haslam@Sun.COM */ 5798803SJonathan.Haslam@Sun.COM static void 5808803SJonathan.Haslam@Sun.COM dcpc_release_interrupts(void) 5818803SJonathan.Haslam@Sun.COM { 5828803SJonathan.Haslam@Sun.COM cpu_t *c = cpu_list; 5838803SJonathan.Haslam@Sun.COM 5848803SJonathan.Haslam@Sun.COM do { 5858803SJonathan.Haslam@Sun.COM cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_FREE; 5868803SJonathan.Haslam@Sun.COM membar_producer(); 5878803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list); 5888803SJonathan.Haslam@Sun.COM } 5898803SJonathan.Haslam@Sun.COM 5908803SJonathan.Haslam@Sun.COM /* 5918803SJonathan.Haslam@Sun.COM * dcpc_program_event() can be called owing to a new enabling or if a multi 5928803SJonathan.Haslam@Sun.COM * overflow platform has disabled a request but needs to program the requests 5938803SJonathan.Haslam@Sun.COM * that are still valid. 5948803SJonathan.Haslam@Sun.COM * 5958803SJonathan.Haslam@Sun.COM * Every invocation of dcpc_program_event() will create a new kcpc_ctx_t 5968803SJonathan.Haslam@Sun.COM * and a new request set which contains the new enabling and any old enablings 5978803SJonathan.Haslam@Sun.COM * which are still valid (possible with multi-overflow platforms). 5988803SJonathan.Haslam@Sun.COM */ 5998803SJonathan.Haslam@Sun.COM static int 6008803SJonathan.Haslam@Sun.COM dcpc_program_event(dcpc_probe_t *pp) 6018803SJonathan.Haslam@Sun.COM { 6028803SJonathan.Haslam@Sun.COM cpu_t *c; 6038803SJonathan.Haslam@Sun.COM int ret = 0; 6048803SJonathan.Haslam@Sun.COM 6058803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 6068803SJonathan.Haslam@Sun.COM 6078803SJonathan.Haslam@Sun.COM kpreempt_disable(); 6088803SJonathan.Haslam@Sun.COM 6098803SJonathan.Haslam@Sun.COM dcpc_block_interrupts(); 6108803SJonathan.Haslam@Sun.COM 6118803SJonathan.Haslam@Sun.COM c = cpu_list; 6128803SJonathan.Haslam@Sun.COM 6138803SJonathan.Haslam@Sun.COM do { 6148803SJonathan.Haslam@Sun.COM /* 6158803SJonathan.Haslam@Sun.COM * Skip CPUs that are currently offline. 6168803SJonathan.Haslam@Sun.COM */ 6178803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE) 6188803SJonathan.Haslam@Sun.COM continue; 6198803SJonathan.Haslam@Sun.COM 620*11389SAlexander.Kolbasov@Sun.COM /* 621*11389SAlexander.Kolbasov@Sun.COM * Stop counters but preserve existing DTrace CPC context 622*11389SAlexander.Kolbasov@Sun.COM * if there is one. 623*11389SAlexander.Kolbasov@Sun.COM * 624*11389SAlexander.Kolbasov@Sun.COM * If we come here when the first event is programmed for a CPU, 625*11389SAlexander.Kolbasov@Sun.COM * there should be no DTrace CPC context installed. In this 626*11389SAlexander.Kolbasov@Sun.COM * case, kcpc_cpu_stop() will ensure that there is no other 627*11389SAlexander.Kolbasov@Sun.COM * context on the CPU. 628*11389SAlexander.Kolbasov@Sun.COM * 629*11389SAlexander.Kolbasov@Sun.COM * If we add new enabling to the original one, the CPU should 630*11389SAlexander.Kolbasov@Sun.COM * have the old DTrace CPC context which we need to keep around 631*11389SAlexander.Kolbasov@Sun.COM * since dcpc_program_event() will add to it. 632*11389SAlexander.Kolbasov@Sun.COM */ 6338803SJonathan.Haslam@Sun.COM if (c->cpu_cpc_ctx != NULL) 634*11389SAlexander.Kolbasov@Sun.COM kcpc_cpu_stop(c, B_TRUE); 6358803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list); 6368803SJonathan.Haslam@Sun.COM 6378803SJonathan.Haslam@Sun.COM dcpc_release_interrupts(); 6388803SJonathan.Haslam@Sun.COM 6398803SJonathan.Haslam@Sun.COM /* 6408803SJonathan.Haslam@Sun.COM * If this enabling is being removed (in the case of a multi event 6418803SJonathan.Haslam@Sun.COM * capable system with more than one active enabling), we can now 6428803SJonathan.Haslam@Sun.COM * update the active request array to reflect the enablings that need 6438803SJonathan.Haslam@Sun.COM * to be reprogrammed. 6448803SJonathan.Haslam@Sun.COM */ 6458803SJonathan.Haslam@Sun.COM if (pp->dcpc_disabling == 1) 6468803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; 6478803SJonathan.Haslam@Sun.COM 6488803SJonathan.Haslam@Sun.COM do { 6498803SJonathan.Haslam@Sun.COM /* 6508803SJonathan.Haslam@Sun.COM * Skip CPUs that are currently offline. 6518803SJonathan.Haslam@Sun.COM */ 6528803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE) 6538803SJonathan.Haslam@Sun.COM continue; 6548803SJonathan.Haslam@Sun.COM 6558803SJonathan.Haslam@Sun.COM ret = dcpc_program_cpu_event(c); 6568803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list && ret == 0); 6578803SJonathan.Haslam@Sun.COM 6588803SJonathan.Haslam@Sun.COM /* 6598803SJonathan.Haslam@Sun.COM * If dcpc_program_cpu_event() fails then it is because we couldn't 6608803SJonathan.Haslam@Sun.COM * configure the requests in the set for the CPU and not because of 6618803SJonathan.Haslam@Sun.COM * an error programming the hardware. If we have a failure here then 6628803SJonathan.Haslam@Sun.COM * we assume no CPUs have been programmed in the above step as they 6638803SJonathan.Haslam@Sun.COM * are all configured identically. 6648803SJonathan.Haslam@Sun.COM */ 6658803SJonathan.Haslam@Sun.COM if (ret != 0) { 6668803SJonathan.Haslam@Sun.COM pp->dcpc_enabled = 0; 6678803SJonathan.Haslam@Sun.COM kpreempt_enable(); 6688803SJonathan.Haslam@Sun.COM return (-1); 6698803SJonathan.Haslam@Sun.COM } 6708803SJonathan.Haslam@Sun.COM 6718803SJonathan.Haslam@Sun.COM if (pp->dcpc_disabling != 1) 6728803SJonathan.Haslam@Sun.COM pp->dcpc_enabled = 1; 6738803SJonathan.Haslam@Sun.COM 6748803SJonathan.Haslam@Sun.COM kpreempt_enable(); 6758803SJonathan.Haslam@Sun.COM 6768803SJonathan.Haslam@Sun.COM return (0); 6778803SJonathan.Haslam@Sun.COM } 6788803SJonathan.Haslam@Sun.COM 6798803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 6808803SJonathan.Haslam@Sun.COM static int 6818803SJonathan.Haslam@Sun.COM dcpc_enable(void *arg, dtrace_id_t id, void *parg) 6828803SJonathan.Haslam@Sun.COM { 6838803SJonathan.Haslam@Sun.COM dcpc_probe_t *pp = parg; 6848803SJonathan.Haslam@Sun.COM int i, found = 0; 6858803SJonathan.Haslam@Sun.COM cpu_t *c; 6868803SJonathan.Haslam@Sun.COM 6878803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 6888803SJonathan.Haslam@Sun.COM 6898803SJonathan.Haslam@Sun.COM /* 6908803SJonathan.Haslam@Sun.COM * Bail out if the counters are being used by a libcpc consumer. 6918803SJonathan.Haslam@Sun.COM */ 6928803SJonathan.Haslam@Sun.COM rw_enter(&kcpc_cpuctx_lock, RW_READER); 6938803SJonathan.Haslam@Sun.COM if (kcpc_cpuctx > 0) { 6948803SJonathan.Haslam@Sun.COM rw_exit(&kcpc_cpuctx_lock); 6958803SJonathan.Haslam@Sun.COM return (-1); 6968803SJonathan.Haslam@Sun.COM } 6978803SJonathan.Haslam@Sun.COM 6988803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use++; 6998803SJonathan.Haslam@Sun.COM rw_exit(&kcpc_cpuctx_lock); 7008803SJonathan.Haslam@Sun.COM 7018803SJonathan.Haslam@Sun.COM /* 7028803SJonathan.Haslam@Sun.COM * Locate this enabling in the first free entry of the active 7038803SJonathan.Haslam@Sun.COM * request array. 7048803SJonathan.Haslam@Sun.COM */ 7058803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) { 7068803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] == NULL) { 7078803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[i] = pp; 7088803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = i; 7098803SJonathan.Haslam@Sun.COM found = 1; 7108803SJonathan.Haslam@Sun.COM break; 7118803SJonathan.Haslam@Sun.COM } 7128803SJonathan.Haslam@Sun.COM } 7138803SJonathan.Haslam@Sun.COM 7148803SJonathan.Haslam@Sun.COM /* 7158803SJonathan.Haslam@Sun.COM * If we couldn't find a slot for this probe then there is no 7168803SJonathan.Haslam@Sun.COM * room at the inn. 7178803SJonathan.Haslam@Sun.COM */ 7188803SJonathan.Haslam@Sun.COM if (!found) { 7198803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use--; 7208803SJonathan.Haslam@Sun.COM return (-1); 7218803SJonathan.Haslam@Sun.COM } 7228803SJonathan.Haslam@Sun.COM 7238803SJonathan.Haslam@Sun.COM ASSERT(pp->dcpc_actv_req_idx >= 0); 7248803SJonathan.Haslam@Sun.COM 7258803SJonathan.Haslam@Sun.COM /* 726*11389SAlexander.Kolbasov@Sun.COM * DTrace is taking over CPC contexts, so stop collecting 727*11389SAlexander.Kolbasov@Sun.COM * capacity/utilization data for all CPUs. 728*11389SAlexander.Kolbasov@Sun.COM */ 729*11389SAlexander.Kolbasov@Sun.COM if (dtrace_cpc_in_use == 1) 730*11389SAlexander.Kolbasov@Sun.COM cu_disable(); 731*11389SAlexander.Kolbasov@Sun.COM 732*11389SAlexander.Kolbasov@Sun.COM /* 7338803SJonathan.Haslam@Sun.COM * The following must hold true if we are to (attempt to) enable 7348803SJonathan.Haslam@Sun.COM * this request: 7358803SJonathan.Haslam@Sun.COM * 7368803SJonathan.Haslam@Sun.COM * 1) No enablings currently exist. We allow all platforms to 7378803SJonathan.Haslam@Sun.COM * proceed if this is true. 7388803SJonathan.Haslam@Sun.COM * 7398803SJonathan.Haslam@Sun.COM * OR 7408803SJonathan.Haslam@Sun.COM * 7418803SJonathan.Haslam@Sun.COM * 2) If the platform is multi overflow capable and there are 7428803SJonathan.Haslam@Sun.COM * less valid enablings than there are counters. There is no 7438803SJonathan.Haslam@Sun.COM * guarantee that a platform can accommodate as many events as 7448803SJonathan.Haslam@Sun.COM * it has counters for but we will at least try to program 7458803SJonathan.Haslam@Sun.COM * up to that many requests. 7468803SJonathan.Haslam@Sun.COM * 7478803SJonathan.Haslam@Sun.COM * The 'dcpc_enablings' variable is implictly protected by locking 7488803SJonathan.Haslam@Sun.COM * provided by the DTrace framework and the cpu management framework. 7498803SJonathan.Haslam@Sun.COM */ 7508803SJonathan.Haslam@Sun.COM if (dcpc_enablings == 0 || (dcpc_mult_ovf_cap && 7518803SJonathan.Haslam@Sun.COM dcpc_enablings < cpc_ncounters)) { 7528803SJonathan.Haslam@Sun.COM /* 7538803SJonathan.Haslam@Sun.COM * Before attempting to program the first enabling we need to 7548803SJonathan.Haslam@Sun.COM * invalidate any lwp-based contexts. 7558803SJonathan.Haslam@Sun.COM */ 7568803SJonathan.Haslam@Sun.COM if (dcpc_enablings == 0) 7578803SJonathan.Haslam@Sun.COM kcpc_invalidate_all(); 7588803SJonathan.Haslam@Sun.COM 7598803SJonathan.Haslam@Sun.COM if (dcpc_program_event(pp) == 0) { 7608803SJonathan.Haslam@Sun.COM dcpc_enablings++; 7618803SJonathan.Haslam@Sun.COM return (0); 7628803SJonathan.Haslam@Sun.COM } 7638803SJonathan.Haslam@Sun.COM } 7648803SJonathan.Haslam@Sun.COM 7658803SJonathan.Haslam@Sun.COM /* 7668803SJonathan.Haslam@Sun.COM * If active enablings existed before we failed to enable this probe 7678803SJonathan.Haslam@Sun.COM * on a multi event capable platform then we need to restart counters 7688803SJonathan.Haslam@Sun.COM * as they will have been stopped in the attempted configuration. The 7698803SJonathan.Haslam@Sun.COM * context should now just contain the request prior to this failed 7708803SJonathan.Haslam@Sun.COM * enabling. 7718803SJonathan.Haslam@Sun.COM */ 7728803SJonathan.Haslam@Sun.COM if (dcpc_enablings > 0 && dcpc_mult_ovf_cap) { 7738803SJonathan.Haslam@Sun.COM c = cpu_list; 7748803SJonathan.Haslam@Sun.COM 7758803SJonathan.Haslam@Sun.COM ASSERT(dcpc_mult_ovf_cap == 1); 7768803SJonathan.Haslam@Sun.COM do { 7778803SJonathan.Haslam@Sun.COM /* 7788803SJonathan.Haslam@Sun.COM * Skip CPUs that are currently offline. 7798803SJonathan.Haslam@Sun.COM */ 7808803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE) 7818803SJonathan.Haslam@Sun.COM continue; 7828803SJonathan.Haslam@Sun.COM 783*11389SAlexander.Kolbasov@Sun.COM kcpc_cpu_program(c, c->cpu_cpc_ctx); 7848803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list); 7858803SJonathan.Haslam@Sun.COM } 7868803SJonathan.Haslam@Sun.COM 7878803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use--; 7888803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; 7898803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno = -1; 7908803SJonathan.Haslam@Sun.COM 791*11389SAlexander.Kolbasov@Sun.COM /* 792*11389SAlexander.Kolbasov@Sun.COM * If all probes are removed, enable capacity/utilization data 793*11389SAlexander.Kolbasov@Sun.COM * collection for every CPU. 794*11389SAlexander.Kolbasov@Sun.COM */ 795*11389SAlexander.Kolbasov@Sun.COM if (dtrace_cpc_in_use == 0) 796*11389SAlexander.Kolbasov@Sun.COM cu_enable(); 797*11389SAlexander.Kolbasov@Sun.COM 7988803SJonathan.Haslam@Sun.COM return (-1); 7998803SJonathan.Haslam@Sun.COM } 8008803SJonathan.Haslam@Sun.COM 8018803SJonathan.Haslam@Sun.COM /* 8028803SJonathan.Haslam@Sun.COM * If only one enabling is active then remove the context and free 8038803SJonathan.Haslam@Sun.COM * everything up. If there are multiple enablings active then remove this 8048803SJonathan.Haslam@Sun.COM * one, its associated meta-data and re-program the hardware. 8058803SJonathan.Haslam@Sun.COM */ 8068803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 8078803SJonathan.Haslam@Sun.COM static void 8088803SJonathan.Haslam@Sun.COM dcpc_disable(void *arg, dtrace_id_t id, void *parg) 8098803SJonathan.Haslam@Sun.COM { 8108803SJonathan.Haslam@Sun.COM cpu_t *c; 8118803SJonathan.Haslam@Sun.COM dcpc_probe_t *pp = parg; 8128803SJonathan.Haslam@Sun.COM 8138803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 8148803SJonathan.Haslam@Sun.COM 8158803SJonathan.Haslam@Sun.COM kpreempt_disable(); 8168803SJonathan.Haslam@Sun.COM 8178803SJonathan.Haslam@Sun.COM /* 8188803SJonathan.Haslam@Sun.COM * This probe didn't actually make it as far as being fully enabled 8198803SJonathan.Haslam@Sun.COM * so we needn't do anything with it. 8208803SJonathan.Haslam@Sun.COM */ 8218803SJonathan.Haslam@Sun.COM if (pp->dcpc_enabled == 0) { 8228803SJonathan.Haslam@Sun.COM /* 8238803SJonathan.Haslam@Sun.COM * If we actually allocated this request a slot in the 8248803SJonathan.Haslam@Sun.COM * request array but failed to enabled it then remove the 8258803SJonathan.Haslam@Sun.COM * entry in the array. 8268803SJonathan.Haslam@Sun.COM */ 8278803SJonathan.Haslam@Sun.COM if (pp->dcpc_actv_req_idx >= 0) { 8288803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; 8298803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno = 8308803SJonathan.Haslam@Sun.COM pp->dcpc_disabling = -1; 8318803SJonathan.Haslam@Sun.COM } 8328803SJonathan.Haslam@Sun.COM 8338803SJonathan.Haslam@Sun.COM kpreempt_enable(); 8348803SJonathan.Haslam@Sun.COM return; 8358803SJonathan.Haslam@Sun.COM } 8368803SJonathan.Haslam@Sun.COM 8378803SJonathan.Haslam@Sun.COM /* 8388803SJonathan.Haslam@Sun.COM * If this is the only enabling then stop all the counters and 8398803SJonathan.Haslam@Sun.COM * free up the meta-data. 8408803SJonathan.Haslam@Sun.COM */ 8418803SJonathan.Haslam@Sun.COM if (dcpc_enablings == 1) { 8428803SJonathan.Haslam@Sun.COM ASSERT(dtrace_cpc_in_use == 1); 8438803SJonathan.Haslam@Sun.COM 8448803SJonathan.Haslam@Sun.COM dcpc_block_interrupts(); 8458803SJonathan.Haslam@Sun.COM 8468803SJonathan.Haslam@Sun.COM c = cpu_list; 8478803SJonathan.Haslam@Sun.COM 8488803SJonathan.Haslam@Sun.COM do { 8498803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(c); 8508803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list); 8518803SJonathan.Haslam@Sun.COM 8528803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; 8538803SJonathan.Haslam@Sun.COM dcpc_release_interrupts(); 8548803SJonathan.Haslam@Sun.COM } else { 8558803SJonathan.Haslam@Sun.COM /* 8568803SJonathan.Haslam@Sun.COM * This platform can support multiple overflow events and 8578803SJonathan.Haslam@Sun.COM * the enabling being disabled is not the last one. Remove this 8588803SJonathan.Haslam@Sun.COM * enabling and re-program the hardware with the new config. 8598803SJonathan.Haslam@Sun.COM */ 8608803SJonathan.Haslam@Sun.COM ASSERT(dcpc_mult_ovf_cap); 8618803SJonathan.Haslam@Sun.COM ASSERT(dcpc_enablings > 1); 8628803SJonathan.Haslam@Sun.COM 8638803SJonathan.Haslam@Sun.COM pp->dcpc_disabling = 1; 8648803SJonathan.Haslam@Sun.COM (void) dcpc_program_event(pp); 8658803SJonathan.Haslam@Sun.COM } 8668803SJonathan.Haslam@Sun.COM 8678803SJonathan.Haslam@Sun.COM kpreempt_enable(); 8688803SJonathan.Haslam@Sun.COM 8698803SJonathan.Haslam@Sun.COM dcpc_enablings--; 8708803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use--; 8718803SJonathan.Haslam@Sun.COM pp->dcpc_enabled = 0; 8728803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1; 873*11389SAlexander.Kolbasov@Sun.COM 874*11389SAlexander.Kolbasov@Sun.COM /* 875*11389SAlexander.Kolbasov@Sun.COM * If all probes are removed, enable capacity/utilization data 876*11389SAlexander.Kolbasov@Sun.COM * collection for every CPU 877*11389SAlexander.Kolbasov@Sun.COM */ 878*11389SAlexander.Kolbasov@Sun.COM if (dtrace_cpc_in_use == 0) 879*11389SAlexander.Kolbasov@Sun.COM cu_enable(); 8808803SJonathan.Haslam@Sun.COM } 8818803SJonathan.Haslam@Sun.COM 8828803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 8838803SJonathan.Haslam@Sun.COM static int 8848803SJonathan.Haslam@Sun.COM dcpc_cpu_setup(cpu_setup_t what, processorid_t cpu, void *arg) 8858803SJonathan.Haslam@Sun.COM { 8868803SJonathan.Haslam@Sun.COM cpu_t *c; 8878803SJonathan.Haslam@Sun.COM uint8_t *state; 8888803SJonathan.Haslam@Sun.COM 8898803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 8908803SJonathan.Haslam@Sun.COM 8918803SJonathan.Haslam@Sun.COM switch (what) { 8928803SJonathan.Haslam@Sun.COM case CPU_OFF: 8938803SJonathan.Haslam@Sun.COM /* 8948803SJonathan.Haslam@Sun.COM * Offline CPUs are not allowed to take part so remove this 8958803SJonathan.Haslam@Sun.COM * CPU if we are actively tracing. 8968803SJonathan.Haslam@Sun.COM */ 8978803SJonathan.Haslam@Sun.COM if (dtrace_cpc_in_use) { 8988803SJonathan.Haslam@Sun.COM c = cpu_get(cpu); 8998803SJonathan.Haslam@Sun.COM state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state; 9008803SJonathan.Haslam@Sun.COM 9018803SJonathan.Haslam@Sun.COM /* 9028803SJonathan.Haslam@Sun.COM * Indicate that a configuration is in process in 9038803SJonathan.Haslam@Sun.COM * order to stop overflow interrupts being processed 9048803SJonathan.Haslam@Sun.COM * on this CPU while we disable it. 9058803SJonathan.Haslam@Sun.COM */ 9068803SJonathan.Haslam@Sun.COM while (atomic_cas_8(state, DCPC_INTR_FREE, 9078803SJonathan.Haslam@Sun.COM DCPC_INTR_CONFIG) != DCPC_INTR_FREE) 9088803SJonathan.Haslam@Sun.COM continue; 9098803SJonathan.Haslam@Sun.COM 9108803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(c); 9118803SJonathan.Haslam@Sun.COM 9128803SJonathan.Haslam@Sun.COM /* 9138803SJonathan.Haslam@Sun.COM * Reset this CPUs interrupt state as the configuration 9148803SJonathan.Haslam@Sun.COM * has ended. 9158803SJonathan.Haslam@Sun.COM */ 9168803SJonathan.Haslam@Sun.COM cpu_core[c->cpu_id].cpuc_dcpc_intr_state = 9178803SJonathan.Haslam@Sun.COM DCPC_INTR_FREE; 9188803SJonathan.Haslam@Sun.COM membar_producer(); 9198803SJonathan.Haslam@Sun.COM } 9208803SJonathan.Haslam@Sun.COM break; 9218803SJonathan.Haslam@Sun.COM 9228803SJonathan.Haslam@Sun.COM case CPU_ON: 9238803SJonathan.Haslam@Sun.COM case CPU_SETUP: 9248803SJonathan.Haslam@Sun.COM /* 9258803SJonathan.Haslam@Sun.COM * This CPU is being initialized or brought online so program 9268803SJonathan.Haslam@Sun.COM * it with the current request set if we are actively tracing. 9278803SJonathan.Haslam@Sun.COM */ 9288803SJonathan.Haslam@Sun.COM if (dtrace_cpc_in_use) { 9298803SJonathan.Haslam@Sun.COM c = cpu_get(cpu); 9308803SJonathan.Haslam@Sun.COM (void) dcpc_program_cpu_event(c); 9318803SJonathan.Haslam@Sun.COM } 9328803SJonathan.Haslam@Sun.COM break; 9338803SJonathan.Haslam@Sun.COM 9348803SJonathan.Haslam@Sun.COM default: 9358803SJonathan.Haslam@Sun.COM break; 9368803SJonathan.Haslam@Sun.COM } 9378803SJonathan.Haslam@Sun.COM 9388803SJonathan.Haslam@Sun.COM return (0); 9398803SJonathan.Haslam@Sun.COM } 9408803SJonathan.Haslam@Sun.COM 9418803SJonathan.Haslam@Sun.COM static dtrace_pattr_t dcpc_attr = { 9428803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 9438803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 9448803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 9458803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_CPU }, 9468803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 9478803SJonathan.Haslam@Sun.COM }; 9488803SJonathan.Haslam@Sun.COM 9498803SJonathan.Haslam@Sun.COM static dtrace_pops_t dcpc_pops = { 9508803SJonathan.Haslam@Sun.COM dcpc_provide, 9518803SJonathan.Haslam@Sun.COM NULL, 9528803SJonathan.Haslam@Sun.COM dcpc_enable, 9538803SJonathan.Haslam@Sun.COM dcpc_disable, 9548803SJonathan.Haslam@Sun.COM NULL, 9558803SJonathan.Haslam@Sun.COM NULL, 9568803SJonathan.Haslam@Sun.COM NULL, 9578803SJonathan.Haslam@Sun.COM NULL, 9588803SJonathan.Haslam@Sun.COM dcpc_usermode, 9598803SJonathan.Haslam@Sun.COM dcpc_destroy 9608803SJonathan.Haslam@Sun.COM }; 9618803SJonathan.Haslam@Sun.COM 9628803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 9638803SJonathan.Haslam@Sun.COM static int 9648803SJonathan.Haslam@Sun.COM dcpc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 9658803SJonathan.Haslam@Sun.COM { 9668803SJonathan.Haslam@Sun.COM return (0); 9678803SJonathan.Haslam@Sun.COM } 9688803SJonathan.Haslam@Sun.COM 9698803SJonathan.Haslam@Sun.COM /*ARGSUSED*/ 9708803SJonathan.Haslam@Sun.COM static int 9718803SJonathan.Haslam@Sun.COM dcpc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 9728803SJonathan.Haslam@Sun.COM { 9738803SJonathan.Haslam@Sun.COM int error; 9748803SJonathan.Haslam@Sun.COM 9758803SJonathan.Haslam@Sun.COM switch (infocmd) { 9768803SJonathan.Haslam@Sun.COM case DDI_INFO_DEVT2DEVINFO: 9778803SJonathan.Haslam@Sun.COM *result = (void *)dcpc_devi; 9788803SJonathan.Haslam@Sun.COM error = DDI_SUCCESS; 9798803SJonathan.Haslam@Sun.COM break; 9808803SJonathan.Haslam@Sun.COM case DDI_INFO_DEVT2INSTANCE: 9818803SJonathan.Haslam@Sun.COM *result = (void *)0; 9828803SJonathan.Haslam@Sun.COM error = DDI_SUCCESS; 9838803SJonathan.Haslam@Sun.COM break; 9848803SJonathan.Haslam@Sun.COM default: 9858803SJonathan.Haslam@Sun.COM error = DDI_FAILURE; 9868803SJonathan.Haslam@Sun.COM } 9878803SJonathan.Haslam@Sun.COM return (error); 9888803SJonathan.Haslam@Sun.COM } 9898803SJonathan.Haslam@Sun.COM 9908803SJonathan.Haslam@Sun.COM static int 9918803SJonathan.Haslam@Sun.COM dcpc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 9928803SJonathan.Haslam@Sun.COM { 9938803SJonathan.Haslam@Sun.COM switch (cmd) { 9948803SJonathan.Haslam@Sun.COM case DDI_DETACH: 9958803SJonathan.Haslam@Sun.COM break; 9968803SJonathan.Haslam@Sun.COM case DDI_SUSPEND: 9978803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS); 9988803SJonathan.Haslam@Sun.COM default: 9998803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 10008803SJonathan.Haslam@Sun.COM } 10018803SJonathan.Haslam@Sun.COM 10028803SJonathan.Haslam@Sun.COM if (dtrace_unregister(dcpc_pid) != 0) 10038803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 10048803SJonathan.Haslam@Sun.COM 10058803SJonathan.Haslam@Sun.COM ddi_remove_minor_node(devi, NULL); 10068803SJonathan.Haslam@Sun.COM 10078803SJonathan.Haslam@Sun.COM mutex_enter(&cpu_lock); 10088803SJonathan.Haslam@Sun.COM unregister_cpu_setup_func(dcpc_cpu_setup, NULL); 10098803SJonathan.Haslam@Sun.COM mutex_exit(&cpu_lock); 10108803SJonathan.Haslam@Sun.COM 10118803SJonathan.Haslam@Sun.COM kmem_free(dcpc_actv_reqs, cpc_ncounters * sizeof (dcpc_probe_t *)); 10128803SJonathan.Haslam@Sun.COM 10138803SJonathan.Haslam@Sun.COM kcpc_unregister_dcpc(); 10148803SJonathan.Haslam@Sun.COM 10158803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS); 10168803SJonathan.Haslam@Sun.COM } 10178803SJonathan.Haslam@Sun.COM 10188803SJonathan.Haslam@Sun.COM static int 10198803SJonathan.Haslam@Sun.COM dcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 10208803SJonathan.Haslam@Sun.COM { 10218803SJonathan.Haslam@Sun.COM uint_t caps; 10228803SJonathan.Haslam@Sun.COM char *attrs; 10238803SJonathan.Haslam@Sun.COM 10248803SJonathan.Haslam@Sun.COM switch (cmd) { 10258803SJonathan.Haslam@Sun.COM case DDI_ATTACH: 10268803SJonathan.Haslam@Sun.COM break; 10278803SJonathan.Haslam@Sun.COM case DDI_RESUME: 10288803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS); 10298803SJonathan.Haslam@Sun.COM default: 10308803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 10318803SJonathan.Haslam@Sun.COM } 10328803SJonathan.Haslam@Sun.COM 10338803SJonathan.Haslam@Sun.COM if (kcpc_pcbe_loaded() == -1) 10348803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 10358803SJonathan.Haslam@Sun.COM 10368803SJonathan.Haslam@Sun.COM caps = kcpc_pcbe_capabilities(); 10378803SJonathan.Haslam@Sun.COM 10388803SJonathan.Haslam@Sun.COM if (!(caps & CPC_CAP_OVERFLOW_INTERRUPT)) { 10399397SJonathan.Haslam@Sun.COM cmn_err(CE_NOTE, "!dcpc: Counter Overflow not supported"\ 10409397SJonathan.Haslam@Sun.COM " on this processor"); 10418803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 10428803SJonathan.Haslam@Sun.COM } 10438803SJonathan.Haslam@Sun.COM 10448803SJonathan.Haslam@Sun.COM if (ddi_create_minor_node(devi, "dcpc", S_IFCHR, 0, 10458803SJonathan.Haslam@Sun.COM DDI_PSEUDO, NULL) == DDI_FAILURE || 10468803SJonathan.Haslam@Sun.COM dtrace_register("cpc", &dcpc_attr, DTRACE_PRIV_KERNEL, 10478803SJonathan.Haslam@Sun.COM NULL, &dcpc_pops, NULL, &dcpc_pid) != 0) { 10488803SJonathan.Haslam@Sun.COM ddi_remove_minor_node(devi, NULL); 10498803SJonathan.Haslam@Sun.COM return (DDI_FAILURE); 10508803SJonathan.Haslam@Sun.COM } 10518803SJonathan.Haslam@Sun.COM 10528803SJonathan.Haslam@Sun.COM mutex_enter(&cpu_lock); 10538803SJonathan.Haslam@Sun.COM register_cpu_setup_func(dcpc_cpu_setup, NULL); 10548803SJonathan.Haslam@Sun.COM mutex_exit(&cpu_lock); 10558803SJonathan.Haslam@Sun.COM 10568803SJonathan.Haslam@Sun.COM dcpc_ovf_mask = (1 << cpc_ncounters) - 1; 10578803SJonathan.Haslam@Sun.COM ASSERT(dcpc_ovf_mask != 0); 10588803SJonathan.Haslam@Sun.COM 10598803SJonathan.Haslam@Sun.COM if (caps & CPC_CAP_OVERFLOW_PRECISE) 10608803SJonathan.Haslam@Sun.COM dcpc_mult_ovf_cap = 1; 10618803SJonathan.Haslam@Sun.COM 10628803SJonathan.Haslam@Sun.COM /* 10638803SJonathan.Haslam@Sun.COM * Determine which, if any, mask attribute the back-end can use. 10648803SJonathan.Haslam@Sun.COM */ 10658803SJonathan.Haslam@Sun.COM attrs = kcpc_list_attrs(); 10668803SJonathan.Haslam@Sun.COM if (strstr(attrs, "umask") != NULL) 10678803SJonathan.Haslam@Sun.COM dcpc_mask_type |= DCPC_UMASK; 10688803SJonathan.Haslam@Sun.COM else if (strstr(attrs, "emask") != NULL) 10698803SJonathan.Haslam@Sun.COM dcpc_mask_type |= DCPC_EMASK; 10708803SJonathan.Haslam@Sun.COM 10718803SJonathan.Haslam@Sun.COM /* 10728803SJonathan.Haslam@Sun.COM * The dcpc_actv_reqs array is used to store the requests that 10738803SJonathan.Haslam@Sun.COM * we currently have programmed. The order of requests in this 10748803SJonathan.Haslam@Sun.COM * array is not necessarily the order that the event appears in 10758803SJonathan.Haslam@Sun.COM * the kcpc_request_t array. Once entered into a slot in the array 10768803SJonathan.Haslam@Sun.COM * the entry is not moved until it's removed. 10778803SJonathan.Haslam@Sun.COM */ 10788803SJonathan.Haslam@Sun.COM dcpc_actv_reqs = 10798803SJonathan.Haslam@Sun.COM kmem_zalloc(cpc_ncounters * sizeof (dcpc_probe_t *), KM_SLEEP); 10808803SJonathan.Haslam@Sun.COM 10818803SJonathan.Haslam@Sun.COM dcpc_min_overflow = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 10828803SJonathan.Haslam@Sun.COM DDI_PROP_DONTPASS, "dcpc-min-overflow", DCPC_MIN_OVF_DEFAULT); 10838803SJonathan.Haslam@Sun.COM 10848803SJonathan.Haslam@Sun.COM kcpc_register_dcpc(dcpc_fire); 10858803SJonathan.Haslam@Sun.COM 10868803SJonathan.Haslam@Sun.COM ddi_report_dev(devi); 10878803SJonathan.Haslam@Sun.COM dcpc_devi = devi; 10888803SJonathan.Haslam@Sun.COM 10898803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS); 10908803SJonathan.Haslam@Sun.COM } 10918803SJonathan.Haslam@Sun.COM 10928803SJonathan.Haslam@Sun.COM static struct cb_ops dcpc_cb_ops = { 10938803SJonathan.Haslam@Sun.COM dcpc_open, /* open */ 10948803SJonathan.Haslam@Sun.COM nodev, /* close */ 10958803SJonathan.Haslam@Sun.COM nulldev, /* strategy */ 10968803SJonathan.Haslam@Sun.COM nulldev, /* print */ 10978803SJonathan.Haslam@Sun.COM nodev, /* dump */ 10988803SJonathan.Haslam@Sun.COM nodev, /* read */ 10998803SJonathan.Haslam@Sun.COM nodev, /* write */ 11008803SJonathan.Haslam@Sun.COM nodev, /* ioctl */ 11018803SJonathan.Haslam@Sun.COM nodev, /* devmap */ 11028803SJonathan.Haslam@Sun.COM nodev, /* mmap */ 11038803SJonathan.Haslam@Sun.COM nodev, /* segmap */ 11048803SJonathan.Haslam@Sun.COM nochpoll, /* poll */ 11058803SJonathan.Haslam@Sun.COM ddi_prop_op, /* cb_prop_op */ 11068803SJonathan.Haslam@Sun.COM 0, /* streamtab */ 11078803SJonathan.Haslam@Sun.COM D_NEW | D_MP /* Driver compatibility flag */ 11088803SJonathan.Haslam@Sun.COM }; 11098803SJonathan.Haslam@Sun.COM 11108803SJonathan.Haslam@Sun.COM static struct dev_ops dcpc_ops = { 11118803SJonathan.Haslam@Sun.COM DEVO_REV, /* devo_rev, */ 11128803SJonathan.Haslam@Sun.COM 0, /* refcnt */ 11138803SJonathan.Haslam@Sun.COM dcpc_info, /* get_dev_info */ 11148803SJonathan.Haslam@Sun.COM nulldev, /* identify */ 11158803SJonathan.Haslam@Sun.COM nulldev, /* probe */ 11168803SJonathan.Haslam@Sun.COM dcpc_attach, /* attach */ 11178803SJonathan.Haslam@Sun.COM dcpc_detach, /* detach */ 11188803SJonathan.Haslam@Sun.COM nodev, /* reset */ 11198803SJonathan.Haslam@Sun.COM &dcpc_cb_ops, /* driver operations */ 11208803SJonathan.Haslam@Sun.COM NULL, /* bus operations */ 11218803SJonathan.Haslam@Sun.COM nodev, /* dev power */ 11228803SJonathan.Haslam@Sun.COM ddi_quiesce_not_needed /* quiesce */ 11238803SJonathan.Haslam@Sun.COM }; 11248803SJonathan.Haslam@Sun.COM 11258803SJonathan.Haslam@Sun.COM /* 11268803SJonathan.Haslam@Sun.COM * Module linkage information for the kernel. 11278803SJonathan.Haslam@Sun.COM */ 11288803SJonathan.Haslam@Sun.COM static struct modldrv modldrv = { 11298803SJonathan.Haslam@Sun.COM &mod_driverops, /* module type */ 11308803SJonathan.Haslam@Sun.COM "DTrace CPC Module", /* name of module */ 11318803SJonathan.Haslam@Sun.COM &dcpc_ops, /* driver ops */ 11328803SJonathan.Haslam@Sun.COM }; 11338803SJonathan.Haslam@Sun.COM 11348803SJonathan.Haslam@Sun.COM static struct modlinkage modlinkage = { 11358803SJonathan.Haslam@Sun.COM MODREV_1, 11368803SJonathan.Haslam@Sun.COM (void *)&modldrv, 11378803SJonathan.Haslam@Sun.COM NULL 11388803SJonathan.Haslam@Sun.COM }; 11398803SJonathan.Haslam@Sun.COM 11408803SJonathan.Haslam@Sun.COM int 11418803SJonathan.Haslam@Sun.COM _init(void) 11428803SJonathan.Haslam@Sun.COM { 11438803SJonathan.Haslam@Sun.COM return (mod_install(&modlinkage)); 11448803SJonathan.Haslam@Sun.COM } 11458803SJonathan.Haslam@Sun.COM 11468803SJonathan.Haslam@Sun.COM int 11478803SJonathan.Haslam@Sun.COM _info(struct modinfo *modinfop) 11488803SJonathan.Haslam@Sun.COM { 11498803SJonathan.Haslam@Sun.COM return (mod_info(&modlinkage, modinfop)); 11508803SJonathan.Haslam@Sun.COM } 11518803SJonathan.Haslam@Sun.COM 11528803SJonathan.Haslam@Sun.COM int 11538803SJonathan.Haslam@Sun.COM _fini(void) 11548803SJonathan.Haslam@Sun.COM { 11558803SJonathan.Haslam@Sun.COM return (mod_remove(&modlinkage)); 11568803SJonathan.Haslam@Sun.COM } 1157