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 /*
23*11455SJonathan.Haslam@Sun.COM * Copyright 2010 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>
3811389SAlexander.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
dcpc_fire(uint64_t bitmap)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
dcpc_create_probe(dtrace_provider_id_t id,const char * probename,char * eventname,int64_t umask,uint32_t ovfval,char flag)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
dcpc_provide(void * arg,const dtrace_probedesc_t * desc)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
dcpc_destroy(void * arg,dtrace_id_t id,void * parg)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
dcpc_usermode(void * arg,dtrace_id_t id,void * parg)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
dcpc_populate_set(cpu_t * c,dcpc_probe_t * pp,kcpc_set_t * set,int reqno)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 *
dcpc_create_set(cpu_t * c)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
dcpc_program_cpu_event(cpu_t * c)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
46711389SAlexander.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 */
49211389SAlexander.Kolbasov@Sun.COM octx = c->cpu_cpc_ctx;
4938803SJonathan.Haslam@Sun.COM
49411389SAlexander.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));
499*11455SJonathan.Haslam@Sun.COM kcpc_free_configs(oset);
5008803SJonathan.Haslam@Sun.COM kcpc_free_set(oset);
5018803SJonathan.Haslam@Sun.COM kcpc_ctx_free(octx);
5028803SJonathan.Haslam@Sun.COM }
5038803SJonathan.Haslam@Sun.COM
5048803SJonathan.Haslam@Sun.COM return (0);
5058803SJonathan.Haslam@Sun.COM
5068803SJonathan.Haslam@Sun.COM err:
5078803SJonathan.Haslam@Sun.COM /*
5088803SJonathan.Haslam@Sun.COM * We failed to configure this request up so free things up and
5098803SJonathan.Haslam@Sun.COM * get out.
5108803SJonathan.Haslam@Sun.COM */
511*11455SJonathan.Haslam@Sun.COM kcpc_free_configs(set);
5128803SJonathan.Haslam@Sun.COM kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t));
5138803SJonathan.Haslam@Sun.COM kcpc_free_set(set);
5148803SJonathan.Haslam@Sun.COM kcpc_ctx_free(ctx);
5158803SJonathan.Haslam@Sun.COM
5168803SJonathan.Haslam@Sun.COM return (-1);
5178803SJonathan.Haslam@Sun.COM }
5188803SJonathan.Haslam@Sun.COM
5198803SJonathan.Haslam@Sun.COM static void
dcpc_disable_cpu(cpu_t * c)5208803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(cpu_t *c)
5218803SJonathan.Haslam@Sun.COM {
5228803SJonathan.Haslam@Sun.COM kcpc_ctx_t *ctx;
5238803SJonathan.Haslam@Sun.COM kcpc_set_t *set;
5248803SJonathan.Haslam@Sun.COM
5258803SJonathan.Haslam@Sun.COM /*
5268803SJonathan.Haslam@Sun.COM * Leave this CPU alone if it's already offline.
5278803SJonathan.Haslam@Sun.COM */
5288803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE)
5298803SJonathan.Haslam@Sun.COM return;
5308803SJonathan.Haslam@Sun.COM
53111389SAlexander.Kolbasov@Sun.COM /*
53211389SAlexander.Kolbasov@Sun.COM * Grab CPUs CPC context before kcpc_cpu_stop() stops counters and
53311389SAlexander.Kolbasov@Sun.COM * changes it.
53411389SAlexander.Kolbasov@Sun.COM */
53511389SAlexander.Kolbasov@Sun.COM ctx = c->cpu_cpc_ctx;
5368803SJonathan.Haslam@Sun.COM
53711389SAlexander.Kolbasov@Sun.COM kcpc_cpu_stop(c, B_FALSE);
53811389SAlexander.Kolbasov@Sun.COM
5398803SJonathan.Haslam@Sun.COM set = ctx->kc_set;
5408803SJonathan.Haslam@Sun.COM
5418803SJonathan.Haslam@Sun.COM kcpc_free_configs(set);
5428803SJonathan.Haslam@Sun.COM kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t));
5438803SJonathan.Haslam@Sun.COM kcpc_free_set(set);
5448803SJonathan.Haslam@Sun.COM kcpc_ctx_free(ctx);
5458803SJonathan.Haslam@Sun.COM }
5468803SJonathan.Haslam@Sun.COM
5478803SJonathan.Haslam@Sun.COM /*
548*11455SJonathan.Haslam@Sun.COM * The dcpc_*_interrupts() routines are responsible for manipulating the
549*11455SJonathan.Haslam@Sun.COM * per-CPU dcpc interrupt state byte. The purpose of the state byte is to
550*11455SJonathan.Haslam@Sun.COM * synchronize processing of hardware overflow interrupts wth configuration
551*11455SJonathan.Haslam@Sun.COM * changes made to the CPU performance counter subsystem by the dcpc provider.
552*11455SJonathan.Haslam@Sun.COM *
553*11455SJonathan.Haslam@Sun.COM * The dcpc provider claims ownership of the overflow interrupt mechanism
554*11455SJonathan.Haslam@Sun.COM * by transitioning the state byte from DCPC_INTR_INACTIVE (indicating the
555*11455SJonathan.Haslam@Sun.COM * dcpc provider is not in use) to DCPC_INTR_FREE (the dcpc provider owns the
556*11455SJonathan.Haslam@Sun.COM * overflow mechanism and interrupts may be processed). Before modifying
557*11455SJonathan.Haslam@Sun.COM * a CPUs configuration state the state byte is transitioned from
558*11455SJonathan.Haslam@Sun.COM * DCPC_INTR_FREE to DCPC_INTR_CONFIG ("configuration in process" state).
559*11455SJonathan.Haslam@Sun.COM * The hardware overflow handler, kcpc_hw_overflow_intr(), will only process
560*11455SJonathan.Haslam@Sun.COM * an interrupt when a configuration is not in process (i.e. the state is
561*11455SJonathan.Haslam@Sun.COM * marked as free). During interrupt processing the state is set to
562*11455SJonathan.Haslam@Sun.COM * DCPC_INTR_PROCESSING by the overflow handler. When the last dcpc based
563*11455SJonathan.Haslam@Sun.COM * enabling is removed, the state byte is set to DCPC_INTR_INACTIVE to indicate
564*11455SJonathan.Haslam@Sun.COM * the dcpc provider is no longer interested in overflow interrupts.
5658803SJonathan.Haslam@Sun.COM */
5668803SJonathan.Haslam@Sun.COM static void
dcpc_block_interrupts(void)5678803SJonathan.Haslam@Sun.COM dcpc_block_interrupts(void)
5688803SJonathan.Haslam@Sun.COM {
569*11455SJonathan.Haslam@Sun.COM cpu_t *c = cpu_list;
5708803SJonathan.Haslam@Sun.COM uint8_t *state;
5718803SJonathan.Haslam@Sun.COM
572*11455SJonathan.Haslam@Sun.COM ASSERT(cpu_core[c->cpu_id].cpuc_dcpc_intr_state != DCPC_INTR_INACTIVE);
5738803SJonathan.Haslam@Sun.COM
5748803SJonathan.Haslam@Sun.COM do {
5758803SJonathan.Haslam@Sun.COM state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state;
5768803SJonathan.Haslam@Sun.COM
5778803SJonathan.Haslam@Sun.COM while (atomic_cas_8(state, DCPC_INTR_FREE,
5788803SJonathan.Haslam@Sun.COM DCPC_INTR_CONFIG) != DCPC_INTR_FREE)
5798803SJonathan.Haslam@Sun.COM continue;
5808803SJonathan.Haslam@Sun.COM
5818803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list);
5828803SJonathan.Haslam@Sun.COM }
5838803SJonathan.Haslam@Sun.COM
5848803SJonathan.Haslam@Sun.COM /*
5858803SJonathan.Haslam@Sun.COM * Set all CPUs dcpc interrupt state to DCPC_INTR_FREE to indicate that
5868803SJonathan.Haslam@Sun.COM * overflow interrupts can be processed safely.
5878803SJonathan.Haslam@Sun.COM */
5888803SJonathan.Haslam@Sun.COM static void
dcpc_release_interrupts(void)5898803SJonathan.Haslam@Sun.COM dcpc_release_interrupts(void)
5908803SJonathan.Haslam@Sun.COM {
5918803SJonathan.Haslam@Sun.COM cpu_t *c = cpu_list;
5928803SJonathan.Haslam@Sun.COM
593*11455SJonathan.Haslam@Sun.COM ASSERT(cpu_core[c->cpu_id].cpuc_dcpc_intr_state != DCPC_INTR_INACTIVE);
594*11455SJonathan.Haslam@Sun.COM
5958803SJonathan.Haslam@Sun.COM do {
5968803SJonathan.Haslam@Sun.COM cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_FREE;
5978803SJonathan.Haslam@Sun.COM membar_producer();
5988803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list);
5998803SJonathan.Haslam@Sun.COM }
6008803SJonathan.Haslam@Sun.COM
6018803SJonathan.Haslam@Sun.COM /*
602*11455SJonathan.Haslam@Sun.COM * Transition all CPUs dcpc interrupt state from DCPC_INTR_INACTIVE to
603*11455SJonathan.Haslam@Sun.COM * to DCPC_INTR_FREE. This indicates that the dcpc provider is now
604*11455SJonathan.Haslam@Sun.COM * responsible for handling all overflow interrupt activity. Should only be
605*11455SJonathan.Haslam@Sun.COM * called before enabling the first dcpc based probe.
606*11455SJonathan.Haslam@Sun.COM */
607*11455SJonathan.Haslam@Sun.COM static void
dcpc_claim_interrupts(void)608*11455SJonathan.Haslam@Sun.COM dcpc_claim_interrupts(void)
609*11455SJonathan.Haslam@Sun.COM {
610*11455SJonathan.Haslam@Sun.COM cpu_t *c = cpu_list;
611*11455SJonathan.Haslam@Sun.COM
612*11455SJonathan.Haslam@Sun.COM ASSERT(cpu_core[c->cpu_id].cpuc_dcpc_intr_state == DCPC_INTR_INACTIVE);
613*11455SJonathan.Haslam@Sun.COM
614*11455SJonathan.Haslam@Sun.COM do {
615*11455SJonathan.Haslam@Sun.COM cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_FREE;
616*11455SJonathan.Haslam@Sun.COM membar_producer();
617*11455SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list);
618*11455SJonathan.Haslam@Sun.COM }
619*11455SJonathan.Haslam@Sun.COM
620*11455SJonathan.Haslam@Sun.COM /*
621*11455SJonathan.Haslam@Sun.COM * Set all CPUs dcpc interrupt state to DCPC_INTR_INACTIVE to indicate that
622*11455SJonathan.Haslam@Sun.COM * the dcpc provider is no longer processing overflow interrupts. Only called
623*11455SJonathan.Haslam@Sun.COM * during removal of the last dcpc based enabling.
624*11455SJonathan.Haslam@Sun.COM */
625*11455SJonathan.Haslam@Sun.COM static void
dcpc_surrender_interrupts(void)626*11455SJonathan.Haslam@Sun.COM dcpc_surrender_interrupts(void)
627*11455SJonathan.Haslam@Sun.COM {
628*11455SJonathan.Haslam@Sun.COM cpu_t *c = cpu_list;
629*11455SJonathan.Haslam@Sun.COM
630*11455SJonathan.Haslam@Sun.COM ASSERT(cpu_core[c->cpu_id].cpuc_dcpc_intr_state != DCPC_INTR_INACTIVE);
631*11455SJonathan.Haslam@Sun.COM
632*11455SJonathan.Haslam@Sun.COM do {
633*11455SJonathan.Haslam@Sun.COM cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_INACTIVE;
634*11455SJonathan.Haslam@Sun.COM membar_producer();
635*11455SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list);
636*11455SJonathan.Haslam@Sun.COM }
637*11455SJonathan.Haslam@Sun.COM
638*11455SJonathan.Haslam@Sun.COM /*
6398803SJonathan.Haslam@Sun.COM * dcpc_program_event() can be called owing to a new enabling or if a multi
6408803SJonathan.Haslam@Sun.COM * overflow platform has disabled a request but needs to program the requests
6418803SJonathan.Haslam@Sun.COM * that are still valid.
6428803SJonathan.Haslam@Sun.COM *
6438803SJonathan.Haslam@Sun.COM * Every invocation of dcpc_program_event() will create a new kcpc_ctx_t
6448803SJonathan.Haslam@Sun.COM * and a new request set which contains the new enabling and any old enablings
6458803SJonathan.Haslam@Sun.COM * which are still valid (possible with multi-overflow platforms).
6468803SJonathan.Haslam@Sun.COM */
6478803SJonathan.Haslam@Sun.COM static int
dcpc_program_event(dcpc_probe_t * pp)6488803SJonathan.Haslam@Sun.COM dcpc_program_event(dcpc_probe_t *pp)
6498803SJonathan.Haslam@Sun.COM {
6508803SJonathan.Haslam@Sun.COM cpu_t *c;
6518803SJonathan.Haslam@Sun.COM int ret = 0;
6528803SJonathan.Haslam@Sun.COM
6538803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
6548803SJonathan.Haslam@Sun.COM
6558803SJonathan.Haslam@Sun.COM kpreempt_disable();
6568803SJonathan.Haslam@Sun.COM
6578803SJonathan.Haslam@Sun.COM dcpc_block_interrupts();
6588803SJonathan.Haslam@Sun.COM
6598803SJonathan.Haslam@Sun.COM c = cpu_list;
6608803SJonathan.Haslam@Sun.COM
6618803SJonathan.Haslam@Sun.COM do {
6628803SJonathan.Haslam@Sun.COM /*
6638803SJonathan.Haslam@Sun.COM * Skip CPUs that are currently offline.
6648803SJonathan.Haslam@Sun.COM */
6658803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE)
6668803SJonathan.Haslam@Sun.COM continue;
6678803SJonathan.Haslam@Sun.COM
66811389SAlexander.Kolbasov@Sun.COM /*
66911389SAlexander.Kolbasov@Sun.COM * Stop counters but preserve existing DTrace CPC context
67011389SAlexander.Kolbasov@Sun.COM * if there is one.
67111389SAlexander.Kolbasov@Sun.COM *
67211389SAlexander.Kolbasov@Sun.COM * If we come here when the first event is programmed for a CPU,
67311389SAlexander.Kolbasov@Sun.COM * there should be no DTrace CPC context installed. In this
67411389SAlexander.Kolbasov@Sun.COM * case, kcpc_cpu_stop() will ensure that there is no other
67511389SAlexander.Kolbasov@Sun.COM * context on the CPU.
67611389SAlexander.Kolbasov@Sun.COM *
67711389SAlexander.Kolbasov@Sun.COM * If we add new enabling to the original one, the CPU should
67811389SAlexander.Kolbasov@Sun.COM * have the old DTrace CPC context which we need to keep around
67911389SAlexander.Kolbasov@Sun.COM * since dcpc_program_event() will add to it.
68011389SAlexander.Kolbasov@Sun.COM */
6818803SJonathan.Haslam@Sun.COM if (c->cpu_cpc_ctx != NULL)
68211389SAlexander.Kolbasov@Sun.COM kcpc_cpu_stop(c, B_TRUE);
6838803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list);
6848803SJonathan.Haslam@Sun.COM
6858803SJonathan.Haslam@Sun.COM dcpc_release_interrupts();
6868803SJonathan.Haslam@Sun.COM
6878803SJonathan.Haslam@Sun.COM /*
6888803SJonathan.Haslam@Sun.COM * If this enabling is being removed (in the case of a multi event
6898803SJonathan.Haslam@Sun.COM * capable system with more than one active enabling), we can now
6908803SJonathan.Haslam@Sun.COM * update the active request array to reflect the enablings that need
6918803SJonathan.Haslam@Sun.COM * to be reprogrammed.
6928803SJonathan.Haslam@Sun.COM */
6938803SJonathan.Haslam@Sun.COM if (pp->dcpc_disabling == 1)
6948803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
6958803SJonathan.Haslam@Sun.COM
6968803SJonathan.Haslam@Sun.COM do {
6978803SJonathan.Haslam@Sun.COM /*
6988803SJonathan.Haslam@Sun.COM * Skip CPUs that are currently offline.
6998803SJonathan.Haslam@Sun.COM */
7008803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE)
7018803SJonathan.Haslam@Sun.COM continue;
7028803SJonathan.Haslam@Sun.COM
7038803SJonathan.Haslam@Sun.COM ret = dcpc_program_cpu_event(c);
7048803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list && ret == 0);
7058803SJonathan.Haslam@Sun.COM
7068803SJonathan.Haslam@Sun.COM /*
7078803SJonathan.Haslam@Sun.COM * If dcpc_program_cpu_event() fails then it is because we couldn't
7088803SJonathan.Haslam@Sun.COM * configure the requests in the set for the CPU and not because of
7098803SJonathan.Haslam@Sun.COM * an error programming the hardware. If we have a failure here then
7108803SJonathan.Haslam@Sun.COM * we assume no CPUs have been programmed in the above step as they
7118803SJonathan.Haslam@Sun.COM * are all configured identically.
7128803SJonathan.Haslam@Sun.COM */
7138803SJonathan.Haslam@Sun.COM if (ret != 0) {
7148803SJonathan.Haslam@Sun.COM pp->dcpc_enabled = 0;
7158803SJonathan.Haslam@Sun.COM kpreempt_enable();
7168803SJonathan.Haslam@Sun.COM return (-1);
7178803SJonathan.Haslam@Sun.COM }
7188803SJonathan.Haslam@Sun.COM
7198803SJonathan.Haslam@Sun.COM if (pp->dcpc_disabling != 1)
7208803SJonathan.Haslam@Sun.COM pp->dcpc_enabled = 1;
7218803SJonathan.Haslam@Sun.COM
7228803SJonathan.Haslam@Sun.COM kpreempt_enable();
7238803SJonathan.Haslam@Sun.COM
7248803SJonathan.Haslam@Sun.COM return (0);
7258803SJonathan.Haslam@Sun.COM }
7268803SJonathan.Haslam@Sun.COM
7278803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
7288803SJonathan.Haslam@Sun.COM static int
dcpc_enable(void * arg,dtrace_id_t id,void * parg)7298803SJonathan.Haslam@Sun.COM dcpc_enable(void *arg, dtrace_id_t id, void *parg)
7308803SJonathan.Haslam@Sun.COM {
7318803SJonathan.Haslam@Sun.COM dcpc_probe_t *pp = parg;
7328803SJonathan.Haslam@Sun.COM int i, found = 0;
7338803SJonathan.Haslam@Sun.COM cpu_t *c;
7348803SJonathan.Haslam@Sun.COM
7358803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
7368803SJonathan.Haslam@Sun.COM
7378803SJonathan.Haslam@Sun.COM /*
7388803SJonathan.Haslam@Sun.COM * Bail out if the counters are being used by a libcpc consumer.
7398803SJonathan.Haslam@Sun.COM */
7408803SJonathan.Haslam@Sun.COM rw_enter(&kcpc_cpuctx_lock, RW_READER);
7418803SJonathan.Haslam@Sun.COM if (kcpc_cpuctx > 0) {
7428803SJonathan.Haslam@Sun.COM rw_exit(&kcpc_cpuctx_lock);
7438803SJonathan.Haslam@Sun.COM return (-1);
7448803SJonathan.Haslam@Sun.COM }
7458803SJonathan.Haslam@Sun.COM
7468803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use++;
7478803SJonathan.Haslam@Sun.COM rw_exit(&kcpc_cpuctx_lock);
7488803SJonathan.Haslam@Sun.COM
7498803SJonathan.Haslam@Sun.COM /*
7508803SJonathan.Haslam@Sun.COM * Locate this enabling in the first free entry of the active
7518803SJonathan.Haslam@Sun.COM * request array.
7528803SJonathan.Haslam@Sun.COM */
7538803SJonathan.Haslam@Sun.COM for (i = 0; i < cpc_ncounters; i++) {
7548803SJonathan.Haslam@Sun.COM if (dcpc_actv_reqs[i] == NULL) {
7558803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[i] = pp;
7568803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = i;
7578803SJonathan.Haslam@Sun.COM found = 1;
7588803SJonathan.Haslam@Sun.COM break;
7598803SJonathan.Haslam@Sun.COM }
7608803SJonathan.Haslam@Sun.COM }
7618803SJonathan.Haslam@Sun.COM
7628803SJonathan.Haslam@Sun.COM /*
7638803SJonathan.Haslam@Sun.COM * If we couldn't find a slot for this probe then there is no
7648803SJonathan.Haslam@Sun.COM * room at the inn.
7658803SJonathan.Haslam@Sun.COM */
7668803SJonathan.Haslam@Sun.COM if (!found) {
7678803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use--;
7688803SJonathan.Haslam@Sun.COM return (-1);
7698803SJonathan.Haslam@Sun.COM }
7708803SJonathan.Haslam@Sun.COM
7718803SJonathan.Haslam@Sun.COM ASSERT(pp->dcpc_actv_req_idx >= 0);
7728803SJonathan.Haslam@Sun.COM
7738803SJonathan.Haslam@Sun.COM /*
77411389SAlexander.Kolbasov@Sun.COM * DTrace is taking over CPC contexts, so stop collecting
77511389SAlexander.Kolbasov@Sun.COM * capacity/utilization data for all CPUs.
77611389SAlexander.Kolbasov@Sun.COM */
77711389SAlexander.Kolbasov@Sun.COM if (dtrace_cpc_in_use == 1)
77811389SAlexander.Kolbasov@Sun.COM cu_disable();
77911389SAlexander.Kolbasov@Sun.COM
78011389SAlexander.Kolbasov@Sun.COM /*
7818803SJonathan.Haslam@Sun.COM * The following must hold true if we are to (attempt to) enable
7828803SJonathan.Haslam@Sun.COM * this request:
7838803SJonathan.Haslam@Sun.COM *
7848803SJonathan.Haslam@Sun.COM * 1) No enablings currently exist. We allow all platforms to
7858803SJonathan.Haslam@Sun.COM * proceed if this is true.
7868803SJonathan.Haslam@Sun.COM *
7878803SJonathan.Haslam@Sun.COM * OR
7888803SJonathan.Haslam@Sun.COM *
7898803SJonathan.Haslam@Sun.COM * 2) If the platform is multi overflow capable and there are
7908803SJonathan.Haslam@Sun.COM * less valid enablings than there are counters. There is no
7918803SJonathan.Haslam@Sun.COM * guarantee that a platform can accommodate as many events as
7928803SJonathan.Haslam@Sun.COM * it has counters for but we will at least try to program
7938803SJonathan.Haslam@Sun.COM * up to that many requests.
7948803SJonathan.Haslam@Sun.COM *
7958803SJonathan.Haslam@Sun.COM * The 'dcpc_enablings' variable is implictly protected by locking
7968803SJonathan.Haslam@Sun.COM * provided by the DTrace framework and the cpu management framework.
7978803SJonathan.Haslam@Sun.COM */
7988803SJonathan.Haslam@Sun.COM if (dcpc_enablings == 0 || (dcpc_mult_ovf_cap &&
7998803SJonathan.Haslam@Sun.COM dcpc_enablings < cpc_ncounters)) {
8008803SJonathan.Haslam@Sun.COM /*
8018803SJonathan.Haslam@Sun.COM * Before attempting to program the first enabling we need to
802*11455SJonathan.Haslam@Sun.COM * invalidate any lwp-based contexts and lay claim to the
803*11455SJonathan.Haslam@Sun.COM * overflow interrupt mechanism.
8048803SJonathan.Haslam@Sun.COM */
805*11455SJonathan.Haslam@Sun.COM if (dcpc_enablings == 0) {
8068803SJonathan.Haslam@Sun.COM kcpc_invalidate_all();
807*11455SJonathan.Haslam@Sun.COM dcpc_claim_interrupts();
808*11455SJonathan.Haslam@Sun.COM }
8098803SJonathan.Haslam@Sun.COM
8108803SJonathan.Haslam@Sun.COM if (dcpc_program_event(pp) == 0) {
8118803SJonathan.Haslam@Sun.COM dcpc_enablings++;
8128803SJonathan.Haslam@Sun.COM return (0);
8138803SJonathan.Haslam@Sun.COM }
8148803SJonathan.Haslam@Sun.COM }
8158803SJonathan.Haslam@Sun.COM
8168803SJonathan.Haslam@Sun.COM /*
8178803SJonathan.Haslam@Sun.COM * If active enablings existed before we failed to enable this probe
8188803SJonathan.Haslam@Sun.COM * on a multi event capable platform then we need to restart counters
8198803SJonathan.Haslam@Sun.COM * as they will have been stopped in the attempted configuration. The
8208803SJonathan.Haslam@Sun.COM * context should now just contain the request prior to this failed
8218803SJonathan.Haslam@Sun.COM * enabling.
8228803SJonathan.Haslam@Sun.COM */
8238803SJonathan.Haslam@Sun.COM if (dcpc_enablings > 0 && dcpc_mult_ovf_cap) {
8248803SJonathan.Haslam@Sun.COM c = cpu_list;
8258803SJonathan.Haslam@Sun.COM
8268803SJonathan.Haslam@Sun.COM ASSERT(dcpc_mult_ovf_cap == 1);
8278803SJonathan.Haslam@Sun.COM do {
8288803SJonathan.Haslam@Sun.COM /*
8298803SJonathan.Haslam@Sun.COM * Skip CPUs that are currently offline.
8308803SJonathan.Haslam@Sun.COM */
8318803SJonathan.Haslam@Sun.COM if (c->cpu_flags & CPU_OFFLINE)
8328803SJonathan.Haslam@Sun.COM continue;
8338803SJonathan.Haslam@Sun.COM
83411389SAlexander.Kolbasov@Sun.COM kcpc_cpu_program(c, c->cpu_cpc_ctx);
8358803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list);
8368803SJonathan.Haslam@Sun.COM }
8378803SJonathan.Haslam@Sun.COM
838*11455SJonathan.Haslam@Sun.COM /*
839*11455SJonathan.Haslam@Sun.COM * Give up any claim to the overflow interrupt mechanism if no
840*11455SJonathan.Haslam@Sun.COM * dcpc based enablings exist.
841*11455SJonathan.Haslam@Sun.COM */
842*11455SJonathan.Haslam@Sun.COM if (dcpc_enablings == 0)
843*11455SJonathan.Haslam@Sun.COM dcpc_surrender_interrupts();
844*11455SJonathan.Haslam@Sun.COM
8458803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use--;
8468803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
8478803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno = -1;
8488803SJonathan.Haslam@Sun.COM
84911389SAlexander.Kolbasov@Sun.COM /*
85011389SAlexander.Kolbasov@Sun.COM * If all probes are removed, enable capacity/utilization data
85111389SAlexander.Kolbasov@Sun.COM * collection for every CPU.
85211389SAlexander.Kolbasov@Sun.COM */
85311389SAlexander.Kolbasov@Sun.COM if (dtrace_cpc_in_use == 0)
85411389SAlexander.Kolbasov@Sun.COM cu_enable();
85511389SAlexander.Kolbasov@Sun.COM
8568803SJonathan.Haslam@Sun.COM return (-1);
8578803SJonathan.Haslam@Sun.COM }
8588803SJonathan.Haslam@Sun.COM
8598803SJonathan.Haslam@Sun.COM /*
8608803SJonathan.Haslam@Sun.COM * If only one enabling is active then remove the context and free
8618803SJonathan.Haslam@Sun.COM * everything up. If there are multiple enablings active then remove this
8628803SJonathan.Haslam@Sun.COM * one, its associated meta-data and re-program the hardware.
8638803SJonathan.Haslam@Sun.COM */
8648803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
8658803SJonathan.Haslam@Sun.COM static void
dcpc_disable(void * arg,dtrace_id_t id,void * parg)8668803SJonathan.Haslam@Sun.COM dcpc_disable(void *arg, dtrace_id_t id, void *parg)
8678803SJonathan.Haslam@Sun.COM {
8688803SJonathan.Haslam@Sun.COM cpu_t *c;
8698803SJonathan.Haslam@Sun.COM dcpc_probe_t *pp = parg;
8708803SJonathan.Haslam@Sun.COM
8718803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
8728803SJonathan.Haslam@Sun.COM
8738803SJonathan.Haslam@Sun.COM kpreempt_disable();
8748803SJonathan.Haslam@Sun.COM
8758803SJonathan.Haslam@Sun.COM /*
8768803SJonathan.Haslam@Sun.COM * This probe didn't actually make it as far as being fully enabled
8778803SJonathan.Haslam@Sun.COM * so we needn't do anything with it.
8788803SJonathan.Haslam@Sun.COM */
8798803SJonathan.Haslam@Sun.COM if (pp->dcpc_enabled == 0) {
8808803SJonathan.Haslam@Sun.COM /*
8818803SJonathan.Haslam@Sun.COM * If we actually allocated this request a slot in the
8828803SJonathan.Haslam@Sun.COM * request array but failed to enabled it then remove the
8838803SJonathan.Haslam@Sun.COM * entry in the array.
8848803SJonathan.Haslam@Sun.COM */
8858803SJonathan.Haslam@Sun.COM if (pp->dcpc_actv_req_idx >= 0) {
8868803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
8878803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno =
8888803SJonathan.Haslam@Sun.COM pp->dcpc_disabling = -1;
8898803SJonathan.Haslam@Sun.COM }
8908803SJonathan.Haslam@Sun.COM
8918803SJonathan.Haslam@Sun.COM kpreempt_enable();
8928803SJonathan.Haslam@Sun.COM return;
8938803SJonathan.Haslam@Sun.COM }
8948803SJonathan.Haslam@Sun.COM
8958803SJonathan.Haslam@Sun.COM /*
8968803SJonathan.Haslam@Sun.COM * If this is the only enabling then stop all the counters and
8978803SJonathan.Haslam@Sun.COM * free up the meta-data.
8988803SJonathan.Haslam@Sun.COM */
8998803SJonathan.Haslam@Sun.COM if (dcpc_enablings == 1) {
9008803SJonathan.Haslam@Sun.COM ASSERT(dtrace_cpc_in_use == 1);
9018803SJonathan.Haslam@Sun.COM
9028803SJonathan.Haslam@Sun.COM dcpc_block_interrupts();
9038803SJonathan.Haslam@Sun.COM
9048803SJonathan.Haslam@Sun.COM c = cpu_list;
9058803SJonathan.Haslam@Sun.COM
9068803SJonathan.Haslam@Sun.COM do {
9078803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(c);
9088803SJonathan.Haslam@Sun.COM } while ((c = c->cpu_next) != cpu_list);
9098803SJonathan.Haslam@Sun.COM
9108803SJonathan.Haslam@Sun.COM dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
911*11455SJonathan.Haslam@Sun.COM dcpc_surrender_interrupts();
9128803SJonathan.Haslam@Sun.COM } else {
9138803SJonathan.Haslam@Sun.COM /*
9148803SJonathan.Haslam@Sun.COM * This platform can support multiple overflow events and
9158803SJonathan.Haslam@Sun.COM * the enabling being disabled is not the last one. Remove this
9168803SJonathan.Haslam@Sun.COM * enabling and re-program the hardware with the new config.
9178803SJonathan.Haslam@Sun.COM */
9188803SJonathan.Haslam@Sun.COM ASSERT(dcpc_mult_ovf_cap);
9198803SJonathan.Haslam@Sun.COM ASSERT(dcpc_enablings > 1);
9208803SJonathan.Haslam@Sun.COM
9218803SJonathan.Haslam@Sun.COM pp->dcpc_disabling = 1;
9228803SJonathan.Haslam@Sun.COM (void) dcpc_program_event(pp);
9238803SJonathan.Haslam@Sun.COM }
9248803SJonathan.Haslam@Sun.COM
9258803SJonathan.Haslam@Sun.COM kpreempt_enable();
9268803SJonathan.Haslam@Sun.COM
9278803SJonathan.Haslam@Sun.COM dcpc_enablings--;
9288803SJonathan.Haslam@Sun.COM dtrace_cpc_in_use--;
9298803SJonathan.Haslam@Sun.COM pp->dcpc_enabled = 0;
9308803SJonathan.Haslam@Sun.COM pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1;
93111389SAlexander.Kolbasov@Sun.COM
93211389SAlexander.Kolbasov@Sun.COM /*
93311389SAlexander.Kolbasov@Sun.COM * If all probes are removed, enable capacity/utilization data
93411389SAlexander.Kolbasov@Sun.COM * collection for every CPU
93511389SAlexander.Kolbasov@Sun.COM */
93611389SAlexander.Kolbasov@Sun.COM if (dtrace_cpc_in_use == 0)
93711389SAlexander.Kolbasov@Sun.COM cu_enable();
9388803SJonathan.Haslam@Sun.COM }
9398803SJonathan.Haslam@Sun.COM
9408803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
9418803SJonathan.Haslam@Sun.COM static int
dcpc_cpu_setup(cpu_setup_t what,processorid_t cpu,void * arg)9428803SJonathan.Haslam@Sun.COM dcpc_cpu_setup(cpu_setup_t what, processorid_t cpu, void *arg)
9438803SJonathan.Haslam@Sun.COM {
9448803SJonathan.Haslam@Sun.COM cpu_t *c;
9458803SJonathan.Haslam@Sun.COM uint8_t *state;
9468803SJonathan.Haslam@Sun.COM
9478803SJonathan.Haslam@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
9488803SJonathan.Haslam@Sun.COM
9498803SJonathan.Haslam@Sun.COM switch (what) {
9508803SJonathan.Haslam@Sun.COM case CPU_OFF:
9518803SJonathan.Haslam@Sun.COM /*
9528803SJonathan.Haslam@Sun.COM * Offline CPUs are not allowed to take part so remove this
9538803SJonathan.Haslam@Sun.COM * CPU if we are actively tracing.
9548803SJonathan.Haslam@Sun.COM */
9558803SJonathan.Haslam@Sun.COM if (dtrace_cpc_in_use) {
9568803SJonathan.Haslam@Sun.COM c = cpu_get(cpu);
9578803SJonathan.Haslam@Sun.COM state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state;
9588803SJonathan.Haslam@Sun.COM
9598803SJonathan.Haslam@Sun.COM /*
9608803SJonathan.Haslam@Sun.COM * Indicate that a configuration is in process in
9618803SJonathan.Haslam@Sun.COM * order to stop overflow interrupts being processed
9628803SJonathan.Haslam@Sun.COM * on this CPU while we disable it.
9638803SJonathan.Haslam@Sun.COM */
9648803SJonathan.Haslam@Sun.COM while (atomic_cas_8(state, DCPC_INTR_FREE,
9658803SJonathan.Haslam@Sun.COM DCPC_INTR_CONFIG) != DCPC_INTR_FREE)
9668803SJonathan.Haslam@Sun.COM continue;
9678803SJonathan.Haslam@Sun.COM
9688803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(c);
9698803SJonathan.Haslam@Sun.COM
9708803SJonathan.Haslam@Sun.COM /*
9718803SJonathan.Haslam@Sun.COM * Reset this CPUs interrupt state as the configuration
9728803SJonathan.Haslam@Sun.COM * has ended.
9738803SJonathan.Haslam@Sun.COM */
9748803SJonathan.Haslam@Sun.COM cpu_core[c->cpu_id].cpuc_dcpc_intr_state =
9758803SJonathan.Haslam@Sun.COM DCPC_INTR_FREE;
9768803SJonathan.Haslam@Sun.COM membar_producer();
9778803SJonathan.Haslam@Sun.COM }
9788803SJonathan.Haslam@Sun.COM break;
9798803SJonathan.Haslam@Sun.COM
9808803SJonathan.Haslam@Sun.COM case CPU_ON:
9818803SJonathan.Haslam@Sun.COM case CPU_SETUP:
9828803SJonathan.Haslam@Sun.COM /*
9838803SJonathan.Haslam@Sun.COM * This CPU is being initialized or brought online so program
9848803SJonathan.Haslam@Sun.COM * it with the current request set if we are actively tracing.
9858803SJonathan.Haslam@Sun.COM */
9868803SJonathan.Haslam@Sun.COM if (dtrace_cpc_in_use) {
9878803SJonathan.Haslam@Sun.COM c = cpu_get(cpu);
9888803SJonathan.Haslam@Sun.COM (void) dcpc_program_cpu_event(c);
9898803SJonathan.Haslam@Sun.COM }
9908803SJonathan.Haslam@Sun.COM break;
9918803SJonathan.Haslam@Sun.COM
9928803SJonathan.Haslam@Sun.COM default:
9938803SJonathan.Haslam@Sun.COM break;
9948803SJonathan.Haslam@Sun.COM }
9958803SJonathan.Haslam@Sun.COM
9968803SJonathan.Haslam@Sun.COM return (0);
9978803SJonathan.Haslam@Sun.COM }
9988803SJonathan.Haslam@Sun.COM
9998803SJonathan.Haslam@Sun.COM static dtrace_pattr_t dcpc_attr = {
10008803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
10018803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
10028803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
10038803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_CPU },
10048803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
10058803SJonathan.Haslam@Sun.COM };
10068803SJonathan.Haslam@Sun.COM
10078803SJonathan.Haslam@Sun.COM static dtrace_pops_t dcpc_pops = {
10088803SJonathan.Haslam@Sun.COM dcpc_provide,
10098803SJonathan.Haslam@Sun.COM NULL,
10108803SJonathan.Haslam@Sun.COM dcpc_enable,
10118803SJonathan.Haslam@Sun.COM dcpc_disable,
10128803SJonathan.Haslam@Sun.COM NULL,
10138803SJonathan.Haslam@Sun.COM NULL,
10148803SJonathan.Haslam@Sun.COM NULL,
10158803SJonathan.Haslam@Sun.COM NULL,
10168803SJonathan.Haslam@Sun.COM dcpc_usermode,
10178803SJonathan.Haslam@Sun.COM dcpc_destroy
10188803SJonathan.Haslam@Sun.COM };
10198803SJonathan.Haslam@Sun.COM
10208803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
10218803SJonathan.Haslam@Sun.COM static int
dcpc_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)10228803SJonathan.Haslam@Sun.COM dcpc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
10238803SJonathan.Haslam@Sun.COM {
10248803SJonathan.Haslam@Sun.COM return (0);
10258803SJonathan.Haslam@Sun.COM }
10268803SJonathan.Haslam@Sun.COM
10278803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
10288803SJonathan.Haslam@Sun.COM static int
dcpc_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)10298803SJonathan.Haslam@Sun.COM dcpc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
10308803SJonathan.Haslam@Sun.COM {
10318803SJonathan.Haslam@Sun.COM int error;
10328803SJonathan.Haslam@Sun.COM
10338803SJonathan.Haslam@Sun.COM switch (infocmd) {
10348803SJonathan.Haslam@Sun.COM case DDI_INFO_DEVT2DEVINFO:
10358803SJonathan.Haslam@Sun.COM *result = (void *)dcpc_devi;
10368803SJonathan.Haslam@Sun.COM error = DDI_SUCCESS;
10378803SJonathan.Haslam@Sun.COM break;
10388803SJonathan.Haslam@Sun.COM case DDI_INFO_DEVT2INSTANCE:
10398803SJonathan.Haslam@Sun.COM *result = (void *)0;
10408803SJonathan.Haslam@Sun.COM error = DDI_SUCCESS;
10418803SJonathan.Haslam@Sun.COM break;
10428803SJonathan.Haslam@Sun.COM default:
10438803SJonathan.Haslam@Sun.COM error = DDI_FAILURE;
10448803SJonathan.Haslam@Sun.COM }
10458803SJonathan.Haslam@Sun.COM return (error);
10468803SJonathan.Haslam@Sun.COM }
10478803SJonathan.Haslam@Sun.COM
10488803SJonathan.Haslam@Sun.COM static int
dcpc_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)10498803SJonathan.Haslam@Sun.COM dcpc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
10508803SJonathan.Haslam@Sun.COM {
10518803SJonathan.Haslam@Sun.COM switch (cmd) {
10528803SJonathan.Haslam@Sun.COM case DDI_DETACH:
10538803SJonathan.Haslam@Sun.COM break;
10548803SJonathan.Haslam@Sun.COM case DDI_SUSPEND:
10558803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS);
10568803SJonathan.Haslam@Sun.COM default:
10578803SJonathan.Haslam@Sun.COM return (DDI_FAILURE);
10588803SJonathan.Haslam@Sun.COM }
10598803SJonathan.Haslam@Sun.COM
10608803SJonathan.Haslam@Sun.COM if (dtrace_unregister(dcpc_pid) != 0)
10618803SJonathan.Haslam@Sun.COM return (DDI_FAILURE);
10628803SJonathan.Haslam@Sun.COM
10638803SJonathan.Haslam@Sun.COM ddi_remove_minor_node(devi, NULL);
10648803SJonathan.Haslam@Sun.COM
10658803SJonathan.Haslam@Sun.COM mutex_enter(&cpu_lock);
10668803SJonathan.Haslam@Sun.COM unregister_cpu_setup_func(dcpc_cpu_setup, NULL);
10678803SJonathan.Haslam@Sun.COM mutex_exit(&cpu_lock);
10688803SJonathan.Haslam@Sun.COM
10698803SJonathan.Haslam@Sun.COM kmem_free(dcpc_actv_reqs, cpc_ncounters * sizeof (dcpc_probe_t *));
10708803SJonathan.Haslam@Sun.COM
10718803SJonathan.Haslam@Sun.COM kcpc_unregister_dcpc();
10728803SJonathan.Haslam@Sun.COM
10738803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS);
10748803SJonathan.Haslam@Sun.COM }
10758803SJonathan.Haslam@Sun.COM
10768803SJonathan.Haslam@Sun.COM static int
dcpc_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)10778803SJonathan.Haslam@Sun.COM dcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
10788803SJonathan.Haslam@Sun.COM {
10798803SJonathan.Haslam@Sun.COM uint_t caps;
10808803SJonathan.Haslam@Sun.COM char *attrs;
10818803SJonathan.Haslam@Sun.COM
10828803SJonathan.Haslam@Sun.COM switch (cmd) {
10838803SJonathan.Haslam@Sun.COM case DDI_ATTACH:
10848803SJonathan.Haslam@Sun.COM break;
10858803SJonathan.Haslam@Sun.COM case DDI_RESUME:
10868803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS);
10878803SJonathan.Haslam@Sun.COM default:
10888803SJonathan.Haslam@Sun.COM return (DDI_FAILURE);
10898803SJonathan.Haslam@Sun.COM }
10908803SJonathan.Haslam@Sun.COM
10918803SJonathan.Haslam@Sun.COM if (kcpc_pcbe_loaded() == -1)
10928803SJonathan.Haslam@Sun.COM return (DDI_FAILURE);
10938803SJonathan.Haslam@Sun.COM
10948803SJonathan.Haslam@Sun.COM caps = kcpc_pcbe_capabilities();
10958803SJonathan.Haslam@Sun.COM
10968803SJonathan.Haslam@Sun.COM if (!(caps & CPC_CAP_OVERFLOW_INTERRUPT)) {
10979397SJonathan.Haslam@Sun.COM cmn_err(CE_NOTE, "!dcpc: Counter Overflow not supported"\
10989397SJonathan.Haslam@Sun.COM " on this processor");
10998803SJonathan.Haslam@Sun.COM return (DDI_FAILURE);
11008803SJonathan.Haslam@Sun.COM }
11018803SJonathan.Haslam@Sun.COM
11028803SJonathan.Haslam@Sun.COM if (ddi_create_minor_node(devi, "dcpc", S_IFCHR, 0,
11038803SJonathan.Haslam@Sun.COM DDI_PSEUDO, NULL) == DDI_FAILURE ||
11048803SJonathan.Haslam@Sun.COM dtrace_register("cpc", &dcpc_attr, DTRACE_PRIV_KERNEL,
11058803SJonathan.Haslam@Sun.COM NULL, &dcpc_pops, NULL, &dcpc_pid) != 0) {
11068803SJonathan.Haslam@Sun.COM ddi_remove_minor_node(devi, NULL);
11078803SJonathan.Haslam@Sun.COM return (DDI_FAILURE);
11088803SJonathan.Haslam@Sun.COM }
11098803SJonathan.Haslam@Sun.COM
11108803SJonathan.Haslam@Sun.COM mutex_enter(&cpu_lock);
11118803SJonathan.Haslam@Sun.COM register_cpu_setup_func(dcpc_cpu_setup, NULL);
11128803SJonathan.Haslam@Sun.COM mutex_exit(&cpu_lock);
11138803SJonathan.Haslam@Sun.COM
11148803SJonathan.Haslam@Sun.COM dcpc_ovf_mask = (1 << cpc_ncounters) - 1;
11158803SJonathan.Haslam@Sun.COM ASSERT(dcpc_ovf_mask != 0);
11168803SJonathan.Haslam@Sun.COM
11178803SJonathan.Haslam@Sun.COM if (caps & CPC_CAP_OVERFLOW_PRECISE)
11188803SJonathan.Haslam@Sun.COM dcpc_mult_ovf_cap = 1;
11198803SJonathan.Haslam@Sun.COM
11208803SJonathan.Haslam@Sun.COM /*
11218803SJonathan.Haslam@Sun.COM * Determine which, if any, mask attribute the back-end can use.
11228803SJonathan.Haslam@Sun.COM */
11238803SJonathan.Haslam@Sun.COM attrs = kcpc_list_attrs();
11248803SJonathan.Haslam@Sun.COM if (strstr(attrs, "umask") != NULL)
11258803SJonathan.Haslam@Sun.COM dcpc_mask_type |= DCPC_UMASK;
11268803SJonathan.Haslam@Sun.COM else if (strstr(attrs, "emask") != NULL)
11278803SJonathan.Haslam@Sun.COM dcpc_mask_type |= DCPC_EMASK;
11288803SJonathan.Haslam@Sun.COM
11298803SJonathan.Haslam@Sun.COM /*
11308803SJonathan.Haslam@Sun.COM * The dcpc_actv_reqs array is used to store the requests that
11318803SJonathan.Haslam@Sun.COM * we currently have programmed. The order of requests in this
11328803SJonathan.Haslam@Sun.COM * array is not necessarily the order that the event appears in
11338803SJonathan.Haslam@Sun.COM * the kcpc_request_t array. Once entered into a slot in the array
11348803SJonathan.Haslam@Sun.COM * the entry is not moved until it's removed.
11358803SJonathan.Haslam@Sun.COM */
11368803SJonathan.Haslam@Sun.COM dcpc_actv_reqs =
11378803SJonathan.Haslam@Sun.COM kmem_zalloc(cpc_ncounters * sizeof (dcpc_probe_t *), KM_SLEEP);
11388803SJonathan.Haslam@Sun.COM
11398803SJonathan.Haslam@Sun.COM dcpc_min_overflow = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
11408803SJonathan.Haslam@Sun.COM DDI_PROP_DONTPASS, "dcpc-min-overflow", DCPC_MIN_OVF_DEFAULT);
11418803SJonathan.Haslam@Sun.COM
11428803SJonathan.Haslam@Sun.COM kcpc_register_dcpc(dcpc_fire);
11438803SJonathan.Haslam@Sun.COM
11448803SJonathan.Haslam@Sun.COM ddi_report_dev(devi);
11458803SJonathan.Haslam@Sun.COM dcpc_devi = devi;
11468803SJonathan.Haslam@Sun.COM
11478803SJonathan.Haslam@Sun.COM return (DDI_SUCCESS);
11488803SJonathan.Haslam@Sun.COM }
11498803SJonathan.Haslam@Sun.COM
11508803SJonathan.Haslam@Sun.COM static struct cb_ops dcpc_cb_ops = {
11518803SJonathan.Haslam@Sun.COM dcpc_open, /* open */
11528803SJonathan.Haslam@Sun.COM nodev, /* close */
11538803SJonathan.Haslam@Sun.COM nulldev, /* strategy */
11548803SJonathan.Haslam@Sun.COM nulldev, /* print */
11558803SJonathan.Haslam@Sun.COM nodev, /* dump */
11568803SJonathan.Haslam@Sun.COM nodev, /* read */
11578803SJonathan.Haslam@Sun.COM nodev, /* write */
11588803SJonathan.Haslam@Sun.COM nodev, /* ioctl */
11598803SJonathan.Haslam@Sun.COM nodev, /* devmap */
11608803SJonathan.Haslam@Sun.COM nodev, /* mmap */
11618803SJonathan.Haslam@Sun.COM nodev, /* segmap */
11628803SJonathan.Haslam@Sun.COM nochpoll, /* poll */
11638803SJonathan.Haslam@Sun.COM ddi_prop_op, /* cb_prop_op */
11648803SJonathan.Haslam@Sun.COM 0, /* streamtab */
11658803SJonathan.Haslam@Sun.COM D_NEW | D_MP /* Driver compatibility flag */
11668803SJonathan.Haslam@Sun.COM };
11678803SJonathan.Haslam@Sun.COM
11688803SJonathan.Haslam@Sun.COM static struct dev_ops dcpc_ops = {
11698803SJonathan.Haslam@Sun.COM DEVO_REV, /* devo_rev, */
11708803SJonathan.Haslam@Sun.COM 0, /* refcnt */
11718803SJonathan.Haslam@Sun.COM dcpc_info, /* get_dev_info */
11728803SJonathan.Haslam@Sun.COM nulldev, /* identify */
11738803SJonathan.Haslam@Sun.COM nulldev, /* probe */
11748803SJonathan.Haslam@Sun.COM dcpc_attach, /* attach */
11758803SJonathan.Haslam@Sun.COM dcpc_detach, /* detach */
11768803SJonathan.Haslam@Sun.COM nodev, /* reset */
11778803SJonathan.Haslam@Sun.COM &dcpc_cb_ops, /* driver operations */
11788803SJonathan.Haslam@Sun.COM NULL, /* bus operations */
11798803SJonathan.Haslam@Sun.COM nodev, /* dev power */
11808803SJonathan.Haslam@Sun.COM ddi_quiesce_not_needed /* quiesce */
11818803SJonathan.Haslam@Sun.COM };
11828803SJonathan.Haslam@Sun.COM
11838803SJonathan.Haslam@Sun.COM /*
11848803SJonathan.Haslam@Sun.COM * Module linkage information for the kernel.
11858803SJonathan.Haslam@Sun.COM */
11868803SJonathan.Haslam@Sun.COM static struct modldrv modldrv = {
11878803SJonathan.Haslam@Sun.COM &mod_driverops, /* module type */
11888803SJonathan.Haslam@Sun.COM "DTrace CPC Module", /* name of module */
11898803SJonathan.Haslam@Sun.COM &dcpc_ops, /* driver ops */
11908803SJonathan.Haslam@Sun.COM };
11918803SJonathan.Haslam@Sun.COM
11928803SJonathan.Haslam@Sun.COM static struct modlinkage modlinkage = {
11938803SJonathan.Haslam@Sun.COM MODREV_1,
11948803SJonathan.Haslam@Sun.COM (void *)&modldrv,
11958803SJonathan.Haslam@Sun.COM NULL
11968803SJonathan.Haslam@Sun.COM };
11978803SJonathan.Haslam@Sun.COM
11988803SJonathan.Haslam@Sun.COM int
_init(void)11998803SJonathan.Haslam@Sun.COM _init(void)
12008803SJonathan.Haslam@Sun.COM {
12018803SJonathan.Haslam@Sun.COM return (mod_install(&modlinkage));
12028803SJonathan.Haslam@Sun.COM }
12038803SJonathan.Haslam@Sun.COM
12048803SJonathan.Haslam@Sun.COM int
_info(struct modinfo * modinfop)12058803SJonathan.Haslam@Sun.COM _info(struct modinfo *modinfop)
12068803SJonathan.Haslam@Sun.COM {
12078803SJonathan.Haslam@Sun.COM return (mod_info(&modlinkage, modinfop));
12088803SJonathan.Haslam@Sun.COM }
12098803SJonathan.Haslam@Sun.COM
12108803SJonathan.Haslam@Sun.COM int
_fini(void)12118803SJonathan.Haslam@Sun.COM _fini(void)
12128803SJonathan.Haslam@Sun.COM {
12138803SJonathan.Haslam@Sun.COM return (mod_remove(&modlinkage));
12148803SJonathan.Haslam@Sun.COM }
1215