111389SAlexander.Kolbasov@Sun.COM /*
211389SAlexander.Kolbasov@Sun.COM * CDDL HEADER START
311389SAlexander.Kolbasov@Sun.COM *
411389SAlexander.Kolbasov@Sun.COM * The contents of this file are subject to the terms of the
511389SAlexander.Kolbasov@Sun.COM * Common Development and Distribution License (the "License").
611389SAlexander.Kolbasov@Sun.COM * You may not use this file except in compliance with the License.
711389SAlexander.Kolbasov@Sun.COM *
811389SAlexander.Kolbasov@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911389SAlexander.Kolbasov@Sun.COM * or http://www.opensolaris.org/os/licensing.
1011389SAlexander.Kolbasov@Sun.COM * See the License for the specific language governing permissions
1111389SAlexander.Kolbasov@Sun.COM * and limitations under the License.
1211389SAlexander.Kolbasov@Sun.COM *
1311389SAlexander.Kolbasov@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1411389SAlexander.Kolbasov@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511389SAlexander.Kolbasov@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1611389SAlexander.Kolbasov@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1711389SAlexander.Kolbasov@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1811389SAlexander.Kolbasov@Sun.COM *
1911389SAlexander.Kolbasov@Sun.COM * CDDL HEADER END
2011389SAlexander.Kolbasov@Sun.COM */
2111389SAlexander.Kolbasov@Sun.COM
2211389SAlexander.Kolbasov@Sun.COM /*
23*13124SAlexander.Kolbasov@Sun.COM * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2411389SAlexander.Kolbasov@Sun.COM */
2511389SAlexander.Kolbasov@Sun.COM
2611389SAlexander.Kolbasov@Sun.COM /*
2711389SAlexander.Kolbasov@Sun.COM * Support for determining capacity and utilization of performance relevant
2811389SAlexander.Kolbasov@Sun.COM * hardware components in a computer
2911389SAlexander.Kolbasov@Sun.COM *
3011389SAlexander.Kolbasov@Sun.COM * THEORY
3111389SAlexander.Kolbasov@Sun.COM * ------
3211389SAlexander.Kolbasov@Sun.COM * The capacity and utilization of the performance relevant hardware components
3311389SAlexander.Kolbasov@Sun.COM * is needed to be able to optimize performance while minimizing the amount of
3411389SAlexander.Kolbasov@Sun.COM * power used on a system. The idea is to use hardware performance counters
3511389SAlexander.Kolbasov@Sun.COM * and potentially other means to determine the capacity and utilization of
3611389SAlexander.Kolbasov@Sun.COM * performance relevant hardware components (eg. execution pipeline, cache,
3711389SAlexander.Kolbasov@Sun.COM * memory, etc.) and attribute the utilization to the responsible CPU and the
3811389SAlexander.Kolbasov@Sun.COM * thread running there.
3911389SAlexander.Kolbasov@Sun.COM *
4011389SAlexander.Kolbasov@Sun.COM * This will help characterize the utilization of performance relevant
4111389SAlexander.Kolbasov@Sun.COM * components and how much is used by each CPU and each thread. With
4211389SAlexander.Kolbasov@Sun.COM * that data, the utilization can be aggregated to all the CPUs sharing each
4311389SAlexander.Kolbasov@Sun.COM * performance relevant hardware component to calculate the total utilization
4411389SAlexander.Kolbasov@Sun.COM * of each component and compare that with the component's capacity to
4511389SAlexander.Kolbasov@Sun.COM * essentially determine the actual hardware load of the component. The
4611389SAlexander.Kolbasov@Sun.COM * hardware utilization attributed to each running thread can also be
4711389SAlexander.Kolbasov@Sun.COM * aggregated to determine the total hardware utilization of each component to
4811389SAlexander.Kolbasov@Sun.COM * a workload.
4911389SAlexander.Kolbasov@Sun.COM *
5011389SAlexander.Kolbasov@Sun.COM * Once that is done, one can determine how much of each performance relevant
5111389SAlexander.Kolbasov@Sun.COM * hardware component is needed by a given thread or set of threads (eg. a
5211389SAlexander.Kolbasov@Sun.COM * workload) and size up exactly what hardware is needed by the threads and how
5311389SAlexander.Kolbasov@Sun.COM * much. With this info, we can better place threads among CPUs to match their
5411389SAlexander.Kolbasov@Sun.COM * exact hardware resource needs and potentially lower or raise the power based
5511389SAlexander.Kolbasov@Sun.COM * on their utilization or pack threads onto the fewest hardware components
5611389SAlexander.Kolbasov@Sun.COM * needed and power off any remaining unused components to minimize power
5711389SAlexander.Kolbasov@Sun.COM * without sacrificing performance.
5811389SAlexander.Kolbasov@Sun.COM *
5911389SAlexander.Kolbasov@Sun.COM * IMPLEMENTATION
6011389SAlexander.Kolbasov@Sun.COM * --------------
6111389SAlexander.Kolbasov@Sun.COM * The code has been designed and implemented to make (un)programming and
6211389SAlexander.Kolbasov@Sun.COM * reading the counters for a given CPU as lightweight and fast as possible.
6311389SAlexander.Kolbasov@Sun.COM * This is very important because we need to read and potentially (un)program
6411389SAlexander.Kolbasov@Sun.COM * the counters very often and in performance sensitive code. Specifically,
6511389SAlexander.Kolbasov@Sun.COM * the counters may need to be (un)programmed during context switch and/or a
6611389SAlexander.Kolbasov@Sun.COM * cyclic handler when there are more counter events to count than existing
6711389SAlexander.Kolbasov@Sun.COM * counters.
6811389SAlexander.Kolbasov@Sun.COM *
6911389SAlexander.Kolbasov@Sun.COM * Consequently, the code has been split up to allow allocating and
7011389SAlexander.Kolbasov@Sun.COM * initializing everything needed to program and read the counters on a given
7111389SAlexander.Kolbasov@Sun.COM * CPU once and make (un)programming and reading the counters for a given CPU
7211389SAlexander.Kolbasov@Sun.COM * not have to allocate/free memory or grab any locks. To do this, all the
7311389SAlexander.Kolbasov@Sun.COM * state needed to (un)program and read the counters on a CPU is kept per CPU
7411389SAlexander.Kolbasov@Sun.COM * and is made lock free by forcing any code that reads or manipulates the
7511389SAlexander.Kolbasov@Sun.COM * counters or the state needed to (un)program or read the counters to run on
7611389SAlexander.Kolbasov@Sun.COM * the target CPU and disable preemption while running on the target CPU to
7711389SAlexander.Kolbasov@Sun.COM * protect any critical sections. All counter manipulation on the target CPU is
7811389SAlexander.Kolbasov@Sun.COM * happening either from a cross-call to the target CPU or at the same PIL as
7911389SAlexander.Kolbasov@Sun.COM * used by the cross-call subsystem. This guarantees that counter manipulation
8011389SAlexander.Kolbasov@Sun.COM * is not interrupted by cross-calls from other CPUs.
8111389SAlexander.Kolbasov@Sun.COM *
8211389SAlexander.Kolbasov@Sun.COM * The synchronization has been made lock free or as simple as possible for
8311389SAlexander.Kolbasov@Sun.COM * performance and to avoid getting the locking all tangled up when we interpose
8411389SAlexander.Kolbasov@Sun.COM * on the CPC routines that (un)program the counters to manage the counters
8511389SAlexander.Kolbasov@Sun.COM * between the kernel and user on each CPU. When the user starts using the
8611389SAlexander.Kolbasov@Sun.COM * counters on a given CPU, the kernel will unprogram the counters that it is
8711389SAlexander.Kolbasov@Sun.COM * using on that CPU just before they are programmed for the user. Then the
8811389SAlexander.Kolbasov@Sun.COM * kernel will program the counters on a given CPU for its own use when the user
8911389SAlexander.Kolbasov@Sun.COM * stops using them.
9011389SAlexander.Kolbasov@Sun.COM *
9111389SAlexander.Kolbasov@Sun.COM * There is a special interaction with DTrace cpc provider (dcpc). Before dcpc
9211389SAlexander.Kolbasov@Sun.COM * enables any probe, it requests to disable and unprogram all counters used for
9311389SAlexander.Kolbasov@Sun.COM * capacity and utilizations. These counters are never re-programmed back until
9411389SAlexander.Kolbasov@Sun.COM * dcpc completes. When all DTrace cpc probes are removed, dcpc notifies CU
9511389SAlexander.Kolbasov@Sun.COM * framework and it re-programs the counters.
9611389SAlexander.Kolbasov@Sun.COM *
9711389SAlexander.Kolbasov@Sun.COM * When a CPU is going offline, its CU counters are unprogrammed and disabled,
9811389SAlexander.Kolbasov@Sun.COM * so that they would not be re-programmed again by some other activity on the
9911389SAlexander.Kolbasov@Sun.COM * CPU that is going offline.
10011389SAlexander.Kolbasov@Sun.COM *
10111389SAlexander.Kolbasov@Sun.COM * The counters are programmed during boot. However, a flag is available to
10211389SAlexander.Kolbasov@Sun.COM * disable this if necessary (see cu_flag below). A handler is provided to
10311389SAlexander.Kolbasov@Sun.COM * (un)program the counters during CPU on/offline. Basic routines are provided
10411389SAlexander.Kolbasov@Sun.COM * to initialize and tear down this module, initialize and tear down any state
10511389SAlexander.Kolbasov@Sun.COM * needed for a given CPU, and (un)program the counters for a given CPU.
10611389SAlexander.Kolbasov@Sun.COM * Lastly, a handler is provided to read the counters and attribute the
10711389SAlexander.Kolbasov@Sun.COM * utilization to the responsible CPU.
10811389SAlexander.Kolbasov@Sun.COM */
10911389SAlexander.Kolbasov@Sun.COM #include <sys/types.h>
11011389SAlexander.Kolbasov@Sun.COM #include <sys/cmn_err.h>
11111389SAlexander.Kolbasov@Sun.COM #include <sys/cpuvar.h>
11211389SAlexander.Kolbasov@Sun.COM #include <sys/ddi.h>
113*13124SAlexander.Kolbasov@Sun.COM #include <sys/systm.h>
11411389SAlexander.Kolbasov@Sun.COM #include <sys/disp.h>
11511389SAlexander.Kolbasov@Sun.COM #include <sys/sdt.h>
11611389SAlexander.Kolbasov@Sun.COM #include <sys/sunddi.h>
11711389SAlexander.Kolbasov@Sun.COM #include <sys/thread.h>
11811389SAlexander.Kolbasov@Sun.COM #include <sys/pghw.h>
11911389SAlexander.Kolbasov@Sun.COM #include <sys/cmt.h>
120*13124SAlexander.Kolbasov@Sun.COM #include <sys/policy.h>
12111389SAlexander.Kolbasov@Sun.COM #include <sys/x_call.h>
12211389SAlexander.Kolbasov@Sun.COM #include <sys/cap_util.h>
12311389SAlexander.Kolbasov@Sun.COM
12411389SAlexander.Kolbasov@Sun.COM #include <sys/archsystm.h>
12511389SAlexander.Kolbasov@Sun.COM #include <sys/promif.h>
12611389SAlexander.Kolbasov@Sun.COM
12711389SAlexander.Kolbasov@Sun.COM #if defined(__x86)
12811389SAlexander.Kolbasov@Sun.COM #include <sys/xc_levels.h>
12911389SAlexander.Kolbasov@Sun.COM #endif
13011389SAlexander.Kolbasov@Sun.COM
13111389SAlexander.Kolbasov@Sun.COM
13211389SAlexander.Kolbasov@Sun.COM /*
13311389SAlexander.Kolbasov@Sun.COM * Default CPU hardware performance counter flags to use for measuring capacity
13411389SAlexander.Kolbasov@Sun.COM * and utilization
13511389SAlexander.Kolbasov@Sun.COM */
13611389SAlexander.Kolbasov@Sun.COM #define CU_CPC_FLAGS_DEFAULT \
13711389SAlexander.Kolbasov@Sun.COM (CPC_COUNT_USER|CPC_COUNT_SYSTEM|CPC_OVF_NOTIFY_EMT)
13811389SAlexander.Kolbasov@Sun.COM
13911389SAlexander.Kolbasov@Sun.COM /*
14011389SAlexander.Kolbasov@Sun.COM * Possible Flags for controlling this module.
14111389SAlexander.Kolbasov@Sun.COM */
14211389SAlexander.Kolbasov@Sun.COM #define CU_FLAG_ENABLE 1 /* Enable module */
14311389SAlexander.Kolbasov@Sun.COM #define CU_FLAG_READY 2 /* Ready to setup module */
14411389SAlexander.Kolbasov@Sun.COM #define CU_FLAG_ON 4 /* Module is on */
14511389SAlexander.Kolbasov@Sun.COM
14611389SAlexander.Kolbasov@Sun.COM /*
14711389SAlexander.Kolbasov@Sun.COM * pg_cpu kstats calculate utilization rate and maximum utilization rate for
14811389SAlexander.Kolbasov@Sun.COM * some CPUs. The rate is calculated based on data from two subsequent
14911389SAlexander.Kolbasov@Sun.COM * snapshots. When the time between such two snapshots is too small, the
15011389SAlexander.Kolbasov@Sun.COM * resulting rate may have low accuracy, so we only consider snapshots which
15111389SAlexander.Kolbasov@Sun.COM * are separated by SAMPLE_INTERVAL nanoseconds from one another. We do not
15211389SAlexander.Kolbasov@Sun.COM * update the rate if the interval is smaller than that.
15311389SAlexander.Kolbasov@Sun.COM *
15411389SAlexander.Kolbasov@Sun.COM * Use one tenth of a second as the minimum interval for utilization rate
15511389SAlexander.Kolbasov@Sun.COM * calculation.
15611389SAlexander.Kolbasov@Sun.COM *
15711389SAlexander.Kolbasov@Sun.COM * NOTE: The CU_SAMPLE_INTERVAL_MIN should be higher than the scaling factor in
15811389SAlexander.Kolbasov@Sun.COM * the CU_RATE() macro below to guarantee that we never divide by zero.
15911389SAlexander.Kolbasov@Sun.COM *
16011389SAlexander.Kolbasov@Sun.COM * Rate is the number of events per second. The rate is the number of events
16111389SAlexander.Kolbasov@Sun.COM * divided by time and multiplied by the number of nanoseconds in a second. We
16211389SAlexander.Kolbasov@Sun.COM * do not want time to be too small since it will cause large errors in
16311389SAlexander.Kolbasov@Sun.COM * division.
16411389SAlexander.Kolbasov@Sun.COM *
16511389SAlexander.Kolbasov@Sun.COM * We do not want to multiply two large numbers (the instruction count and
16611389SAlexander.Kolbasov@Sun.COM * NANOSEC) either since it may cause integer overflow. So we divide both the
16711389SAlexander.Kolbasov@Sun.COM * numerator and the denominator by the same value.
16811389SAlexander.Kolbasov@Sun.COM *
16911389SAlexander.Kolbasov@Sun.COM * NOTE: The scaling factor below should be less than CU_SAMPLE_INTERVAL_MIN
17011389SAlexander.Kolbasov@Sun.COM * above to guarantee that time divided by this value is always non-zero.
17111389SAlexander.Kolbasov@Sun.COM */
17211389SAlexander.Kolbasov@Sun.COM #define CU_RATE(val, time) \
17311389SAlexander.Kolbasov@Sun.COM (((val) * (NANOSEC / CU_SCALE)) / ((time) / CU_SCALE))
17411389SAlexander.Kolbasov@Sun.COM
17511389SAlexander.Kolbasov@Sun.COM #define CU_SAMPLE_INTERVAL_MIN (NANOSEC / 10)
17611389SAlexander.Kolbasov@Sun.COM
17711389SAlexander.Kolbasov@Sun.COM #define CU_SCALE (CU_SAMPLE_INTERVAL_MIN / 10000)
17811389SAlexander.Kolbasov@Sun.COM
17911389SAlexander.Kolbasov@Sun.COM /*
18011389SAlexander.Kolbasov@Sun.COM * When the time between two kstat reads for the same CPU is less than
18111389SAlexander.Kolbasov@Sun.COM * CU_UPDATE_THRESHOLD use the old counter data and skip updating counter values
18211389SAlexander.Kolbasov@Sun.COM * for the CPU. This helps reduce cross-calls when kstat consumers read data
18311389SAlexander.Kolbasov@Sun.COM * very often or when they read PG utilization data and then CPU utilization
18411389SAlexander.Kolbasov@Sun.COM * data quickly after that.
18511389SAlexander.Kolbasov@Sun.COM */
18611389SAlexander.Kolbasov@Sun.COM #define CU_UPDATE_THRESHOLD (NANOSEC / 10)
18711389SAlexander.Kolbasov@Sun.COM
18811389SAlexander.Kolbasov@Sun.COM /*
18911389SAlexander.Kolbasov@Sun.COM * The IS_HIPIL() macro verifies that the code is executed either from a
19011389SAlexander.Kolbasov@Sun.COM * cross-call or from high-PIL interrupt
19111389SAlexander.Kolbasov@Sun.COM */
19211389SAlexander.Kolbasov@Sun.COM #ifdef DEBUG
19311389SAlexander.Kolbasov@Sun.COM #define IS_HIPIL() (getpil() >= XCALL_PIL)
19411389SAlexander.Kolbasov@Sun.COM #else
19511389SAlexander.Kolbasov@Sun.COM #define IS_HIPIL()
19611389SAlexander.Kolbasov@Sun.COM #endif /* DEBUG */
19711389SAlexander.Kolbasov@Sun.COM
19811389SAlexander.Kolbasov@Sun.COM
19911389SAlexander.Kolbasov@Sun.COM typedef void (*cu_cpu_func_t)(uintptr_t, int *);
20011389SAlexander.Kolbasov@Sun.COM
20111389SAlexander.Kolbasov@Sun.COM
20211389SAlexander.Kolbasov@Sun.COM /*
20311389SAlexander.Kolbasov@Sun.COM * Flags to use for programming CPU hardware performance counters to measure
20411389SAlexander.Kolbasov@Sun.COM * capacity and utilization
20511389SAlexander.Kolbasov@Sun.COM */
20611389SAlexander.Kolbasov@Sun.COM int cu_cpc_flags = CU_CPC_FLAGS_DEFAULT;
20711389SAlexander.Kolbasov@Sun.COM
20811389SAlexander.Kolbasov@Sun.COM /*
20911389SAlexander.Kolbasov@Sun.COM * Initial value used for programming hardware counters
21011389SAlexander.Kolbasov@Sun.COM */
21111389SAlexander.Kolbasov@Sun.COM uint64_t cu_cpc_preset_value = 0;
21211389SAlexander.Kolbasov@Sun.COM
21311389SAlexander.Kolbasov@Sun.COM /*
21411389SAlexander.Kolbasov@Sun.COM * List of CPC event requests for capacity and utilization.
21511389SAlexander.Kolbasov@Sun.COM */
21611389SAlexander.Kolbasov@Sun.COM static kcpc_request_list_t *cu_cpc_reqs = NULL;
21711389SAlexander.Kolbasov@Sun.COM
21811389SAlexander.Kolbasov@Sun.COM /*
21911389SAlexander.Kolbasov@Sun.COM * When a CPU is a member of PG with a sharing relationship that is supported
22011389SAlexander.Kolbasov@Sun.COM * by the capacity/utilization framework, a kstat is created for that CPU and
22111389SAlexander.Kolbasov@Sun.COM * sharing relationship.
22211389SAlexander.Kolbasov@Sun.COM *
22311389SAlexander.Kolbasov@Sun.COM * These kstats are updated one at a time, so we can have a single scratch
22411389SAlexander.Kolbasov@Sun.COM * space to fill the data.
22511389SAlexander.Kolbasov@Sun.COM *
22611389SAlexander.Kolbasov@Sun.COM * CPU counter kstats fields:
22711389SAlexander.Kolbasov@Sun.COM *
22811389SAlexander.Kolbasov@Sun.COM * cu_cpu_id CPU ID for this kstat
22911389SAlexander.Kolbasov@Sun.COM *
230*13124SAlexander.Kolbasov@Sun.COM * cu_pg_id PG ID for this kstat
231*13124SAlexander.Kolbasov@Sun.COM *
23211389SAlexander.Kolbasov@Sun.COM * cu_generation Generation value that increases whenever any CPU goes
23311389SAlexander.Kolbasov@Sun.COM * offline or online. Two kstat snapshots for the same
23411389SAlexander.Kolbasov@Sun.COM * CPU may only be compared if they have the same
23511389SAlexander.Kolbasov@Sun.COM * generation.
23611389SAlexander.Kolbasov@Sun.COM *
23711389SAlexander.Kolbasov@Sun.COM * cu_pg_id PG ID for the relationship described by this kstat
23811389SAlexander.Kolbasov@Sun.COM *
23911389SAlexander.Kolbasov@Sun.COM * cu_cpu_util Running value of CPU utilization for the sharing
24011389SAlexander.Kolbasov@Sun.COM * relationship
24111389SAlexander.Kolbasov@Sun.COM *
24211389SAlexander.Kolbasov@Sun.COM * cu_cpu_time_running Total time spent collecting CU data. The time may be
24311389SAlexander.Kolbasov@Sun.COM * less than wall time if CU counters were stopped for
24411389SAlexander.Kolbasov@Sun.COM * some time.
24511389SAlexander.Kolbasov@Sun.COM *
24611389SAlexander.Kolbasov@Sun.COM * cu_cpu_time_stopped Total time the CU counters were stopped.
24711389SAlexander.Kolbasov@Sun.COM *
24811389SAlexander.Kolbasov@Sun.COM * cu_cpu_rate Utilization rate, expressed in operations per second.
24911389SAlexander.Kolbasov@Sun.COM *
25011389SAlexander.Kolbasov@Sun.COM * cu_cpu_rate_max Maximum observed value of utilization rate.
251*13124SAlexander.Kolbasov@Sun.COM *
252*13124SAlexander.Kolbasov@Sun.COM * cu_cpu_relationship Name of sharing relationship for the PG in this kstat
25311389SAlexander.Kolbasov@Sun.COM */
25411389SAlexander.Kolbasov@Sun.COM struct cu_cpu_kstat {
25511389SAlexander.Kolbasov@Sun.COM kstat_named_t cu_cpu_id;
256*13124SAlexander.Kolbasov@Sun.COM kstat_named_t cu_pg_id;
25711389SAlexander.Kolbasov@Sun.COM kstat_named_t cu_generation;
25811389SAlexander.Kolbasov@Sun.COM kstat_named_t cu_cpu_util;
25911389SAlexander.Kolbasov@Sun.COM kstat_named_t cu_cpu_time_running;
26011389SAlexander.Kolbasov@Sun.COM kstat_named_t cu_cpu_time_stopped;
26111389SAlexander.Kolbasov@Sun.COM kstat_named_t cu_cpu_rate;
26211389SAlexander.Kolbasov@Sun.COM kstat_named_t cu_cpu_rate_max;
263*13124SAlexander.Kolbasov@Sun.COM kstat_named_t cu_cpu_relationship;
26411389SAlexander.Kolbasov@Sun.COM } cu_cpu_kstat = {
265*13124SAlexander.Kolbasov@Sun.COM { "cpu_id", KSTAT_DATA_UINT32 },
266*13124SAlexander.Kolbasov@Sun.COM { "pg_id", KSTAT_DATA_INT32 },
26711389SAlexander.Kolbasov@Sun.COM { "generation", KSTAT_DATA_UINT32 },
26811389SAlexander.Kolbasov@Sun.COM { "hw_util", KSTAT_DATA_UINT64 },
26911389SAlexander.Kolbasov@Sun.COM { "hw_util_time_running", KSTAT_DATA_UINT64 },
27011389SAlexander.Kolbasov@Sun.COM { "hw_util_time_stopped", KSTAT_DATA_UINT64 },
27111389SAlexander.Kolbasov@Sun.COM { "hw_util_rate", KSTAT_DATA_UINT64 },
27211389SAlexander.Kolbasov@Sun.COM { "hw_util_rate_max", KSTAT_DATA_UINT64 },
273*13124SAlexander.Kolbasov@Sun.COM { "relationship", KSTAT_DATA_STRING },
27411389SAlexander.Kolbasov@Sun.COM };
27511389SAlexander.Kolbasov@Sun.COM
27611389SAlexander.Kolbasov@Sun.COM /*
27711389SAlexander.Kolbasov@Sun.COM * Flags for controlling this module
27811389SAlexander.Kolbasov@Sun.COM */
27911389SAlexander.Kolbasov@Sun.COM uint_t cu_flags = CU_FLAG_ENABLE;
28011389SAlexander.Kolbasov@Sun.COM
28111389SAlexander.Kolbasov@Sun.COM /*
28211389SAlexander.Kolbasov@Sun.COM * Error return value for cu_init() since it can't return anything to be called
28311389SAlexander.Kolbasov@Sun.COM * from mp_init_tbl[] (:-(
28411389SAlexander.Kolbasov@Sun.COM */
28511389SAlexander.Kolbasov@Sun.COM static int cu_init_error = 0;
28611389SAlexander.Kolbasov@Sun.COM
28711389SAlexander.Kolbasov@Sun.COM hrtime_t cu_sample_interval_min = CU_SAMPLE_INTERVAL_MIN;
28811389SAlexander.Kolbasov@Sun.COM
28911389SAlexander.Kolbasov@Sun.COM hrtime_t cu_update_threshold = CU_UPDATE_THRESHOLD;
29011389SAlexander.Kolbasov@Sun.COM
29111389SAlexander.Kolbasov@Sun.COM static kmutex_t pg_cpu_kstat_lock;
29211389SAlexander.Kolbasov@Sun.COM
29311389SAlexander.Kolbasov@Sun.COM
29411389SAlexander.Kolbasov@Sun.COM /*
29511389SAlexander.Kolbasov@Sun.COM * Forward declaration of interface routines
29611389SAlexander.Kolbasov@Sun.COM */
29711389SAlexander.Kolbasov@Sun.COM void cu_disable(void);
29811389SAlexander.Kolbasov@Sun.COM void cu_enable(void);
29911389SAlexander.Kolbasov@Sun.COM void cu_init(void);
30011389SAlexander.Kolbasov@Sun.COM void cu_cpc_program(cpu_t *cp, int *err);
30111389SAlexander.Kolbasov@Sun.COM void cu_cpc_unprogram(cpu_t *cp, int *err);
30211389SAlexander.Kolbasov@Sun.COM int cu_cpu_update(struct cpu *cp, boolean_t move_to);
30311389SAlexander.Kolbasov@Sun.COM void cu_pg_update(pghw_t *pg);
30411389SAlexander.Kolbasov@Sun.COM
30511389SAlexander.Kolbasov@Sun.COM
30611389SAlexander.Kolbasov@Sun.COM /*
30711389SAlexander.Kolbasov@Sun.COM * Forward declaration of private routines
30811389SAlexander.Kolbasov@Sun.COM */
30911389SAlexander.Kolbasov@Sun.COM static int cu_cpc_init(cpu_t *cp, kcpc_request_list_t *reqs, int nreqs);
31011389SAlexander.Kolbasov@Sun.COM static void cu_cpc_program_xcall(uintptr_t arg, int *err);
31111389SAlexander.Kolbasov@Sun.COM static int cu_cpc_req_add(char *event, kcpc_request_list_t *reqs,
31211389SAlexander.Kolbasov@Sun.COM int nreqs, cu_cntr_stats_t *stats, int kmem_flags, int *nevents);
31311389SAlexander.Kolbasov@Sun.COM static int cu_cpu_callback(cpu_setup_t what, int id, void *arg);
31411389SAlexander.Kolbasov@Sun.COM static void cu_cpu_disable(cpu_t *cp);
31511389SAlexander.Kolbasov@Sun.COM static void cu_cpu_enable(cpu_t *cp);
31611389SAlexander.Kolbasov@Sun.COM static int cu_cpu_init(cpu_t *cp, kcpc_request_list_t *reqs);
31711389SAlexander.Kolbasov@Sun.COM static int cu_cpu_fini(cpu_t *cp);
31811389SAlexander.Kolbasov@Sun.COM static void cu_cpu_kstat_create(pghw_t *pg, cu_cntr_info_t *cntr_info);
31911389SAlexander.Kolbasov@Sun.COM static int cu_cpu_kstat_update(kstat_t *ksp, int rw);
32011389SAlexander.Kolbasov@Sun.COM static int cu_cpu_run(cpu_t *cp, cu_cpu_func_t func, uintptr_t arg);
32111389SAlexander.Kolbasov@Sun.COM static int cu_cpu_update_stats(cu_cntr_stats_t *stats,
32211389SAlexander.Kolbasov@Sun.COM uint64_t cntr_value);
32311389SAlexander.Kolbasov@Sun.COM static void cu_cpu_info_detach_xcall(void);
32411389SAlexander.Kolbasov@Sun.COM
32511389SAlexander.Kolbasov@Sun.COM /*
32611389SAlexander.Kolbasov@Sun.COM * Disable or enable Capacity Utilization counters on all CPUs.
32711389SAlexander.Kolbasov@Sun.COM */
32811389SAlexander.Kolbasov@Sun.COM void
cu_disable(void)32911389SAlexander.Kolbasov@Sun.COM cu_disable(void)
33011389SAlexander.Kolbasov@Sun.COM {
33111389SAlexander.Kolbasov@Sun.COM cpu_t *cp;
33211389SAlexander.Kolbasov@Sun.COM
33311389SAlexander.Kolbasov@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
33411389SAlexander.Kolbasov@Sun.COM
33511389SAlexander.Kolbasov@Sun.COM cp = cpu_active;
33611389SAlexander.Kolbasov@Sun.COM do {
33711389SAlexander.Kolbasov@Sun.COM if (!(cp->cpu_flags & CPU_OFFLINE))
33811389SAlexander.Kolbasov@Sun.COM cu_cpu_disable(cp);
33911389SAlexander.Kolbasov@Sun.COM } while ((cp = cp->cpu_next_onln) != cpu_active);
34011389SAlexander.Kolbasov@Sun.COM }
34111389SAlexander.Kolbasov@Sun.COM
34211389SAlexander.Kolbasov@Sun.COM
34311389SAlexander.Kolbasov@Sun.COM void
cu_enable(void)34411389SAlexander.Kolbasov@Sun.COM cu_enable(void)
34511389SAlexander.Kolbasov@Sun.COM {
34611389SAlexander.Kolbasov@Sun.COM cpu_t *cp;
34711389SAlexander.Kolbasov@Sun.COM
34811389SAlexander.Kolbasov@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
34911389SAlexander.Kolbasov@Sun.COM
35011389SAlexander.Kolbasov@Sun.COM cp = cpu_active;
35111389SAlexander.Kolbasov@Sun.COM do {
35211389SAlexander.Kolbasov@Sun.COM if (!(cp->cpu_flags & CPU_OFFLINE))
35311389SAlexander.Kolbasov@Sun.COM cu_cpu_enable(cp);
35411389SAlexander.Kolbasov@Sun.COM } while ((cp = cp->cpu_next_onln) != cpu_active);
35511389SAlexander.Kolbasov@Sun.COM }
35611389SAlexander.Kolbasov@Sun.COM
35711389SAlexander.Kolbasov@Sun.COM
35811389SAlexander.Kolbasov@Sun.COM /*
35911389SAlexander.Kolbasov@Sun.COM * Setup capacity and utilization support
36011389SAlexander.Kolbasov@Sun.COM */
36111389SAlexander.Kolbasov@Sun.COM void
cu_init(void)36211389SAlexander.Kolbasov@Sun.COM cu_init(void)
36311389SAlexander.Kolbasov@Sun.COM {
36411389SAlexander.Kolbasov@Sun.COM cpu_t *cp;
36511389SAlexander.Kolbasov@Sun.COM
36611389SAlexander.Kolbasov@Sun.COM cu_init_error = 0;
36711389SAlexander.Kolbasov@Sun.COM if (!(cu_flags & CU_FLAG_ENABLE) || (cu_flags & CU_FLAG_ON)) {
36811389SAlexander.Kolbasov@Sun.COM cu_init_error = -1;
36911389SAlexander.Kolbasov@Sun.COM return;
37011389SAlexander.Kolbasov@Sun.COM }
37111389SAlexander.Kolbasov@Sun.COM
37211389SAlexander.Kolbasov@Sun.COM if (kcpc_init() != 0) {
37311389SAlexander.Kolbasov@Sun.COM cu_init_error = -2;
37411389SAlexander.Kolbasov@Sun.COM return;
37511389SAlexander.Kolbasov@Sun.COM }
37611389SAlexander.Kolbasov@Sun.COM
37711389SAlexander.Kolbasov@Sun.COM /*
37811389SAlexander.Kolbasov@Sun.COM * Can't measure hardware capacity and utilization without CPU
37911389SAlexander.Kolbasov@Sun.COM * hardware performance counters
38011389SAlexander.Kolbasov@Sun.COM */
38111389SAlexander.Kolbasov@Sun.COM if (cpc_ncounters <= 0) {
38211389SAlexander.Kolbasov@Sun.COM cu_init_error = -3;
38311389SAlexander.Kolbasov@Sun.COM return;
38411389SAlexander.Kolbasov@Sun.COM }
38511389SAlexander.Kolbasov@Sun.COM
38611389SAlexander.Kolbasov@Sun.COM /*
38711389SAlexander.Kolbasov@Sun.COM * Setup CPC event request queue
38811389SAlexander.Kolbasov@Sun.COM */
38911389SAlexander.Kolbasov@Sun.COM cu_cpc_reqs = kcpc_reqs_init(cpc_ncounters, KM_SLEEP);
39011389SAlexander.Kolbasov@Sun.COM
39111389SAlexander.Kolbasov@Sun.COM mutex_enter(&cpu_lock);
39211389SAlexander.Kolbasov@Sun.COM
39311389SAlexander.Kolbasov@Sun.COM /*
39411389SAlexander.Kolbasov@Sun.COM * Mark flags to say that module is ready to be setup
39511389SAlexander.Kolbasov@Sun.COM */
39611389SAlexander.Kolbasov@Sun.COM cu_flags |= CU_FLAG_READY;
39711389SAlexander.Kolbasov@Sun.COM
39811389SAlexander.Kolbasov@Sun.COM cp = cpu_active;
39911389SAlexander.Kolbasov@Sun.COM do {
40011389SAlexander.Kolbasov@Sun.COM /*
40111389SAlexander.Kolbasov@Sun.COM * Allocate and setup state needed to measure capacity and
40211389SAlexander.Kolbasov@Sun.COM * utilization
40311389SAlexander.Kolbasov@Sun.COM */
40411389SAlexander.Kolbasov@Sun.COM if (cu_cpu_init(cp, cu_cpc_reqs) != 0)
40511389SAlexander.Kolbasov@Sun.COM cu_init_error = -5;
40611389SAlexander.Kolbasov@Sun.COM
40711389SAlexander.Kolbasov@Sun.COM /*
40811389SAlexander.Kolbasov@Sun.COM * Reset list of counter event requests so its space can be
40911389SAlexander.Kolbasov@Sun.COM * reused for a different set of requests for next CPU
41011389SAlexander.Kolbasov@Sun.COM */
41111389SAlexander.Kolbasov@Sun.COM (void) kcpc_reqs_reset(cu_cpc_reqs);
41211389SAlexander.Kolbasov@Sun.COM
41311389SAlexander.Kolbasov@Sun.COM cp = cp->cpu_next_onln;
41411389SAlexander.Kolbasov@Sun.COM } while (cp != cpu_active);
41511389SAlexander.Kolbasov@Sun.COM
41611389SAlexander.Kolbasov@Sun.COM /*
41711389SAlexander.Kolbasov@Sun.COM * Mark flags to say that module is on now and counters are ready to be
41811389SAlexander.Kolbasov@Sun.COM * programmed on all active CPUs
41911389SAlexander.Kolbasov@Sun.COM */
42011389SAlexander.Kolbasov@Sun.COM cu_flags |= CU_FLAG_ON;
42111389SAlexander.Kolbasov@Sun.COM
42211389SAlexander.Kolbasov@Sun.COM /*
42311389SAlexander.Kolbasov@Sun.COM * Program counters on currently active CPUs
42411389SAlexander.Kolbasov@Sun.COM */
42511389SAlexander.Kolbasov@Sun.COM cp = cpu_active;
42611389SAlexander.Kolbasov@Sun.COM do {
42711389SAlexander.Kolbasov@Sun.COM if (cu_cpu_run(cp, cu_cpc_program_xcall,
42811389SAlexander.Kolbasov@Sun.COM (uintptr_t)B_FALSE) != 0)
42911389SAlexander.Kolbasov@Sun.COM cu_init_error = -6;
43011389SAlexander.Kolbasov@Sun.COM
43111389SAlexander.Kolbasov@Sun.COM cp = cp->cpu_next_onln;
43211389SAlexander.Kolbasov@Sun.COM } while (cp != cpu_active);
43311389SAlexander.Kolbasov@Sun.COM
43411389SAlexander.Kolbasov@Sun.COM /*
43511389SAlexander.Kolbasov@Sun.COM * Register callback for CPU state changes to enable and disable
43611389SAlexander.Kolbasov@Sun.COM * CPC counters as CPUs come on and offline
43711389SAlexander.Kolbasov@Sun.COM */
43811389SAlexander.Kolbasov@Sun.COM register_cpu_setup_func(cu_cpu_callback, NULL);
43911389SAlexander.Kolbasov@Sun.COM
44011389SAlexander.Kolbasov@Sun.COM mutex_exit(&cpu_lock);
44111389SAlexander.Kolbasov@Sun.COM }
44211389SAlexander.Kolbasov@Sun.COM
44311389SAlexander.Kolbasov@Sun.COM
44411389SAlexander.Kolbasov@Sun.COM /*
44511389SAlexander.Kolbasov@Sun.COM * Return number of counter events needed to measure capacity and utilization
44611389SAlexander.Kolbasov@Sun.COM * for specified CPU and fill in list of CPC requests with each counter event
44711389SAlexander.Kolbasov@Sun.COM * needed if list where to add CPC requests is given
44811389SAlexander.Kolbasov@Sun.COM *
44911389SAlexander.Kolbasov@Sun.COM * NOTE: Use KM_NOSLEEP for kmem_{,z}alloc() since cpu_lock is held and free
45011389SAlexander.Kolbasov@Sun.COM * everything that has been successfully allocated if any memory
45111389SAlexander.Kolbasov@Sun.COM * allocation fails
45211389SAlexander.Kolbasov@Sun.COM */
45311389SAlexander.Kolbasov@Sun.COM static int
cu_cpc_init(cpu_t * cp,kcpc_request_list_t * reqs,int nreqs)45411389SAlexander.Kolbasov@Sun.COM cu_cpc_init(cpu_t *cp, kcpc_request_list_t *reqs, int nreqs)
45511389SAlexander.Kolbasov@Sun.COM {
45611389SAlexander.Kolbasov@Sun.COM group_t *cmt_pgs;
45711389SAlexander.Kolbasov@Sun.COM cu_cntr_info_t **cntr_info_array;
45811389SAlexander.Kolbasov@Sun.COM cpu_pg_t *cpu_pgs;
45911389SAlexander.Kolbasov@Sun.COM cu_cpu_info_t *cu_cpu_info;
46011389SAlexander.Kolbasov@Sun.COM pg_cmt_t *pg_cmt;
46111389SAlexander.Kolbasov@Sun.COM pghw_t *pg_hw;
46211389SAlexander.Kolbasov@Sun.COM cu_cntr_stats_t *stats;
46311389SAlexander.Kolbasov@Sun.COM int nevents;
46411389SAlexander.Kolbasov@Sun.COM pghw_type_t pg_hw_type;
46511389SAlexander.Kolbasov@Sun.COM group_iter_t iter;
46611389SAlexander.Kolbasov@Sun.COM
46711389SAlexander.Kolbasov@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
46811389SAlexander.Kolbasov@Sun.COM
46911389SAlexander.Kolbasov@Sun.COM /*
47011389SAlexander.Kolbasov@Sun.COM * There has to be a target CPU for this
47111389SAlexander.Kolbasov@Sun.COM */
47211389SAlexander.Kolbasov@Sun.COM if (cp == NULL)
47311389SAlexander.Kolbasov@Sun.COM return (-1);
47411389SAlexander.Kolbasov@Sun.COM
47511389SAlexander.Kolbasov@Sun.COM /*
47611389SAlexander.Kolbasov@Sun.COM * Return 0 when CPU doesn't belong to any group
47711389SAlexander.Kolbasov@Sun.COM */
47811389SAlexander.Kolbasov@Sun.COM cpu_pgs = cp->cpu_pg;
47911389SAlexander.Kolbasov@Sun.COM if (cpu_pgs == NULL || GROUP_SIZE(&cpu_pgs->cmt_pgs) < 1)
48011389SAlexander.Kolbasov@Sun.COM return (0);
48111389SAlexander.Kolbasov@Sun.COM
48211389SAlexander.Kolbasov@Sun.COM cmt_pgs = &cpu_pgs->cmt_pgs;
48311389SAlexander.Kolbasov@Sun.COM cu_cpu_info = cp->cpu_cu_info;
48411389SAlexander.Kolbasov@Sun.COM
48511389SAlexander.Kolbasov@Sun.COM /*
48611389SAlexander.Kolbasov@Sun.COM * Grab counter statistics and info
48711389SAlexander.Kolbasov@Sun.COM */
48811389SAlexander.Kolbasov@Sun.COM if (reqs == NULL) {
48911389SAlexander.Kolbasov@Sun.COM stats = NULL;
49011389SAlexander.Kolbasov@Sun.COM cntr_info_array = NULL;
49111389SAlexander.Kolbasov@Sun.COM } else {
49211389SAlexander.Kolbasov@Sun.COM if (cu_cpu_info == NULL || cu_cpu_info->cu_cntr_stats == NULL)
49311389SAlexander.Kolbasov@Sun.COM return (-2);
49411389SAlexander.Kolbasov@Sun.COM
49511389SAlexander.Kolbasov@Sun.COM stats = cu_cpu_info->cu_cntr_stats;
49611389SAlexander.Kolbasov@Sun.COM cntr_info_array = cu_cpu_info->cu_cntr_info;
49711389SAlexander.Kolbasov@Sun.COM }
49811389SAlexander.Kolbasov@Sun.COM
49911389SAlexander.Kolbasov@Sun.COM /*
50011389SAlexander.Kolbasov@Sun.COM * See whether platform (or processor) specific code knows which CPC
50111389SAlexander.Kolbasov@Sun.COM * events to request, etc. are needed to measure hardware capacity and
50211389SAlexander.Kolbasov@Sun.COM * utilization on this machine
50311389SAlexander.Kolbasov@Sun.COM */
50411389SAlexander.Kolbasov@Sun.COM nevents = cu_plat_cpc_init(cp, reqs, nreqs);
50511389SAlexander.Kolbasov@Sun.COM if (nevents >= 0)
50611389SAlexander.Kolbasov@Sun.COM return (nevents);
50711389SAlexander.Kolbasov@Sun.COM
50811389SAlexander.Kolbasov@Sun.COM /*
50911389SAlexander.Kolbasov@Sun.COM * Let common code decide which CPC events to request, etc. to measure
51011389SAlexander.Kolbasov@Sun.COM * capacity and utilization since platform (or processor) specific does
51111389SAlexander.Kolbasov@Sun.COM * not know....
51211389SAlexander.Kolbasov@Sun.COM *
51311389SAlexander.Kolbasov@Sun.COM * Walk CPU's PG lineage and do following:
51411389SAlexander.Kolbasov@Sun.COM *
51511389SAlexander.Kolbasov@Sun.COM * - Setup CPC request, counter info, and stats needed for each counter
51611389SAlexander.Kolbasov@Sun.COM * event to measure capacity and and utilization for each of CPU's PG
51711389SAlexander.Kolbasov@Sun.COM * hardware sharing relationships
51811389SAlexander.Kolbasov@Sun.COM *
51911389SAlexander.Kolbasov@Sun.COM * - Create PG CPU kstats to export capacity and utilization for each PG
52011389SAlexander.Kolbasov@Sun.COM */
52111389SAlexander.Kolbasov@Sun.COM nevents = 0;
52211389SAlexander.Kolbasov@Sun.COM group_iter_init(&iter);
52311389SAlexander.Kolbasov@Sun.COM while ((pg_cmt = group_iterate(cmt_pgs, &iter)) != NULL) {
52411389SAlexander.Kolbasov@Sun.COM cu_cntr_info_t *cntr_info;
52511389SAlexander.Kolbasov@Sun.COM int nevents_save;
52611389SAlexander.Kolbasov@Sun.COM int nstats;
52711389SAlexander.Kolbasov@Sun.COM
52811389SAlexander.Kolbasov@Sun.COM pg_hw = (pghw_t *)pg_cmt;
52911389SAlexander.Kolbasov@Sun.COM pg_hw_type = pg_hw->pghw_hw;
53011389SAlexander.Kolbasov@Sun.COM nevents_save = nevents;
53111389SAlexander.Kolbasov@Sun.COM nstats = 0;
53211389SAlexander.Kolbasov@Sun.COM
53311389SAlexander.Kolbasov@Sun.COM switch (pg_hw_type) {
53411389SAlexander.Kolbasov@Sun.COM case PGHW_IPIPE:
53511389SAlexander.Kolbasov@Sun.COM if (cu_cpc_req_add("PAPI_tot_ins", reqs, nreqs, stats,
53611389SAlexander.Kolbasov@Sun.COM KM_NOSLEEP, &nevents) != 0)
53711389SAlexander.Kolbasov@Sun.COM continue;
53811389SAlexander.Kolbasov@Sun.COM nstats = 1;
53911389SAlexander.Kolbasov@Sun.COM break;
54011389SAlexander.Kolbasov@Sun.COM
54111389SAlexander.Kolbasov@Sun.COM case PGHW_FPU:
54211389SAlexander.Kolbasov@Sun.COM if (cu_cpc_req_add("PAPI_fp_ins", reqs, nreqs, stats,
54311389SAlexander.Kolbasov@Sun.COM KM_NOSLEEP, &nevents) != 0)
54411389SAlexander.Kolbasov@Sun.COM continue;
54511389SAlexander.Kolbasov@Sun.COM nstats = 1;
54611389SAlexander.Kolbasov@Sun.COM break;
54711389SAlexander.Kolbasov@Sun.COM
54811389SAlexander.Kolbasov@Sun.COM default:
54911389SAlexander.Kolbasov@Sun.COM /*
55011389SAlexander.Kolbasov@Sun.COM * Don't measure capacity and utilization for this kind
55111389SAlexander.Kolbasov@Sun.COM * of PG hardware relationship so skip to next PG in
55211389SAlexander.Kolbasov@Sun.COM * CPU's PG lineage
55311389SAlexander.Kolbasov@Sun.COM */
55411389SAlexander.Kolbasov@Sun.COM continue;
55511389SAlexander.Kolbasov@Sun.COM }
55611389SAlexander.Kolbasov@Sun.COM
55711389SAlexander.Kolbasov@Sun.COM cntr_info = cntr_info_array[pg_hw_type];
55811389SAlexander.Kolbasov@Sun.COM
55911389SAlexander.Kolbasov@Sun.COM /*
56011389SAlexander.Kolbasov@Sun.COM * Nothing to measure for this hardware sharing relationship
56111389SAlexander.Kolbasov@Sun.COM */
56211389SAlexander.Kolbasov@Sun.COM if (nevents - nevents_save == 0) {
56311389SAlexander.Kolbasov@Sun.COM if (cntr_info != NULL)
56411389SAlexander.Kolbasov@Sun.COM kmem_free(cntr_info, sizeof (cu_cntr_info_t));
56511389SAlexander.Kolbasov@Sun.COM cntr_info_array[pg_hw_type] = NULL;
56611389SAlexander.Kolbasov@Sun.COM continue;
56711389SAlexander.Kolbasov@Sun.COM }
56811389SAlexander.Kolbasov@Sun.COM
56911389SAlexander.Kolbasov@Sun.COM /*
57011389SAlexander.Kolbasov@Sun.COM * Fill in counter info for this PG hardware relationship
57111389SAlexander.Kolbasov@Sun.COM */
57211389SAlexander.Kolbasov@Sun.COM if (cntr_info == NULL) {
57311389SAlexander.Kolbasov@Sun.COM cntr_info = kmem_zalloc(sizeof (cu_cntr_info_t),
57411389SAlexander.Kolbasov@Sun.COM KM_NOSLEEP);
57511389SAlexander.Kolbasov@Sun.COM if (cntr_info == NULL)
57611389SAlexander.Kolbasov@Sun.COM continue;
57711389SAlexander.Kolbasov@Sun.COM cntr_info_array[pg_hw_type] = cntr_info;
57811389SAlexander.Kolbasov@Sun.COM }
57911389SAlexander.Kolbasov@Sun.COM cntr_info->ci_cpu = cp;
58011389SAlexander.Kolbasov@Sun.COM cntr_info->ci_pg = pg_hw;
58111389SAlexander.Kolbasov@Sun.COM cntr_info->ci_stats = &stats[nevents_save];
58211389SAlexander.Kolbasov@Sun.COM cntr_info->ci_nstats = nstats;
58311389SAlexander.Kolbasov@Sun.COM
58411389SAlexander.Kolbasov@Sun.COM /*
58511389SAlexander.Kolbasov@Sun.COM * Create PG CPU kstats for this hardware relationship
58611389SAlexander.Kolbasov@Sun.COM */
58711389SAlexander.Kolbasov@Sun.COM cu_cpu_kstat_create(pg_hw, cntr_info);
58811389SAlexander.Kolbasov@Sun.COM }
58911389SAlexander.Kolbasov@Sun.COM
59011389SAlexander.Kolbasov@Sun.COM return (nevents);
59111389SAlexander.Kolbasov@Sun.COM }
59211389SAlexander.Kolbasov@Sun.COM
59311389SAlexander.Kolbasov@Sun.COM
59411389SAlexander.Kolbasov@Sun.COM /*
59511389SAlexander.Kolbasov@Sun.COM * Program counters for capacity and utilization on given CPU
59611389SAlexander.Kolbasov@Sun.COM *
59711389SAlexander.Kolbasov@Sun.COM * If any of the following conditions is true, the counters are not programmed:
59811389SAlexander.Kolbasov@Sun.COM *
59911389SAlexander.Kolbasov@Sun.COM * - CU framework is disabled
60011389SAlexander.Kolbasov@Sun.COM * - The cpu_cu_info field of the cpu structure is NULL
60111389SAlexander.Kolbasov@Sun.COM * - DTrace is active
60211389SAlexander.Kolbasov@Sun.COM * - Counters are programmed already
60311389SAlexander.Kolbasov@Sun.COM * - Counters are disabled (by calls to cu_cpu_disable())
60411389SAlexander.Kolbasov@Sun.COM */
60511389SAlexander.Kolbasov@Sun.COM void
cu_cpc_program(cpu_t * cp,int * err)60611389SAlexander.Kolbasov@Sun.COM cu_cpc_program(cpu_t *cp, int *err)
60711389SAlexander.Kolbasov@Sun.COM {
60811389SAlexander.Kolbasov@Sun.COM cu_cpc_ctx_t *cpu_ctx;
60911389SAlexander.Kolbasov@Sun.COM kcpc_ctx_t *ctx;
61011389SAlexander.Kolbasov@Sun.COM cu_cpu_info_t *cu_cpu_info;
61111389SAlexander.Kolbasov@Sun.COM
61211389SAlexander.Kolbasov@Sun.COM ASSERT(IS_HIPIL());
61311389SAlexander.Kolbasov@Sun.COM /*
61411389SAlexander.Kolbasov@Sun.COM * Should be running on given CPU. We disable preemption to keep CPU
61511389SAlexander.Kolbasov@Sun.COM * from disappearing and make sure flags and CPC context don't change
61611389SAlexander.Kolbasov@Sun.COM * from underneath us
61711389SAlexander.Kolbasov@Sun.COM */
61811389SAlexander.Kolbasov@Sun.COM kpreempt_disable();
61911389SAlexander.Kolbasov@Sun.COM ASSERT(cp == CPU);
62011389SAlexander.Kolbasov@Sun.COM
62111389SAlexander.Kolbasov@Sun.COM /*
62211389SAlexander.Kolbasov@Sun.COM * Module not ready to program counters
62311389SAlexander.Kolbasov@Sun.COM */
62411389SAlexander.Kolbasov@Sun.COM if (!(cu_flags & CU_FLAG_ON)) {
62511389SAlexander.Kolbasov@Sun.COM *err = -1;
62611389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
62711389SAlexander.Kolbasov@Sun.COM return;
62811389SAlexander.Kolbasov@Sun.COM }
62911389SAlexander.Kolbasov@Sun.COM
63011389SAlexander.Kolbasov@Sun.COM if (cp == NULL) {
63111389SAlexander.Kolbasov@Sun.COM *err = -2;
63211389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
63311389SAlexander.Kolbasov@Sun.COM return;
63411389SAlexander.Kolbasov@Sun.COM }
63511389SAlexander.Kolbasov@Sun.COM
63611389SAlexander.Kolbasov@Sun.COM cu_cpu_info = cp->cpu_cu_info;
63711389SAlexander.Kolbasov@Sun.COM if (cu_cpu_info == NULL) {
63811389SAlexander.Kolbasov@Sun.COM *err = -3;
63911389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
64011389SAlexander.Kolbasov@Sun.COM return;
64111389SAlexander.Kolbasov@Sun.COM }
64211389SAlexander.Kolbasov@Sun.COM
64311389SAlexander.Kolbasov@Sun.COM /*
64411389SAlexander.Kolbasov@Sun.COM * If DTrace CPC is active or counters turned on already or are
64511389SAlexander.Kolbasov@Sun.COM * disabled, just return.
64611389SAlexander.Kolbasov@Sun.COM */
64711389SAlexander.Kolbasov@Sun.COM if (dtrace_cpc_in_use || (cu_cpu_info->cu_flag & CU_CPU_CNTRS_ON) ||
64811389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_disabled) {
64911389SAlexander.Kolbasov@Sun.COM *err = 1;
65011389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
65111389SAlexander.Kolbasov@Sun.COM return;
65211389SAlexander.Kolbasov@Sun.COM }
65311389SAlexander.Kolbasov@Sun.COM
65411389SAlexander.Kolbasov@Sun.COM if ((CPU->cpu_cpc_ctx != NULL) &&
65511389SAlexander.Kolbasov@Sun.COM !(CPU->cpu_cpc_ctx->kc_flags & KCPC_CTX_INVALID_STOPPED)) {
65611389SAlexander.Kolbasov@Sun.COM *err = -4;
65711389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
65811389SAlexander.Kolbasov@Sun.COM return;
65911389SAlexander.Kolbasov@Sun.COM }
66011389SAlexander.Kolbasov@Sun.COM
66111389SAlexander.Kolbasov@Sun.COM /*
66211389SAlexander.Kolbasov@Sun.COM * Get CPU's CPC context needed for capacity and utilization
66311389SAlexander.Kolbasov@Sun.COM */
66411389SAlexander.Kolbasov@Sun.COM cpu_ctx = &cu_cpu_info->cu_cpc_ctx;
66511389SAlexander.Kolbasov@Sun.COM ASSERT(cpu_ctx != NULL);
66611389SAlexander.Kolbasov@Sun.COM ASSERT(cpu_ctx->nctx >= 0);
66711389SAlexander.Kolbasov@Sun.COM
66811389SAlexander.Kolbasov@Sun.COM ASSERT(cpu_ctx->ctx_ptr_array == NULL || cpu_ctx->ctx_ptr_array_sz > 0);
66911389SAlexander.Kolbasov@Sun.COM ASSERT(cpu_ctx->nctx <= cpu_ctx->ctx_ptr_array_sz);
67011389SAlexander.Kolbasov@Sun.COM if (cpu_ctx->nctx <= 0 || cpu_ctx->ctx_ptr_array == NULL ||
67111389SAlexander.Kolbasov@Sun.COM cpu_ctx->ctx_ptr_array_sz <= 0) {
67211389SAlexander.Kolbasov@Sun.COM *err = -5;
67311389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
67411389SAlexander.Kolbasov@Sun.COM return;
67511389SAlexander.Kolbasov@Sun.COM }
67611389SAlexander.Kolbasov@Sun.COM
67711389SAlexander.Kolbasov@Sun.COM /*
67811389SAlexander.Kolbasov@Sun.COM * Increment index in CPU's CPC context info to point at next context
67911389SAlexander.Kolbasov@Sun.COM * to program
68011389SAlexander.Kolbasov@Sun.COM *
68111389SAlexander.Kolbasov@Sun.COM * NOTE: Do this now instead of after programming counters to ensure
68211389SAlexander.Kolbasov@Sun.COM * that index will always point at *current* context so we will
68311389SAlexander.Kolbasov@Sun.COM * always be able to unprogram *current* context if necessary
68411389SAlexander.Kolbasov@Sun.COM */
68511389SAlexander.Kolbasov@Sun.COM cpu_ctx->cur_index = (cpu_ctx->cur_index + 1) % cpu_ctx->nctx;
68611389SAlexander.Kolbasov@Sun.COM
68711389SAlexander.Kolbasov@Sun.COM ctx = cpu_ctx->ctx_ptr_array[cpu_ctx->cur_index];
68811389SAlexander.Kolbasov@Sun.COM
68911389SAlexander.Kolbasov@Sun.COM /*
69011389SAlexander.Kolbasov@Sun.COM * Clear KCPC_CTX_INVALID and KCPC_CTX_INVALID_STOPPED from CPU's CPC
69111389SAlexander.Kolbasov@Sun.COM * context before programming counters
69211389SAlexander.Kolbasov@Sun.COM *
69311389SAlexander.Kolbasov@Sun.COM * Context is marked with KCPC_CTX_INVALID_STOPPED when context is
69411389SAlexander.Kolbasov@Sun.COM * unprogrammed and may be marked with KCPC_CTX_INVALID when
69511389SAlexander.Kolbasov@Sun.COM * kcpc_invalidate_all() is called by cpustat(1M) and dtrace CPC to
69611389SAlexander.Kolbasov@Sun.COM * invalidate all CPC contexts before they take over all the counters.
69711389SAlexander.Kolbasov@Sun.COM *
69811389SAlexander.Kolbasov@Sun.COM * This isn't necessary since these flags are only used for thread bound
69911389SAlexander.Kolbasov@Sun.COM * CPC contexts not CPU bound CPC contexts like ones used for capacity
70011389SAlexander.Kolbasov@Sun.COM * and utilization.
70111389SAlexander.Kolbasov@Sun.COM *
70211389SAlexander.Kolbasov@Sun.COM * There is no need to protect the flag update since no one is using
70311389SAlexander.Kolbasov@Sun.COM * this context now.
70411389SAlexander.Kolbasov@Sun.COM */
70511389SAlexander.Kolbasov@Sun.COM ctx->kc_flags &= ~(KCPC_CTX_INVALID | KCPC_CTX_INVALID_STOPPED);
70611389SAlexander.Kolbasov@Sun.COM
70711389SAlexander.Kolbasov@Sun.COM /*
70811389SAlexander.Kolbasov@Sun.COM * Program counters on this CPU
70911389SAlexander.Kolbasov@Sun.COM */
71011389SAlexander.Kolbasov@Sun.COM kcpc_program(ctx, B_FALSE, B_FALSE);
71111389SAlexander.Kolbasov@Sun.COM
71211389SAlexander.Kolbasov@Sun.COM cp->cpu_cpc_ctx = ctx;
71311389SAlexander.Kolbasov@Sun.COM
71411389SAlexander.Kolbasov@Sun.COM /*
71511389SAlexander.Kolbasov@Sun.COM * Set state in CPU structure to say that CPU's counters are programmed
71611389SAlexander.Kolbasov@Sun.COM * for capacity and utilization now and that they are transitioning from
71711389SAlexander.Kolbasov@Sun.COM * off to on state. This will cause cu_cpu_update to update stop times
71811389SAlexander.Kolbasov@Sun.COM * for all programmed counters.
71911389SAlexander.Kolbasov@Sun.COM */
72011389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_flag |= CU_CPU_CNTRS_ON | CU_CPU_CNTRS_OFF_ON;
72111389SAlexander.Kolbasov@Sun.COM
72211389SAlexander.Kolbasov@Sun.COM /*
72311389SAlexander.Kolbasov@Sun.COM * Update counter statistics
72411389SAlexander.Kolbasov@Sun.COM */
72511389SAlexander.Kolbasov@Sun.COM (void) cu_cpu_update(cp, B_FALSE);
72611389SAlexander.Kolbasov@Sun.COM
72711389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_flag &= ~CU_CPU_CNTRS_OFF_ON;
72811389SAlexander.Kolbasov@Sun.COM
72911389SAlexander.Kolbasov@Sun.COM *err = 0;
73011389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
73111389SAlexander.Kolbasov@Sun.COM }
73211389SAlexander.Kolbasov@Sun.COM
73311389SAlexander.Kolbasov@Sun.COM
73411389SAlexander.Kolbasov@Sun.COM /*
73511389SAlexander.Kolbasov@Sun.COM * Cross call wrapper routine for cu_cpc_program()
73611389SAlexander.Kolbasov@Sun.COM *
73711389SAlexander.Kolbasov@Sun.COM * Checks to make sure that counters on CPU aren't being used by someone else
73811389SAlexander.Kolbasov@Sun.COM * before calling cu_cpc_program() since cu_cpc_program() needs to assert that
73911389SAlexander.Kolbasov@Sun.COM * nobody else is using the counters to catch and prevent any broken code.
74011389SAlexander.Kolbasov@Sun.COM * Also, this check needs to happen on the target CPU since the CPU's CPC
74111389SAlexander.Kolbasov@Sun.COM * context can only be changed while running on the CPU.
74211389SAlexander.Kolbasov@Sun.COM *
74311389SAlexander.Kolbasov@Sun.COM * If the first argument is TRUE, cu_cpc_program_xcall also checks that there is
74411389SAlexander.Kolbasov@Sun.COM * no valid thread bound cpc context. This is important to check to prevent
74511389SAlexander.Kolbasov@Sun.COM * re-programming thread counters with CU counters when CPU is coming on-line.
74611389SAlexander.Kolbasov@Sun.COM */
74711389SAlexander.Kolbasov@Sun.COM static void
cu_cpc_program_xcall(uintptr_t arg,int * err)74811389SAlexander.Kolbasov@Sun.COM cu_cpc_program_xcall(uintptr_t arg, int *err)
74911389SAlexander.Kolbasov@Sun.COM {
75011389SAlexander.Kolbasov@Sun.COM boolean_t avoid_thread_context = (boolean_t)arg;
75111389SAlexander.Kolbasov@Sun.COM
75211389SAlexander.Kolbasov@Sun.COM kpreempt_disable();
75311389SAlexander.Kolbasov@Sun.COM
75411389SAlexander.Kolbasov@Sun.COM if (CPU->cpu_cpc_ctx != NULL &&
75511389SAlexander.Kolbasov@Sun.COM !(CPU->cpu_cpc_ctx->kc_flags & KCPC_CTX_INVALID_STOPPED)) {
75611389SAlexander.Kolbasov@Sun.COM *err = -100;
75711389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
75811389SAlexander.Kolbasov@Sun.COM return;
75911389SAlexander.Kolbasov@Sun.COM }
76011389SAlexander.Kolbasov@Sun.COM
76111389SAlexander.Kolbasov@Sun.COM if (avoid_thread_context && (curthread->t_cpc_ctx != NULL) &&
76211389SAlexander.Kolbasov@Sun.COM !(curthread->t_cpc_ctx->kc_flags & KCPC_CTX_INVALID_STOPPED)) {
76311389SAlexander.Kolbasov@Sun.COM *err = -200;
76411389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
76511389SAlexander.Kolbasov@Sun.COM return;
76611389SAlexander.Kolbasov@Sun.COM }
76711389SAlexander.Kolbasov@Sun.COM
76811389SAlexander.Kolbasov@Sun.COM cu_cpc_program(CPU, err);
76911389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
77011389SAlexander.Kolbasov@Sun.COM }
77111389SAlexander.Kolbasov@Sun.COM
77211389SAlexander.Kolbasov@Sun.COM
77311389SAlexander.Kolbasov@Sun.COM /*
77411389SAlexander.Kolbasov@Sun.COM * Unprogram counters for capacity and utilization on given CPU
77511389SAlexander.Kolbasov@Sun.COM * This function should be always executed on the target CPU at high PIL
77611389SAlexander.Kolbasov@Sun.COM */
77711389SAlexander.Kolbasov@Sun.COM void
cu_cpc_unprogram(cpu_t * cp,int * err)77811389SAlexander.Kolbasov@Sun.COM cu_cpc_unprogram(cpu_t *cp, int *err)
77911389SAlexander.Kolbasov@Sun.COM {
78011389SAlexander.Kolbasov@Sun.COM cu_cpc_ctx_t *cpu_ctx;
78111389SAlexander.Kolbasov@Sun.COM kcpc_ctx_t *ctx;
78211389SAlexander.Kolbasov@Sun.COM cu_cpu_info_t *cu_cpu_info;
78311389SAlexander.Kolbasov@Sun.COM
78411389SAlexander.Kolbasov@Sun.COM ASSERT(IS_HIPIL());
78511389SAlexander.Kolbasov@Sun.COM /*
78611389SAlexander.Kolbasov@Sun.COM * Should be running on given CPU with preemption disabled to keep CPU
78711389SAlexander.Kolbasov@Sun.COM * from disappearing and make sure flags and CPC context don't change
78811389SAlexander.Kolbasov@Sun.COM * from underneath us
78911389SAlexander.Kolbasov@Sun.COM */
79011389SAlexander.Kolbasov@Sun.COM kpreempt_disable();
79111389SAlexander.Kolbasov@Sun.COM ASSERT(cp == CPU);
79211389SAlexander.Kolbasov@Sun.COM
79311389SAlexander.Kolbasov@Sun.COM /*
79411389SAlexander.Kolbasov@Sun.COM * Module not on
79511389SAlexander.Kolbasov@Sun.COM */
79611389SAlexander.Kolbasov@Sun.COM if (!(cu_flags & CU_FLAG_ON)) {
79711389SAlexander.Kolbasov@Sun.COM *err = -1;
79811389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
79911389SAlexander.Kolbasov@Sun.COM return;
80011389SAlexander.Kolbasov@Sun.COM }
80111389SAlexander.Kolbasov@Sun.COM
80211389SAlexander.Kolbasov@Sun.COM cu_cpu_info = cp->cpu_cu_info;
80311389SAlexander.Kolbasov@Sun.COM if (cu_cpu_info == NULL) {
80411389SAlexander.Kolbasov@Sun.COM *err = -3;
80511389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
80611389SAlexander.Kolbasov@Sun.COM return;
80711389SAlexander.Kolbasov@Sun.COM }
80811389SAlexander.Kolbasov@Sun.COM
80911389SAlexander.Kolbasov@Sun.COM /*
81011389SAlexander.Kolbasov@Sun.COM * Counters turned off already
81111389SAlexander.Kolbasov@Sun.COM */
81211389SAlexander.Kolbasov@Sun.COM if (!(cu_cpu_info->cu_flag & CU_CPU_CNTRS_ON)) {
81311389SAlexander.Kolbasov@Sun.COM *err = 1;
81411389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
81511389SAlexander.Kolbasov@Sun.COM return;
81611389SAlexander.Kolbasov@Sun.COM }
81711389SAlexander.Kolbasov@Sun.COM
81811389SAlexander.Kolbasov@Sun.COM /*
81911389SAlexander.Kolbasov@Sun.COM * Update counter statistics
82011389SAlexander.Kolbasov@Sun.COM */
82111389SAlexander.Kolbasov@Sun.COM (void) cu_cpu_update(cp, B_FALSE);
82211389SAlexander.Kolbasov@Sun.COM
82311389SAlexander.Kolbasov@Sun.COM /*
82411389SAlexander.Kolbasov@Sun.COM * Get CPU's CPC context needed for capacity and utilization
82511389SAlexander.Kolbasov@Sun.COM */
82611389SAlexander.Kolbasov@Sun.COM cpu_ctx = &cu_cpu_info->cu_cpc_ctx;
82711389SAlexander.Kolbasov@Sun.COM if (cpu_ctx->nctx <= 0 || cpu_ctx->ctx_ptr_array == NULL ||
82811389SAlexander.Kolbasov@Sun.COM cpu_ctx->ctx_ptr_array_sz <= 0) {
82911389SAlexander.Kolbasov@Sun.COM *err = -5;
83011389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
83111389SAlexander.Kolbasov@Sun.COM return;
83211389SAlexander.Kolbasov@Sun.COM }
83311389SAlexander.Kolbasov@Sun.COM ctx = cpu_ctx->ctx_ptr_array[cpu_ctx->cur_index];
83411389SAlexander.Kolbasov@Sun.COM
83511389SAlexander.Kolbasov@Sun.COM /*
83611389SAlexander.Kolbasov@Sun.COM * CPU's CPC context should be current capacity and utilization CPC
83711389SAlexander.Kolbasov@Sun.COM * context
83811389SAlexander.Kolbasov@Sun.COM */
83911389SAlexander.Kolbasov@Sun.COM ASSERT(cp->cpu_cpc_ctx == ctx);
84011389SAlexander.Kolbasov@Sun.COM if (cp->cpu_cpc_ctx != ctx) {
84111389SAlexander.Kolbasov@Sun.COM *err = -6;
84211389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
84311389SAlexander.Kolbasov@Sun.COM return;
84411389SAlexander.Kolbasov@Sun.COM }
84511389SAlexander.Kolbasov@Sun.COM
84611389SAlexander.Kolbasov@Sun.COM /*
84711389SAlexander.Kolbasov@Sun.COM * Unprogram counters on CPU.
84811389SAlexander.Kolbasov@Sun.COM */
84911389SAlexander.Kolbasov@Sun.COM kcpc_unprogram(ctx, B_FALSE);
85011389SAlexander.Kolbasov@Sun.COM
85111389SAlexander.Kolbasov@Sun.COM ASSERT(ctx->kc_flags & KCPC_CTX_INVALID_STOPPED);
85211389SAlexander.Kolbasov@Sun.COM
85311389SAlexander.Kolbasov@Sun.COM /*
85411389SAlexander.Kolbasov@Sun.COM * Unset state in CPU structure saying that CPU's counters are
85511389SAlexander.Kolbasov@Sun.COM * programmed
85611389SAlexander.Kolbasov@Sun.COM */
85711389SAlexander.Kolbasov@Sun.COM cp->cpu_cpc_ctx = NULL;
85811389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_flag &= ~CU_CPU_CNTRS_ON;
85911389SAlexander.Kolbasov@Sun.COM
86011389SAlexander.Kolbasov@Sun.COM *err = 0;
86111389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
86211389SAlexander.Kolbasov@Sun.COM }
86311389SAlexander.Kolbasov@Sun.COM
86411389SAlexander.Kolbasov@Sun.COM
86511389SAlexander.Kolbasov@Sun.COM /*
86611389SAlexander.Kolbasov@Sun.COM * Add given counter event to list of CPC requests
86711389SAlexander.Kolbasov@Sun.COM */
86811389SAlexander.Kolbasov@Sun.COM static int
cu_cpc_req_add(char * event,kcpc_request_list_t * reqs,int nreqs,cu_cntr_stats_t * stats,int kmem_flags,int * nevents)86911389SAlexander.Kolbasov@Sun.COM cu_cpc_req_add(char *event, kcpc_request_list_t *reqs, int nreqs,
87011389SAlexander.Kolbasov@Sun.COM cu_cntr_stats_t *stats, int kmem_flags, int *nevents)
87111389SAlexander.Kolbasov@Sun.COM {
87211389SAlexander.Kolbasov@Sun.COM int n;
87311389SAlexander.Kolbasov@Sun.COM int retval;
87411389SAlexander.Kolbasov@Sun.COM uint_t flags;
87511389SAlexander.Kolbasov@Sun.COM
87611389SAlexander.Kolbasov@Sun.COM /*
87711389SAlexander.Kolbasov@Sun.COM * Return error when no counter event specified, counter event not
87811389SAlexander.Kolbasov@Sun.COM * supported by CPC's PCBE, or number of events not given
87911389SAlexander.Kolbasov@Sun.COM */
88011389SAlexander.Kolbasov@Sun.COM if (event == NULL || kcpc_event_supported(event) == B_FALSE ||
88111389SAlexander.Kolbasov@Sun.COM nevents == NULL)
88211389SAlexander.Kolbasov@Sun.COM return (-1);
88311389SAlexander.Kolbasov@Sun.COM
88411389SAlexander.Kolbasov@Sun.COM n = *nevents;
88511389SAlexander.Kolbasov@Sun.COM
88611389SAlexander.Kolbasov@Sun.COM /*
88711389SAlexander.Kolbasov@Sun.COM * Only count number of counter events needed if list
88811389SAlexander.Kolbasov@Sun.COM * where to add CPC requests not given
88911389SAlexander.Kolbasov@Sun.COM */
89011389SAlexander.Kolbasov@Sun.COM if (reqs == NULL) {
89111389SAlexander.Kolbasov@Sun.COM n++;
89211389SAlexander.Kolbasov@Sun.COM *nevents = n;
89311389SAlexander.Kolbasov@Sun.COM return (-3);
89411389SAlexander.Kolbasov@Sun.COM }
89511389SAlexander.Kolbasov@Sun.COM
89611389SAlexander.Kolbasov@Sun.COM /*
89711389SAlexander.Kolbasov@Sun.COM * Return error when stats not given or not enough room on list of CPC
89811389SAlexander.Kolbasov@Sun.COM * requests for more counter events
89911389SAlexander.Kolbasov@Sun.COM */
90011389SAlexander.Kolbasov@Sun.COM if (stats == NULL || (nreqs <= 0 && n >= nreqs))
90111389SAlexander.Kolbasov@Sun.COM return (-4);
90211389SAlexander.Kolbasov@Sun.COM
90311389SAlexander.Kolbasov@Sun.COM /*
90411389SAlexander.Kolbasov@Sun.COM * Use flags in cu_cpc_flags to program counters and enable overflow
90511389SAlexander.Kolbasov@Sun.COM * interrupts/traps (unless PCBE can't handle overflow interrupts) so
90611389SAlexander.Kolbasov@Sun.COM * PCBE can catch counters before they wrap to hopefully give us an
90711389SAlexander.Kolbasov@Sun.COM * accurate (64-bit) virtualized counter
90811389SAlexander.Kolbasov@Sun.COM */
90911389SAlexander.Kolbasov@Sun.COM flags = cu_cpc_flags;
91011389SAlexander.Kolbasov@Sun.COM if ((kcpc_pcbe_capabilities() & CPC_CAP_OVERFLOW_INTERRUPT) == 0)
91111389SAlexander.Kolbasov@Sun.COM flags &= ~CPC_OVF_NOTIFY_EMT;
91211389SAlexander.Kolbasov@Sun.COM
91311389SAlexander.Kolbasov@Sun.COM /*
91411389SAlexander.Kolbasov@Sun.COM * Add CPC request to list
91511389SAlexander.Kolbasov@Sun.COM */
91611389SAlexander.Kolbasov@Sun.COM retval = kcpc_reqs_add(reqs, event, cu_cpc_preset_value,
91711389SAlexander.Kolbasov@Sun.COM flags, 0, NULL, &stats[n], kmem_flags);
91811389SAlexander.Kolbasov@Sun.COM
91911389SAlexander.Kolbasov@Sun.COM if (retval != 0)
92011389SAlexander.Kolbasov@Sun.COM return (-5);
92111389SAlexander.Kolbasov@Sun.COM
92211389SAlexander.Kolbasov@Sun.COM n++;
92311389SAlexander.Kolbasov@Sun.COM *nevents = n;
92411389SAlexander.Kolbasov@Sun.COM return (0);
92511389SAlexander.Kolbasov@Sun.COM }
92611389SAlexander.Kolbasov@Sun.COM
92711389SAlexander.Kolbasov@Sun.COM static void
cu_cpu_info_detach_xcall(void)92811389SAlexander.Kolbasov@Sun.COM cu_cpu_info_detach_xcall(void)
92911389SAlexander.Kolbasov@Sun.COM {
93011389SAlexander.Kolbasov@Sun.COM ASSERT(IS_HIPIL());
93111389SAlexander.Kolbasov@Sun.COM
93211389SAlexander.Kolbasov@Sun.COM CPU->cpu_cu_info = NULL;
93311389SAlexander.Kolbasov@Sun.COM }
93411389SAlexander.Kolbasov@Sun.COM
93511389SAlexander.Kolbasov@Sun.COM
93611389SAlexander.Kolbasov@Sun.COM /*
93711389SAlexander.Kolbasov@Sun.COM * Enable or disable collection of capacity/utilization data for a current CPU.
93811389SAlexander.Kolbasov@Sun.COM * Counters are enabled if 'on' argument is True and disabled if it is False.
93911389SAlexander.Kolbasov@Sun.COM * This function should be always executed at high PIL
94011389SAlexander.Kolbasov@Sun.COM */
94111389SAlexander.Kolbasov@Sun.COM static void
cu_cpc_trigger(uintptr_t arg1,uintptr_t arg2)94211389SAlexander.Kolbasov@Sun.COM cu_cpc_trigger(uintptr_t arg1, uintptr_t arg2)
94311389SAlexander.Kolbasov@Sun.COM {
94411389SAlexander.Kolbasov@Sun.COM cpu_t *cp = (cpu_t *)arg1;
94511389SAlexander.Kolbasov@Sun.COM boolean_t on = (boolean_t)arg2;
94611389SAlexander.Kolbasov@Sun.COM int error;
94711389SAlexander.Kolbasov@Sun.COM cu_cpu_info_t *cu_cpu_info;
94811389SAlexander.Kolbasov@Sun.COM
94911389SAlexander.Kolbasov@Sun.COM ASSERT(IS_HIPIL());
95011389SAlexander.Kolbasov@Sun.COM kpreempt_disable();
95111389SAlexander.Kolbasov@Sun.COM ASSERT(cp == CPU);
95211389SAlexander.Kolbasov@Sun.COM
95311389SAlexander.Kolbasov@Sun.COM if (!(cu_flags & CU_FLAG_ON)) {
95411389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
95511389SAlexander.Kolbasov@Sun.COM return;
95611389SAlexander.Kolbasov@Sun.COM }
95711389SAlexander.Kolbasov@Sun.COM
95811389SAlexander.Kolbasov@Sun.COM cu_cpu_info = cp->cpu_cu_info;
95911389SAlexander.Kolbasov@Sun.COM if (cu_cpu_info == NULL) {
96011389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
96111389SAlexander.Kolbasov@Sun.COM return;
96211389SAlexander.Kolbasov@Sun.COM }
96311389SAlexander.Kolbasov@Sun.COM
96411389SAlexander.Kolbasov@Sun.COM ASSERT(!cu_cpu_info->cu_disabled ||
96511389SAlexander.Kolbasov@Sun.COM !(cu_cpu_info->cu_flag & CU_CPU_CNTRS_ON));
96611389SAlexander.Kolbasov@Sun.COM
96711389SAlexander.Kolbasov@Sun.COM if (on) {
96811389SAlexander.Kolbasov@Sun.COM /*
96911389SAlexander.Kolbasov@Sun.COM * Decrement the cu_disabled counter.
97011389SAlexander.Kolbasov@Sun.COM * Once it drops to zero, call cu_cpc_program.
97111389SAlexander.Kolbasov@Sun.COM */
97211389SAlexander.Kolbasov@Sun.COM if (cu_cpu_info->cu_disabled > 0)
97311389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_disabled--;
97411389SAlexander.Kolbasov@Sun.COM if (cu_cpu_info->cu_disabled == 0)
97511389SAlexander.Kolbasov@Sun.COM cu_cpc_program(CPU, &error);
97611389SAlexander.Kolbasov@Sun.COM } else if (cu_cpu_info->cu_disabled++ == 0) {
97711389SAlexander.Kolbasov@Sun.COM /*
97811389SAlexander.Kolbasov@Sun.COM * This is the first attempt to disable CU, so turn it off
97911389SAlexander.Kolbasov@Sun.COM */
98011389SAlexander.Kolbasov@Sun.COM cu_cpc_unprogram(cp, &error);
98111389SAlexander.Kolbasov@Sun.COM ASSERT(!(cu_cpu_info->cu_flag & CU_CPU_CNTRS_ON));
98211389SAlexander.Kolbasov@Sun.COM }
98311389SAlexander.Kolbasov@Sun.COM
98411389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
98511389SAlexander.Kolbasov@Sun.COM }
98611389SAlexander.Kolbasov@Sun.COM
98711389SAlexander.Kolbasov@Sun.COM
98811389SAlexander.Kolbasov@Sun.COM /*
98911389SAlexander.Kolbasov@Sun.COM * Callback for changes in CPU states
99011389SAlexander.Kolbasov@Sun.COM * Used to enable or disable hardware performance counters on CPUs that are
99111389SAlexander.Kolbasov@Sun.COM * turned on or off
99211389SAlexander.Kolbasov@Sun.COM *
99311389SAlexander.Kolbasov@Sun.COM * NOTE: cpc should be programmed/unprogrammed while running on the target CPU.
99411389SAlexander.Kolbasov@Sun.COM * We have to use thread_affinity_set to hop to the right CPU because these
99511389SAlexander.Kolbasov@Sun.COM * routines expect cpu_lock held, so we can't cross-call other CPUs while
99611389SAlexander.Kolbasov@Sun.COM * holding CPU lock.
99711389SAlexander.Kolbasov@Sun.COM */
99811389SAlexander.Kolbasov@Sun.COM static int
99911389SAlexander.Kolbasov@Sun.COM /* LINTED E_FUNC_ARG_UNUSED */
cu_cpu_callback(cpu_setup_t what,int id,void * arg)100011389SAlexander.Kolbasov@Sun.COM cu_cpu_callback(cpu_setup_t what, int id, void *arg)
100111389SAlexander.Kolbasov@Sun.COM {
100211389SAlexander.Kolbasov@Sun.COM cpu_t *cp;
100311389SAlexander.Kolbasov@Sun.COM int retval = 0;
100411389SAlexander.Kolbasov@Sun.COM
100511389SAlexander.Kolbasov@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
100611389SAlexander.Kolbasov@Sun.COM
100711389SAlexander.Kolbasov@Sun.COM if (!(cu_flags & CU_FLAG_ON))
100811389SAlexander.Kolbasov@Sun.COM return (-1);
100911389SAlexander.Kolbasov@Sun.COM
101011389SAlexander.Kolbasov@Sun.COM cp = cpu_get(id);
101111389SAlexander.Kolbasov@Sun.COM if (cp == NULL)
101211389SAlexander.Kolbasov@Sun.COM return (-2);
101311389SAlexander.Kolbasov@Sun.COM
101411389SAlexander.Kolbasov@Sun.COM switch (what) {
101511389SAlexander.Kolbasov@Sun.COM case CPU_ON:
101611389SAlexander.Kolbasov@Sun.COM /*
101711389SAlexander.Kolbasov@Sun.COM * Setup counters on CPU being turned on
101811389SAlexander.Kolbasov@Sun.COM */
101911389SAlexander.Kolbasov@Sun.COM retval = cu_cpu_init(cp, cu_cpc_reqs);
102011389SAlexander.Kolbasov@Sun.COM
102111389SAlexander.Kolbasov@Sun.COM /*
102211389SAlexander.Kolbasov@Sun.COM * Reset list of counter event requests so its space can be
102311389SAlexander.Kolbasov@Sun.COM * reused for a different set of requests for next CPU
102411389SAlexander.Kolbasov@Sun.COM */
102511389SAlexander.Kolbasov@Sun.COM (void) kcpc_reqs_reset(cu_cpc_reqs);
102611389SAlexander.Kolbasov@Sun.COM break;
102711389SAlexander.Kolbasov@Sun.COM case CPU_INTR_ON:
102811389SAlexander.Kolbasov@Sun.COM /*
102911389SAlexander.Kolbasov@Sun.COM * Setup counters on CPU being turned on.
103011389SAlexander.Kolbasov@Sun.COM */
103111389SAlexander.Kolbasov@Sun.COM retval = cu_cpu_run(cp, cu_cpc_program_xcall,
103211389SAlexander.Kolbasov@Sun.COM (uintptr_t)B_TRUE);
103311389SAlexander.Kolbasov@Sun.COM break;
103411389SAlexander.Kolbasov@Sun.COM case CPU_OFF:
103511389SAlexander.Kolbasov@Sun.COM /*
103611389SAlexander.Kolbasov@Sun.COM * Disable counters on CPU being turned off. Counters will not
103711389SAlexander.Kolbasov@Sun.COM * be re-enabled on this CPU until it comes back online.
103811389SAlexander.Kolbasov@Sun.COM */
103911389SAlexander.Kolbasov@Sun.COM cu_cpu_disable(cp);
104011389SAlexander.Kolbasov@Sun.COM ASSERT(!CU_CPC_ON(cp));
104111389SAlexander.Kolbasov@Sun.COM retval = cu_cpu_fini(cp);
104211389SAlexander.Kolbasov@Sun.COM break;
104311389SAlexander.Kolbasov@Sun.COM default:
104411389SAlexander.Kolbasov@Sun.COM break;
104511389SAlexander.Kolbasov@Sun.COM }
104611389SAlexander.Kolbasov@Sun.COM return (retval);
104711389SAlexander.Kolbasov@Sun.COM }
104811389SAlexander.Kolbasov@Sun.COM
104911389SAlexander.Kolbasov@Sun.COM
105011389SAlexander.Kolbasov@Sun.COM /*
105111389SAlexander.Kolbasov@Sun.COM * Disable or enable Capacity Utilization counters on a given CPU. This function
105211389SAlexander.Kolbasov@Sun.COM * can be called from any CPU to disable counters on the given CPU.
105311389SAlexander.Kolbasov@Sun.COM */
105411389SAlexander.Kolbasov@Sun.COM static void
cu_cpu_disable(cpu_t * cp)105511389SAlexander.Kolbasov@Sun.COM cu_cpu_disable(cpu_t *cp)
105611389SAlexander.Kolbasov@Sun.COM {
105711389SAlexander.Kolbasov@Sun.COM cpu_call(cp, cu_cpc_trigger, (uintptr_t)cp, (uintptr_t)B_FALSE);
105811389SAlexander.Kolbasov@Sun.COM }
105911389SAlexander.Kolbasov@Sun.COM
106011389SAlexander.Kolbasov@Sun.COM
106111389SAlexander.Kolbasov@Sun.COM static void
cu_cpu_enable(cpu_t * cp)106211389SAlexander.Kolbasov@Sun.COM cu_cpu_enable(cpu_t *cp)
106311389SAlexander.Kolbasov@Sun.COM {
106411389SAlexander.Kolbasov@Sun.COM cpu_call(cp, cu_cpc_trigger, (uintptr_t)cp, (uintptr_t)B_TRUE);
106511389SAlexander.Kolbasov@Sun.COM }
106611389SAlexander.Kolbasov@Sun.COM
106711389SAlexander.Kolbasov@Sun.COM
106811389SAlexander.Kolbasov@Sun.COM /*
106911389SAlexander.Kolbasov@Sun.COM * Setup capacity and utilization support for given CPU
107011389SAlexander.Kolbasov@Sun.COM *
107111389SAlexander.Kolbasov@Sun.COM * NOTE: Use KM_NOSLEEP for kmem_{,z}alloc() since cpu_lock is held and free
107211389SAlexander.Kolbasov@Sun.COM * everything that has been successfully allocated including cpu_cu_info
107311389SAlexander.Kolbasov@Sun.COM * if any memory allocation fails
107411389SAlexander.Kolbasov@Sun.COM */
107511389SAlexander.Kolbasov@Sun.COM static int
cu_cpu_init(cpu_t * cp,kcpc_request_list_t * reqs)107611389SAlexander.Kolbasov@Sun.COM cu_cpu_init(cpu_t *cp, kcpc_request_list_t *reqs)
107711389SAlexander.Kolbasov@Sun.COM {
107811389SAlexander.Kolbasov@Sun.COM kcpc_ctx_t **ctx_ptr_array;
107911389SAlexander.Kolbasov@Sun.COM size_t ctx_ptr_array_sz;
108011389SAlexander.Kolbasov@Sun.COM cu_cpc_ctx_t *cpu_ctx;
108111389SAlexander.Kolbasov@Sun.COM cu_cpu_info_t *cu_cpu_info;
108211389SAlexander.Kolbasov@Sun.COM int n;
108311389SAlexander.Kolbasov@Sun.COM
108411389SAlexander.Kolbasov@Sun.COM /*
108511389SAlexander.Kolbasov@Sun.COM * cpu_lock should be held and protect against CPU going away and races
108611389SAlexander.Kolbasov@Sun.COM * with cu_{init,fini,cpu_fini}()
108711389SAlexander.Kolbasov@Sun.COM */
108811389SAlexander.Kolbasov@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
108911389SAlexander.Kolbasov@Sun.COM
109011389SAlexander.Kolbasov@Sun.COM /*
109111389SAlexander.Kolbasov@Sun.COM * Return if not ready to setup counters yet
109211389SAlexander.Kolbasov@Sun.COM */
109311389SAlexander.Kolbasov@Sun.COM if (!(cu_flags & CU_FLAG_READY))
109411389SAlexander.Kolbasov@Sun.COM return (-1);
109511389SAlexander.Kolbasov@Sun.COM
109611389SAlexander.Kolbasov@Sun.COM if (cp->cpu_cu_info == NULL) {
109711389SAlexander.Kolbasov@Sun.COM cp->cpu_cu_info = kmem_zalloc(sizeof (cu_cpu_info_t),
109811389SAlexander.Kolbasov@Sun.COM KM_NOSLEEP);
109911389SAlexander.Kolbasov@Sun.COM if (cp->cpu_cu_info == NULL)
110011389SAlexander.Kolbasov@Sun.COM return (-2);
110111389SAlexander.Kolbasov@Sun.COM }
110211389SAlexander.Kolbasov@Sun.COM
110311389SAlexander.Kolbasov@Sun.COM /*
110411389SAlexander.Kolbasov@Sun.COM * Get capacity and utilization CPC context for CPU and check to see
110511389SAlexander.Kolbasov@Sun.COM * whether it has been setup already
110611389SAlexander.Kolbasov@Sun.COM */
110711389SAlexander.Kolbasov@Sun.COM cu_cpu_info = cp->cpu_cu_info;
110811389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_cpu = cp;
110911389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_disabled = dtrace_cpc_in_use ? 1 : 0;
111011389SAlexander.Kolbasov@Sun.COM
111111389SAlexander.Kolbasov@Sun.COM cpu_ctx = &cu_cpu_info->cu_cpc_ctx;
111211389SAlexander.Kolbasov@Sun.COM if (cpu_ctx->nctx > 0 && cpu_ctx->ctx_ptr_array != NULL &&
111311389SAlexander.Kolbasov@Sun.COM cpu_ctx->ctx_ptr_array_sz > 0) {
111411389SAlexander.Kolbasov@Sun.COM return (1);
111511389SAlexander.Kolbasov@Sun.COM }
111611389SAlexander.Kolbasov@Sun.COM
111711389SAlexander.Kolbasov@Sun.COM /*
111811389SAlexander.Kolbasov@Sun.COM * Should have no contexts since it hasn't been setup already
111911389SAlexander.Kolbasov@Sun.COM */
112011389SAlexander.Kolbasov@Sun.COM ASSERT(cpu_ctx->nctx == 0 && cpu_ctx->ctx_ptr_array == NULL &&
112111389SAlexander.Kolbasov@Sun.COM cpu_ctx->ctx_ptr_array_sz == 0);
112211389SAlexander.Kolbasov@Sun.COM
112311389SAlexander.Kolbasov@Sun.COM /*
112411389SAlexander.Kolbasov@Sun.COM * Determine how many CPC events needed to measure capacity and
112511389SAlexander.Kolbasov@Sun.COM * utilization for this CPU, allocate space for counter statistics for
112611389SAlexander.Kolbasov@Sun.COM * each event, and fill in list of CPC event requests with corresponding
112711389SAlexander.Kolbasov@Sun.COM * counter stats for each request to make attributing counter data
112811389SAlexander.Kolbasov@Sun.COM * easier later....
112911389SAlexander.Kolbasov@Sun.COM */
113011389SAlexander.Kolbasov@Sun.COM n = cu_cpc_init(cp, NULL, 0);
113111389SAlexander.Kolbasov@Sun.COM if (n <= 0) {
113211389SAlexander.Kolbasov@Sun.COM (void) cu_cpu_fini(cp);
113311389SAlexander.Kolbasov@Sun.COM return (-3);
113411389SAlexander.Kolbasov@Sun.COM }
113511389SAlexander.Kolbasov@Sun.COM
113611389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_cntr_stats = kmem_zalloc(n * sizeof (cu_cntr_stats_t),
113711389SAlexander.Kolbasov@Sun.COM KM_NOSLEEP);
113811389SAlexander.Kolbasov@Sun.COM if (cu_cpu_info->cu_cntr_stats == NULL) {
113911389SAlexander.Kolbasov@Sun.COM (void) cu_cpu_fini(cp);
114011389SAlexander.Kolbasov@Sun.COM return (-4);
114111389SAlexander.Kolbasov@Sun.COM }
114211389SAlexander.Kolbasov@Sun.COM
114311389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_ncntr_stats = n;
114411389SAlexander.Kolbasov@Sun.COM
114511389SAlexander.Kolbasov@Sun.COM n = cu_cpc_init(cp, reqs, n);
114611389SAlexander.Kolbasov@Sun.COM if (n <= 0) {
114711389SAlexander.Kolbasov@Sun.COM (void) cu_cpu_fini(cp);
114811389SAlexander.Kolbasov@Sun.COM return (-5);
114911389SAlexander.Kolbasov@Sun.COM }
115011389SAlexander.Kolbasov@Sun.COM
115111389SAlexander.Kolbasov@Sun.COM /*
115211389SAlexander.Kolbasov@Sun.COM * Create CPC context with given requests
115311389SAlexander.Kolbasov@Sun.COM */
115411389SAlexander.Kolbasov@Sun.COM ctx_ptr_array = NULL;
115511389SAlexander.Kolbasov@Sun.COM ctx_ptr_array_sz = 0;
115611389SAlexander.Kolbasov@Sun.COM n = kcpc_cpu_ctx_create(cp, reqs, KM_NOSLEEP, &ctx_ptr_array,
115711389SAlexander.Kolbasov@Sun.COM &ctx_ptr_array_sz);
115811389SAlexander.Kolbasov@Sun.COM if (n <= 0) {
115911389SAlexander.Kolbasov@Sun.COM (void) cu_cpu_fini(cp);
116011389SAlexander.Kolbasov@Sun.COM return (-6);
116111389SAlexander.Kolbasov@Sun.COM }
116211389SAlexander.Kolbasov@Sun.COM
116311389SAlexander.Kolbasov@Sun.COM /*
116411389SAlexander.Kolbasov@Sun.COM * Should have contexts
116511389SAlexander.Kolbasov@Sun.COM */
116611389SAlexander.Kolbasov@Sun.COM ASSERT(n > 0 && ctx_ptr_array != NULL && ctx_ptr_array_sz > 0);
116711389SAlexander.Kolbasov@Sun.COM if (ctx_ptr_array == NULL || ctx_ptr_array_sz <= 0) {
116811389SAlexander.Kolbasov@Sun.COM (void) cu_cpu_fini(cp);
116911389SAlexander.Kolbasov@Sun.COM return (-7);
117011389SAlexander.Kolbasov@Sun.COM }
117111389SAlexander.Kolbasov@Sun.COM
117211389SAlexander.Kolbasov@Sun.COM /*
117311389SAlexander.Kolbasov@Sun.COM * Fill in CPC context info for CPU needed for capacity and utilization
117411389SAlexander.Kolbasov@Sun.COM */
117511389SAlexander.Kolbasov@Sun.COM cpu_ctx->cur_index = 0;
117611389SAlexander.Kolbasov@Sun.COM cpu_ctx->nctx = n;
117711389SAlexander.Kolbasov@Sun.COM cpu_ctx->ctx_ptr_array = ctx_ptr_array;
117811389SAlexander.Kolbasov@Sun.COM cpu_ctx->ctx_ptr_array_sz = ctx_ptr_array_sz;
117911389SAlexander.Kolbasov@Sun.COM return (0);
118011389SAlexander.Kolbasov@Sun.COM }
118111389SAlexander.Kolbasov@Sun.COM
118211389SAlexander.Kolbasov@Sun.COM /*
118311389SAlexander.Kolbasov@Sun.COM * Tear down capacity and utilization support for given CPU
118411389SAlexander.Kolbasov@Sun.COM */
118511389SAlexander.Kolbasov@Sun.COM static int
cu_cpu_fini(cpu_t * cp)118611389SAlexander.Kolbasov@Sun.COM cu_cpu_fini(cpu_t *cp)
118711389SAlexander.Kolbasov@Sun.COM {
118811389SAlexander.Kolbasov@Sun.COM kcpc_ctx_t *ctx;
118911389SAlexander.Kolbasov@Sun.COM cu_cpc_ctx_t *cpu_ctx;
119011389SAlexander.Kolbasov@Sun.COM cu_cpu_info_t *cu_cpu_info;
119111389SAlexander.Kolbasov@Sun.COM int i;
119211389SAlexander.Kolbasov@Sun.COM pghw_type_t pg_hw_type;
119311389SAlexander.Kolbasov@Sun.COM
119411389SAlexander.Kolbasov@Sun.COM /*
119511389SAlexander.Kolbasov@Sun.COM * cpu_lock should be held and protect against CPU going away and races
119611389SAlexander.Kolbasov@Sun.COM * with cu_{init,fini,cpu_init}()
119711389SAlexander.Kolbasov@Sun.COM */
119811389SAlexander.Kolbasov@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
119911389SAlexander.Kolbasov@Sun.COM
120011389SAlexander.Kolbasov@Sun.COM /*
120111389SAlexander.Kolbasov@Sun.COM * Have to at least be ready to setup counters to have allocated
120211389SAlexander.Kolbasov@Sun.COM * anything that needs to be deallocated now
120311389SAlexander.Kolbasov@Sun.COM */
120411389SAlexander.Kolbasov@Sun.COM if (!(cu_flags & CU_FLAG_READY))
120511389SAlexander.Kolbasov@Sun.COM return (-1);
120611389SAlexander.Kolbasov@Sun.COM
120711389SAlexander.Kolbasov@Sun.COM /*
120811389SAlexander.Kolbasov@Sun.COM * Nothing to do if CPU's capacity and utilization info doesn't exist
120911389SAlexander.Kolbasov@Sun.COM */
121011389SAlexander.Kolbasov@Sun.COM cu_cpu_info = cp->cpu_cu_info;
121111389SAlexander.Kolbasov@Sun.COM if (cu_cpu_info == NULL)
121211389SAlexander.Kolbasov@Sun.COM return (1);
121311389SAlexander.Kolbasov@Sun.COM
121411389SAlexander.Kolbasov@Sun.COM /*
121511389SAlexander.Kolbasov@Sun.COM * Tear down any existing kstats and counter info for each hardware
121611389SAlexander.Kolbasov@Sun.COM * sharing relationship
121711389SAlexander.Kolbasov@Sun.COM */
121811389SAlexander.Kolbasov@Sun.COM for (pg_hw_type = PGHW_START; pg_hw_type < PGHW_NUM_COMPONENTS;
121911389SAlexander.Kolbasov@Sun.COM pg_hw_type++) {
122011389SAlexander.Kolbasov@Sun.COM cu_cntr_info_t *cntr_info;
122111389SAlexander.Kolbasov@Sun.COM
122211389SAlexander.Kolbasov@Sun.COM cntr_info = cu_cpu_info->cu_cntr_info[pg_hw_type];
122311389SAlexander.Kolbasov@Sun.COM if (cntr_info == NULL)
122411389SAlexander.Kolbasov@Sun.COM continue;
122511389SAlexander.Kolbasov@Sun.COM
122611389SAlexander.Kolbasov@Sun.COM if (cntr_info->ci_kstat != NULL) {
122711389SAlexander.Kolbasov@Sun.COM kstat_delete(cntr_info->ci_kstat);
122811389SAlexander.Kolbasov@Sun.COM cntr_info->ci_kstat = NULL;
122911389SAlexander.Kolbasov@Sun.COM }
123011389SAlexander.Kolbasov@Sun.COM kmem_free(cntr_info, sizeof (cu_cntr_info_t));
123111389SAlexander.Kolbasov@Sun.COM }
123211389SAlexander.Kolbasov@Sun.COM
123311389SAlexander.Kolbasov@Sun.COM /*
123411389SAlexander.Kolbasov@Sun.COM * Free counter statistics for CPU
123511389SAlexander.Kolbasov@Sun.COM */
123611389SAlexander.Kolbasov@Sun.COM ASSERT(cu_cpu_info->cu_cntr_stats == NULL ||
123711389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_ncntr_stats > 0);
123811389SAlexander.Kolbasov@Sun.COM if (cu_cpu_info->cu_cntr_stats != NULL &&
123911389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_ncntr_stats > 0) {
124011389SAlexander.Kolbasov@Sun.COM kmem_free(cu_cpu_info->cu_cntr_stats,
124111389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_ncntr_stats * sizeof (cu_cntr_stats_t));
124211389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_cntr_stats = NULL;
124311389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_ncntr_stats = 0;
124411389SAlexander.Kolbasov@Sun.COM }
124511389SAlexander.Kolbasov@Sun.COM
124611389SAlexander.Kolbasov@Sun.COM /*
124711389SAlexander.Kolbasov@Sun.COM * Get capacity and utilization CPC contexts for given CPU and check to
124811389SAlexander.Kolbasov@Sun.COM * see whether they have been freed already
124911389SAlexander.Kolbasov@Sun.COM */
125011389SAlexander.Kolbasov@Sun.COM cpu_ctx = &cu_cpu_info->cu_cpc_ctx;
125111389SAlexander.Kolbasov@Sun.COM if (cpu_ctx != NULL && cpu_ctx->ctx_ptr_array != NULL &&
125211389SAlexander.Kolbasov@Sun.COM cpu_ctx->ctx_ptr_array_sz > 0) {
125311389SAlexander.Kolbasov@Sun.COM /*
125411389SAlexander.Kolbasov@Sun.COM * Free CPC contexts for given CPU
125511389SAlexander.Kolbasov@Sun.COM */
125611389SAlexander.Kolbasov@Sun.COM for (i = 0; i < cpu_ctx->nctx; i++) {
125711389SAlexander.Kolbasov@Sun.COM ctx = cpu_ctx->ctx_ptr_array[i];
125811389SAlexander.Kolbasov@Sun.COM if (ctx == NULL)
125911389SAlexander.Kolbasov@Sun.COM continue;
126011389SAlexander.Kolbasov@Sun.COM kcpc_free(ctx, 0);
126111389SAlexander.Kolbasov@Sun.COM }
126211389SAlexander.Kolbasov@Sun.COM
126311389SAlexander.Kolbasov@Sun.COM /*
126411389SAlexander.Kolbasov@Sun.COM * Free CPC context pointer array
126511389SAlexander.Kolbasov@Sun.COM */
126611389SAlexander.Kolbasov@Sun.COM kmem_free(cpu_ctx->ctx_ptr_array, cpu_ctx->ctx_ptr_array_sz);
126711389SAlexander.Kolbasov@Sun.COM
126811389SAlexander.Kolbasov@Sun.COM /*
126911389SAlexander.Kolbasov@Sun.COM * Zero CPC info for CPU
127011389SAlexander.Kolbasov@Sun.COM */
127111389SAlexander.Kolbasov@Sun.COM bzero(cpu_ctx, sizeof (cu_cpc_ctx_t));
127211389SAlexander.Kolbasov@Sun.COM }
127311389SAlexander.Kolbasov@Sun.COM
127411389SAlexander.Kolbasov@Sun.COM /*
127511389SAlexander.Kolbasov@Sun.COM * Set cp->cpu_cu_info pointer to NULL. Go through cross-call to ensure
127611389SAlexander.Kolbasov@Sun.COM * that no one is going to access the cpu_cu_info whicch we are going to
127711389SAlexander.Kolbasov@Sun.COM * free.
127811389SAlexander.Kolbasov@Sun.COM */
127911389SAlexander.Kolbasov@Sun.COM if (cpu_is_online(cp))
128011389SAlexander.Kolbasov@Sun.COM cpu_call(cp, (cpu_call_func_t)cu_cpu_info_detach_xcall, 0, 0);
128111389SAlexander.Kolbasov@Sun.COM else
128211389SAlexander.Kolbasov@Sun.COM cp->cpu_cu_info = NULL;
128311389SAlexander.Kolbasov@Sun.COM
128411389SAlexander.Kolbasov@Sun.COM /*
128511389SAlexander.Kolbasov@Sun.COM * Free CPU's capacity and utilization info
128611389SAlexander.Kolbasov@Sun.COM */
128711389SAlexander.Kolbasov@Sun.COM kmem_free(cu_cpu_info, sizeof (cu_cpu_info_t));
128811389SAlexander.Kolbasov@Sun.COM
128911389SAlexander.Kolbasov@Sun.COM return (0);
129011389SAlexander.Kolbasov@Sun.COM }
129111389SAlexander.Kolbasov@Sun.COM
129211389SAlexander.Kolbasov@Sun.COM /*
129311389SAlexander.Kolbasov@Sun.COM * Create capacity & utilization kstats for given PG CPU hardware sharing
129411389SAlexander.Kolbasov@Sun.COM * relationship
129511389SAlexander.Kolbasov@Sun.COM */
129611389SAlexander.Kolbasov@Sun.COM static void
cu_cpu_kstat_create(pghw_t * pg,cu_cntr_info_t * cntr_info)129711389SAlexander.Kolbasov@Sun.COM cu_cpu_kstat_create(pghw_t *pg, cu_cntr_info_t *cntr_info)
129811389SAlexander.Kolbasov@Sun.COM {
129911389SAlexander.Kolbasov@Sun.COM kstat_t *ks;
1300*13124SAlexander.Kolbasov@Sun.COM char *sharing = pghw_type_string(pg->pghw_hw);
1301*13124SAlexander.Kolbasov@Sun.COM char name[KSTAT_STRLEN + 1];
130211389SAlexander.Kolbasov@Sun.COM
130311389SAlexander.Kolbasov@Sun.COM /*
130411389SAlexander.Kolbasov@Sun.COM * Just return when no counter info or CPU
130511389SAlexander.Kolbasov@Sun.COM */
130611389SAlexander.Kolbasov@Sun.COM if (cntr_info == NULL || cntr_info->ci_cpu == NULL)
130711389SAlexander.Kolbasov@Sun.COM return;
130811389SAlexander.Kolbasov@Sun.COM
130911389SAlexander.Kolbasov@Sun.COM /*
1310*13124SAlexander.Kolbasov@Sun.COM * Canonify PG name to conform to kstat name rules
131111389SAlexander.Kolbasov@Sun.COM */
1312*13124SAlexander.Kolbasov@Sun.COM (void) strncpy(name, pghw_type_string(pg->pghw_hw), KSTAT_STRLEN + 1);
1313*13124SAlexander.Kolbasov@Sun.COM strident_canon(name, TASKQ_NAMELEN + 1);
131411389SAlexander.Kolbasov@Sun.COM
1315*13124SAlexander.Kolbasov@Sun.COM if ((ks = kstat_create_zone("pg_hw_perf_cpu",
1316*13124SAlexander.Kolbasov@Sun.COM cntr_info->ci_cpu->cpu_id,
1317*13124SAlexander.Kolbasov@Sun.COM name, "processor_group", KSTAT_TYPE_NAMED,
131811389SAlexander.Kolbasov@Sun.COM sizeof (cu_cpu_kstat) / sizeof (kstat_named_t),
131911389SAlexander.Kolbasov@Sun.COM KSTAT_FLAG_VIRTUAL, GLOBAL_ZONEID)) == NULL)
132011389SAlexander.Kolbasov@Sun.COM return;
132111389SAlexander.Kolbasov@Sun.COM
132211389SAlexander.Kolbasov@Sun.COM ks->ks_lock = &pg_cpu_kstat_lock;
132311389SAlexander.Kolbasov@Sun.COM ks->ks_data = &cu_cpu_kstat;
132411389SAlexander.Kolbasov@Sun.COM ks->ks_update = cu_cpu_kstat_update;
1325*13124SAlexander.Kolbasov@Sun.COM ks->ks_data_size += strlen(sharing) + 1;
132611389SAlexander.Kolbasov@Sun.COM
132711389SAlexander.Kolbasov@Sun.COM ks->ks_private = cntr_info;
132811389SAlexander.Kolbasov@Sun.COM cntr_info->ci_kstat = ks;
132911389SAlexander.Kolbasov@Sun.COM kstat_install(cntr_info->ci_kstat);
133011389SAlexander.Kolbasov@Sun.COM }
133111389SAlexander.Kolbasov@Sun.COM
133211389SAlexander.Kolbasov@Sun.COM
133311389SAlexander.Kolbasov@Sun.COM /*
133411389SAlexander.Kolbasov@Sun.COM * Propagate values from CPU capacity & utilization stats to kstats
133511389SAlexander.Kolbasov@Sun.COM */
133611389SAlexander.Kolbasov@Sun.COM static int
cu_cpu_kstat_update(kstat_t * ksp,int rw)133711389SAlexander.Kolbasov@Sun.COM cu_cpu_kstat_update(kstat_t *ksp, int rw)
133811389SAlexander.Kolbasov@Sun.COM {
133911389SAlexander.Kolbasov@Sun.COM cpu_t *cp;
134011389SAlexander.Kolbasov@Sun.COM cu_cntr_info_t *cntr_info = ksp->ks_private;
134111389SAlexander.Kolbasov@Sun.COM struct cu_cpu_kstat *kstat = &cu_cpu_kstat;
134211389SAlexander.Kolbasov@Sun.COM pghw_t *pg;
134311389SAlexander.Kolbasov@Sun.COM cu_cntr_stats_t *stats;
134411389SAlexander.Kolbasov@Sun.COM
134511389SAlexander.Kolbasov@Sun.COM if (rw == KSTAT_WRITE)
134611389SAlexander.Kolbasov@Sun.COM return (EACCES);
134711389SAlexander.Kolbasov@Sun.COM
1348*13124SAlexander.Kolbasov@Sun.COM cp = cntr_info->ci_cpu;
1349*13124SAlexander.Kolbasov@Sun.COM pg = cntr_info->ci_pg;
1350*13124SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_id.value.ui32 = cp->cpu_id;
1351*13124SAlexander.Kolbasov@Sun.COM kstat->cu_pg_id.value.i32 = ((pg_t *)pg)->pg_id;
1352*13124SAlexander.Kolbasov@Sun.COM
1353*13124SAlexander.Kolbasov@Sun.COM /*
1354*13124SAlexander.Kolbasov@Sun.COM * The caller should have priv_cpc_cpu privilege to get utilization
1355*13124SAlexander.Kolbasov@Sun.COM * data. Callers who do not have the privilege will see zeroes as the
1356*13124SAlexander.Kolbasov@Sun.COM * values.
1357*13124SAlexander.Kolbasov@Sun.COM */
1358*13124SAlexander.Kolbasov@Sun.COM if (secpolicy_cpc_cpu(crgetcred()) != 0) {
1359*13124SAlexander.Kolbasov@Sun.COM kstat->cu_generation.value.ui32 = cp->cpu_generation;
1360*13124SAlexander.Kolbasov@Sun.COM kstat_named_setstr(&kstat->cu_cpu_relationship,
1361*13124SAlexander.Kolbasov@Sun.COM pghw_type_string(pg->pghw_hw));
1362*13124SAlexander.Kolbasov@Sun.COM
1363*13124SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_util.value.ui64 = 0;
1364*13124SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_rate.value.ui64 = 0;
1365*13124SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_rate_max.value.ui64 = 0;
1366*13124SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_time_running.value.ui64 = 0;
1367*13124SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_time_stopped.value.ui64 = 0;
1368*13124SAlexander.Kolbasov@Sun.COM
1369*13124SAlexander.Kolbasov@Sun.COM return (0);
1370*13124SAlexander.Kolbasov@Sun.COM }
1371*13124SAlexander.Kolbasov@Sun.COM
137211389SAlexander.Kolbasov@Sun.COM kpreempt_disable();
137311389SAlexander.Kolbasov@Sun.COM
137411389SAlexander.Kolbasov@Sun.COM /*
137511389SAlexander.Kolbasov@Sun.COM * Update capacity and utilization statistics needed for CPU's PG (CPU)
137611389SAlexander.Kolbasov@Sun.COM * kstats
137711389SAlexander.Kolbasov@Sun.COM */
1378*13124SAlexander.Kolbasov@Sun.COM
137911389SAlexander.Kolbasov@Sun.COM (void) cu_cpu_update(cp, B_TRUE);
138011389SAlexander.Kolbasov@Sun.COM
138111389SAlexander.Kolbasov@Sun.COM stats = cntr_info->ci_stats;
138211389SAlexander.Kolbasov@Sun.COM kstat->cu_generation.value.ui32 = cp->cpu_generation;
1383*13124SAlexander.Kolbasov@Sun.COM kstat_named_setstr(&kstat->cu_cpu_relationship,
1384*13124SAlexander.Kolbasov@Sun.COM pghw_type_string(pg->pghw_hw));
138511389SAlexander.Kolbasov@Sun.COM
138611389SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_util.value.ui64 = stats->cs_value_total;
138711389SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_rate.value.ui64 = stats->cs_rate;
138811389SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_rate_max.value.ui64 = stats->cs_rate_max;
138911389SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_time_running.value.ui64 = stats->cs_time_running;
139011389SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_time_stopped.value.ui64 = stats->cs_time_stopped;
1391*13124SAlexander.Kolbasov@Sun.COM
139211389SAlexander.Kolbasov@Sun.COM /*
139311389SAlexander.Kolbasov@Sun.COM * Counters are stopped now, so the cs_time_stopped was last
139411389SAlexander.Kolbasov@Sun.COM * updated at cs_time_start time. Add the time passed since then
139511389SAlexander.Kolbasov@Sun.COM * to the stopped time.
139611389SAlexander.Kolbasov@Sun.COM */
139711389SAlexander.Kolbasov@Sun.COM if (!(cp->cpu_cu_info->cu_flag & CU_CPU_CNTRS_ON))
139811389SAlexander.Kolbasov@Sun.COM kstat->cu_cpu_time_stopped.value.ui64 +=
139911389SAlexander.Kolbasov@Sun.COM gethrtime() - stats->cs_time_start;
140011389SAlexander.Kolbasov@Sun.COM
140111389SAlexander.Kolbasov@Sun.COM kpreempt_enable();
140211389SAlexander.Kolbasov@Sun.COM
140311389SAlexander.Kolbasov@Sun.COM return (0);
140411389SAlexander.Kolbasov@Sun.COM }
140511389SAlexander.Kolbasov@Sun.COM
140611389SAlexander.Kolbasov@Sun.COM /*
140711389SAlexander.Kolbasov@Sun.COM * Run specified function with specified argument on a given CPU and return
140811389SAlexander.Kolbasov@Sun.COM * whatever the function returns
140911389SAlexander.Kolbasov@Sun.COM */
141011389SAlexander.Kolbasov@Sun.COM static int
cu_cpu_run(cpu_t * cp,cu_cpu_func_t func,uintptr_t arg)141111389SAlexander.Kolbasov@Sun.COM cu_cpu_run(cpu_t *cp, cu_cpu_func_t func, uintptr_t arg)
141211389SAlexander.Kolbasov@Sun.COM {
141311389SAlexander.Kolbasov@Sun.COM int error = 0;
141411389SAlexander.Kolbasov@Sun.COM
141511389SAlexander.Kolbasov@Sun.COM /*
141611389SAlexander.Kolbasov@Sun.COM * cpu_call() will call func on the CPU specified with given argument
141711389SAlexander.Kolbasov@Sun.COM * and return func's return value in last argument
141811389SAlexander.Kolbasov@Sun.COM */
141911389SAlexander.Kolbasov@Sun.COM cpu_call(cp, (cpu_call_func_t)func, arg, (uintptr_t)&error);
142011389SAlexander.Kolbasov@Sun.COM return (error);
142111389SAlexander.Kolbasov@Sun.COM }
142211389SAlexander.Kolbasov@Sun.COM
142311389SAlexander.Kolbasov@Sun.COM
142411389SAlexander.Kolbasov@Sun.COM /*
142511389SAlexander.Kolbasov@Sun.COM * Update counter statistics on a given CPU.
142611389SAlexander.Kolbasov@Sun.COM *
142711389SAlexander.Kolbasov@Sun.COM * If move_to argument is True, execute the function on the CPU specified
142811389SAlexander.Kolbasov@Sun.COM * Otherwise, assume that it is already runninng on the right CPU
142911389SAlexander.Kolbasov@Sun.COM *
143011389SAlexander.Kolbasov@Sun.COM * If move_to is specified, the caller should hold cpu_lock or have preemption
143111389SAlexander.Kolbasov@Sun.COM * disabled. Otherwise it is up to the caller to guarantee that things do not
143211389SAlexander.Kolbasov@Sun.COM * change in the process.
143311389SAlexander.Kolbasov@Sun.COM */
143411389SAlexander.Kolbasov@Sun.COM int
cu_cpu_update(struct cpu * cp,boolean_t move_to)143511389SAlexander.Kolbasov@Sun.COM cu_cpu_update(struct cpu *cp, boolean_t move_to)
143611389SAlexander.Kolbasov@Sun.COM {
143711389SAlexander.Kolbasov@Sun.COM int retval;
143811389SAlexander.Kolbasov@Sun.COM cu_cpu_info_t *cu_cpu_info = cp->cpu_cu_info;
143911389SAlexander.Kolbasov@Sun.COM hrtime_t time_snap;
144011389SAlexander.Kolbasov@Sun.COM
144111389SAlexander.Kolbasov@Sun.COM ASSERT(!move_to || MUTEX_HELD(&cpu_lock) || curthread->t_preempt > 0);
144211389SAlexander.Kolbasov@Sun.COM
144311389SAlexander.Kolbasov@Sun.COM /*
144411389SAlexander.Kolbasov@Sun.COM * Nothing to do if counters are not programmed
144511389SAlexander.Kolbasov@Sun.COM */
144611389SAlexander.Kolbasov@Sun.COM if (!(cu_flags & CU_FLAG_ON) ||
144711389SAlexander.Kolbasov@Sun.COM (cu_cpu_info == NULL) ||
144811389SAlexander.Kolbasov@Sun.COM !(cu_cpu_info->cu_flag & CU_CPU_CNTRS_ON))
144911389SAlexander.Kolbasov@Sun.COM return (0);
145011389SAlexander.Kolbasov@Sun.COM
145111389SAlexander.Kolbasov@Sun.COM /*
145211389SAlexander.Kolbasov@Sun.COM * Don't update CPU statistics if it was updated recently
145311389SAlexander.Kolbasov@Sun.COM * and provide old results instead
145411389SAlexander.Kolbasov@Sun.COM */
145511389SAlexander.Kolbasov@Sun.COM time_snap = gethrtime();
145611389SAlexander.Kolbasov@Sun.COM if ((time_snap - cu_cpu_info->cu_sample_time) < cu_update_threshold) {
145711389SAlexander.Kolbasov@Sun.COM DTRACE_PROBE1(cu__drop__sample, cpu_t *, cp);
145811389SAlexander.Kolbasov@Sun.COM return (0);
145911389SAlexander.Kolbasov@Sun.COM }
146011389SAlexander.Kolbasov@Sun.COM
146111389SAlexander.Kolbasov@Sun.COM cu_cpu_info->cu_sample_time = time_snap;
146211389SAlexander.Kolbasov@Sun.COM
146311389SAlexander.Kolbasov@Sun.COM /*
146411389SAlexander.Kolbasov@Sun.COM * CPC counter should be read on the CPU that is running the counter. We
146511389SAlexander.Kolbasov@Sun.COM * either have to move ourselves to the target CPU or insure that we
146611389SAlexander.Kolbasov@Sun.COM * already run there.
146711389SAlexander.Kolbasov@Sun.COM *
146811389SAlexander.Kolbasov@Sun.COM * We use cross-call to the target CPU to execute kcpc_read() and
146911389SAlexander.Kolbasov@Sun.COM * cu_cpu_update_stats() there.
147011389SAlexander.Kolbasov@Sun.COM */
147111389SAlexander.Kolbasov@Sun.COM retval = 0;
147211389SAlexander.Kolbasov@Sun.COM if (move_to)
147311389SAlexander.Kolbasov@Sun.COM (void) cu_cpu_run(cp, (cu_cpu_func_t)kcpc_read,
147411389SAlexander.Kolbasov@Sun.COM (uintptr_t)cu_cpu_update_stats);
147511389SAlexander.Kolbasov@Sun.COM else {
147611389SAlexander.Kolbasov@Sun.COM retval = kcpc_read((kcpc_update_func_t)cu_cpu_update_stats);
147711389SAlexander.Kolbasov@Sun.COM /*
147811389SAlexander.Kolbasov@Sun.COM * Offset negative return value by -10 so we can distinguish it
147911389SAlexander.Kolbasov@Sun.COM * from error return values of this routine vs kcpc_read()
148011389SAlexander.Kolbasov@Sun.COM */
148111389SAlexander.Kolbasov@Sun.COM if (retval < 0)
148211389SAlexander.Kolbasov@Sun.COM retval -= 10;
148311389SAlexander.Kolbasov@Sun.COM }
148411389SAlexander.Kolbasov@Sun.COM
148511389SAlexander.Kolbasov@Sun.COM return (retval);
148611389SAlexander.Kolbasov@Sun.COM }
148711389SAlexander.Kolbasov@Sun.COM
148811389SAlexander.Kolbasov@Sun.COM
148911389SAlexander.Kolbasov@Sun.COM /*
149011389SAlexander.Kolbasov@Sun.COM * Update CPU counter statistics for current CPU.
149111389SAlexander.Kolbasov@Sun.COM * This function may be called from a cross-call
149211389SAlexander.Kolbasov@Sun.COM */
149311389SAlexander.Kolbasov@Sun.COM static int
cu_cpu_update_stats(cu_cntr_stats_t * stats,uint64_t cntr_value)149411389SAlexander.Kolbasov@Sun.COM cu_cpu_update_stats(cu_cntr_stats_t *stats, uint64_t cntr_value)
149511389SAlexander.Kolbasov@Sun.COM {
149611389SAlexander.Kolbasov@Sun.COM cu_cpu_info_t *cu_cpu_info = CPU->cpu_cu_info;
149711389SAlexander.Kolbasov@Sun.COM uint_t flags;
149811389SAlexander.Kolbasov@Sun.COM uint64_t delta;
149911389SAlexander.Kolbasov@Sun.COM hrtime_t time_delta;
150011389SAlexander.Kolbasov@Sun.COM hrtime_t time_snap;
150111389SAlexander.Kolbasov@Sun.COM
150211389SAlexander.Kolbasov@Sun.COM if (stats == NULL)
150311389SAlexander.Kolbasov@Sun.COM return (-1);
150411389SAlexander.Kolbasov@Sun.COM
150511389SAlexander.Kolbasov@Sun.COM /*
150611389SAlexander.Kolbasov@Sun.COM * Nothing to do if counters are not programmed. This should not happen,
150711389SAlexander.Kolbasov@Sun.COM * but we check just in case.
150811389SAlexander.Kolbasov@Sun.COM */
150911389SAlexander.Kolbasov@Sun.COM ASSERT(cu_flags & CU_FLAG_ON);
151011389SAlexander.Kolbasov@Sun.COM ASSERT(cu_cpu_info != NULL);
151111389SAlexander.Kolbasov@Sun.COM if (!(cu_flags & CU_FLAG_ON) ||
151211389SAlexander.Kolbasov@Sun.COM (cu_cpu_info == NULL))
151311389SAlexander.Kolbasov@Sun.COM return (-2);
151411389SAlexander.Kolbasov@Sun.COM
151511389SAlexander.Kolbasov@Sun.COM flags = cu_cpu_info->cu_flag;
151611389SAlexander.Kolbasov@Sun.COM ASSERT(flags & CU_CPU_CNTRS_ON);
151711389SAlexander.Kolbasov@Sun.COM if (!(flags & CU_CPU_CNTRS_ON))
151811389SAlexander.Kolbasov@Sun.COM return (-2);
151911389SAlexander.Kolbasov@Sun.COM
152011389SAlexander.Kolbasov@Sun.COM /*
152111389SAlexander.Kolbasov@Sun.COM * Take snapshot of high resolution timer
152211389SAlexander.Kolbasov@Sun.COM */
152311389SAlexander.Kolbasov@Sun.COM time_snap = gethrtime();
152411389SAlexander.Kolbasov@Sun.COM
152511389SAlexander.Kolbasov@Sun.COM /*
152611389SAlexander.Kolbasov@Sun.COM * CU counters have just been programmed. We cannot assume that the new
152711389SAlexander.Kolbasov@Sun.COM * cntr_value continues from where we left off, so use the cntr_value as
152811389SAlexander.Kolbasov@Sun.COM * the new initial value.
152911389SAlexander.Kolbasov@Sun.COM */
153011389SAlexander.Kolbasov@Sun.COM if (flags & CU_CPU_CNTRS_OFF_ON)
153111389SAlexander.Kolbasov@Sun.COM stats->cs_value_start = cntr_value;
153211389SAlexander.Kolbasov@Sun.COM
153311389SAlexander.Kolbasov@Sun.COM /*
153411389SAlexander.Kolbasov@Sun.COM * Calculate delta in counter values between start of sampling period
153511389SAlexander.Kolbasov@Sun.COM * and now
153611389SAlexander.Kolbasov@Sun.COM */
153711389SAlexander.Kolbasov@Sun.COM delta = cntr_value - stats->cs_value_start;
153811389SAlexander.Kolbasov@Sun.COM
153911389SAlexander.Kolbasov@Sun.COM /*
154011389SAlexander.Kolbasov@Sun.COM * Calculate time between start of sampling period and now
154111389SAlexander.Kolbasov@Sun.COM */
154211389SAlexander.Kolbasov@Sun.COM time_delta = stats->cs_time_start ?
154311389SAlexander.Kolbasov@Sun.COM time_snap - stats->cs_time_start :
154411389SAlexander.Kolbasov@Sun.COM 0;
154511389SAlexander.Kolbasov@Sun.COM stats->cs_time_start = time_snap;
154611389SAlexander.Kolbasov@Sun.COM stats->cs_value_start = cntr_value;
154711389SAlexander.Kolbasov@Sun.COM
154811389SAlexander.Kolbasov@Sun.COM if (time_delta > 0) { /* wrap shouldn't happen */
154911389SAlexander.Kolbasov@Sun.COM /*
155011389SAlexander.Kolbasov@Sun.COM * Update either running or stopped time based on the transition
155111389SAlexander.Kolbasov@Sun.COM * state
155211389SAlexander.Kolbasov@Sun.COM */
155311389SAlexander.Kolbasov@Sun.COM if (flags & CU_CPU_CNTRS_OFF_ON)
155411389SAlexander.Kolbasov@Sun.COM stats->cs_time_stopped += time_delta;
155511389SAlexander.Kolbasov@Sun.COM else
155611389SAlexander.Kolbasov@Sun.COM stats->cs_time_running += time_delta;
155711389SAlexander.Kolbasov@Sun.COM }
155811389SAlexander.Kolbasov@Sun.COM
155911389SAlexander.Kolbasov@Sun.COM /*
156011389SAlexander.Kolbasov@Sun.COM * Update rest of counter statistics if counter value didn't wrap
156111389SAlexander.Kolbasov@Sun.COM */
156211389SAlexander.Kolbasov@Sun.COM if (delta > 0) {
156311389SAlexander.Kolbasov@Sun.COM /*
156411389SAlexander.Kolbasov@Sun.COM * Update utilization rate if the interval between samples is
156511389SAlexander.Kolbasov@Sun.COM * sufficient.
156611389SAlexander.Kolbasov@Sun.COM */
156711389SAlexander.Kolbasov@Sun.COM ASSERT(cu_sample_interval_min > CU_SCALE);
156811389SAlexander.Kolbasov@Sun.COM if (time_delta > cu_sample_interval_min)
156911389SAlexander.Kolbasov@Sun.COM stats->cs_rate = CU_RATE(delta, time_delta);
157011389SAlexander.Kolbasov@Sun.COM if (stats->cs_rate_max < stats->cs_rate)
157111389SAlexander.Kolbasov@Sun.COM stats->cs_rate_max = stats->cs_rate;
157211389SAlexander.Kolbasov@Sun.COM
157311389SAlexander.Kolbasov@Sun.COM stats->cs_value_last = delta;
157411389SAlexander.Kolbasov@Sun.COM stats->cs_value_total += delta;
157511389SAlexander.Kolbasov@Sun.COM }
157611389SAlexander.Kolbasov@Sun.COM
157711389SAlexander.Kolbasov@Sun.COM return (0);
157811389SAlexander.Kolbasov@Sun.COM }
157911389SAlexander.Kolbasov@Sun.COM
158011389SAlexander.Kolbasov@Sun.COM /*
158111389SAlexander.Kolbasov@Sun.COM * Update CMT PG utilization data.
158211389SAlexander.Kolbasov@Sun.COM *
158311389SAlexander.Kolbasov@Sun.COM * This routine computes the running total utilization and times for the
158411389SAlexander.Kolbasov@Sun.COM * specified PG by adding up the total utilization and counter running and
158511389SAlexander.Kolbasov@Sun.COM * stopped times of all CPUs in the PG and calculates the utilization rate and
158611389SAlexander.Kolbasov@Sun.COM * maximum rate for all CPUs in the PG.
158711389SAlexander.Kolbasov@Sun.COM */
158811389SAlexander.Kolbasov@Sun.COM void
cu_pg_update(pghw_t * pg)158911389SAlexander.Kolbasov@Sun.COM cu_pg_update(pghw_t *pg)
159011389SAlexander.Kolbasov@Sun.COM {
159111389SAlexander.Kolbasov@Sun.COM pg_cpu_itr_t cpu_iter;
159211389SAlexander.Kolbasov@Sun.COM pghw_type_t pg_hwtype;
159311389SAlexander.Kolbasov@Sun.COM cpu_t *cpu;
159411389SAlexander.Kolbasov@Sun.COM pghw_util_t *hw_util = &pg->pghw_stats;
159511389SAlexander.Kolbasov@Sun.COM uint64_t old_utilization = hw_util->pghw_util;
159611389SAlexander.Kolbasov@Sun.COM hrtime_t now;
159711389SAlexander.Kolbasov@Sun.COM hrtime_t time_delta;
159811389SAlexander.Kolbasov@Sun.COM uint64_t utilization_delta;
159911389SAlexander.Kolbasov@Sun.COM
160011389SAlexander.Kolbasov@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
160111389SAlexander.Kolbasov@Sun.COM
160211389SAlexander.Kolbasov@Sun.COM now = gethrtime();
160311389SAlexander.Kolbasov@Sun.COM
160411389SAlexander.Kolbasov@Sun.COM pg_hwtype = pg->pghw_hw;
160511389SAlexander.Kolbasov@Sun.COM
160611389SAlexander.Kolbasov@Sun.COM /*
160711389SAlexander.Kolbasov@Sun.COM * Initialize running total utilization and times for PG to 0
160811389SAlexander.Kolbasov@Sun.COM */
160911389SAlexander.Kolbasov@Sun.COM hw_util->pghw_util = 0;
161011389SAlexander.Kolbasov@Sun.COM hw_util->pghw_time_running = 0;
161111389SAlexander.Kolbasov@Sun.COM hw_util->pghw_time_stopped = 0;
161211389SAlexander.Kolbasov@Sun.COM
161311389SAlexander.Kolbasov@Sun.COM /*
161411389SAlexander.Kolbasov@Sun.COM * Iterate over all CPUs in the PG and aggregate utilization, running
161511389SAlexander.Kolbasov@Sun.COM * time and stopped time.
161611389SAlexander.Kolbasov@Sun.COM */
161711389SAlexander.Kolbasov@Sun.COM PG_CPU_ITR_INIT(pg, cpu_iter);
161811389SAlexander.Kolbasov@Sun.COM while ((cpu = pg_cpu_next(&cpu_iter)) != NULL) {
161911389SAlexander.Kolbasov@Sun.COM cu_cpu_info_t *cu_cpu_info = cpu->cpu_cu_info;
162011389SAlexander.Kolbasov@Sun.COM cu_cntr_info_t *cntr_info;
162111389SAlexander.Kolbasov@Sun.COM cu_cntr_stats_t *stats;
162211389SAlexander.Kolbasov@Sun.COM
162311389SAlexander.Kolbasov@Sun.COM if (cu_cpu_info == NULL)
162411389SAlexander.Kolbasov@Sun.COM continue;
162511389SAlexander.Kolbasov@Sun.COM
162611389SAlexander.Kolbasov@Sun.COM /*
162711389SAlexander.Kolbasov@Sun.COM * Update utilization data for the CPU and then
162811389SAlexander.Kolbasov@Sun.COM * aggregate per CPU running totals for PG
162911389SAlexander.Kolbasov@Sun.COM */
163011389SAlexander.Kolbasov@Sun.COM (void) cu_cpu_update(cpu, B_TRUE);
163111389SAlexander.Kolbasov@Sun.COM cntr_info = cu_cpu_info->cu_cntr_info[pg_hwtype];
163211389SAlexander.Kolbasov@Sun.COM
163311389SAlexander.Kolbasov@Sun.COM if (cntr_info == NULL || (stats = cntr_info->ci_stats) == NULL)
163411389SAlexander.Kolbasov@Sun.COM continue;
163511389SAlexander.Kolbasov@Sun.COM
163611389SAlexander.Kolbasov@Sun.COM hw_util->pghw_util += stats->cs_value_total;
163711389SAlexander.Kolbasov@Sun.COM hw_util->pghw_time_running += stats->cs_time_running;
163811389SAlexander.Kolbasov@Sun.COM hw_util->pghw_time_stopped += stats->cs_time_stopped;
163911389SAlexander.Kolbasov@Sun.COM
164011389SAlexander.Kolbasov@Sun.COM /*
164111389SAlexander.Kolbasov@Sun.COM * If counters are stopped now, the pg_time_stopped was last
164211389SAlexander.Kolbasov@Sun.COM * updated at cs_time_start time. Add the time passed since then
164311389SAlexander.Kolbasov@Sun.COM * to the stopped time.
164411389SAlexander.Kolbasov@Sun.COM */
164511389SAlexander.Kolbasov@Sun.COM if (!(cu_cpu_info->cu_flag & CU_CPU_CNTRS_ON))
164611389SAlexander.Kolbasov@Sun.COM hw_util->pghw_time_stopped +=
164711389SAlexander.Kolbasov@Sun.COM now - stats->cs_time_start;
164811389SAlexander.Kolbasov@Sun.COM }
164911389SAlexander.Kolbasov@Sun.COM
165011389SAlexander.Kolbasov@Sun.COM /*
165111389SAlexander.Kolbasov@Sun.COM * Compute per PG instruction rate and maximum rate
165211389SAlexander.Kolbasov@Sun.COM */
165311389SAlexander.Kolbasov@Sun.COM time_delta = now - hw_util->pghw_time_stamp;
165411389SAlexander.Kolbasov@Sun.COM hw_util->pghw_time_stamp = now;
165511389SAlexander.Kolbasov@Sun.COM
165611389SAlexander.Kolbasov@Sun.COM if (old_utilization == 0)
165711389SAlexander.Kolbasov@Sun.COM return;
165811389SAlexander.Kolbasov@Sun.COM
165911389SAlexander.Kolbasov@Sun.COM /*
166011389SAlexander.Kolbasov@Sun.COM * Calculate change in utilization over sampling period and set this to
166111389SAlexander.Kolbasov@Sun.COM * 0 if the delta would be 0 or negative which may happen if any CPUs go
166211389SAlexander.Kolbasov@Sun.COM * offline during the sampling period
166311389SAlexander.Kolbasov@Sun.COM */
166411389SAlexander.Kolbasov@Sun.COM if (hw_util->pghw_util > old_utilization)
166511389SAlexander.Kolbasov@Sun.COM utilization_delta = hw_util->pghw_util - old_utilization;
166611389SAlexander.Kolbasov@Sun.COM else
166711389SAlexander.Kolbasov@Sun.COM utilization_delta = 0;
166811389SAlexander.Kolbasov@Sun.COM
166911389SAlexander.Kolbasov@Sun.COM /*
167011389SAlexander.Kolbasov@Sun.COM * Update utilization rate if the interval between samples is
167111389SAlexander.Kolbasov@Sun.COM * sufficient.
167211389SAlexander.Kolbasov@Sun.COM */
167311389SAlexander.Kolbasov@Sun.COM ASSERT(cu_sample_interval_min > CU_SCALE);
167411389SAlexander.Kolbasov@Sun.COM if (time_delta > CU_SAMPLE_INTERVAL_MIN)
167511389SAlexander.Kolbasov@Sun.COM hw_util->pghw_rate = CU_RATE(utilization_delta, time_delta);
167611389SAlexander.Kolbasov@Sun.COM
167711389SAlexander.Kolbasov@Sun.COM /*
167811389SAlexander.Kolbasov@Sun.COM * Update the maximum observed rate
167911389SAlexander.Kolbasov@Sun.COM */
168011389SAlexander.Kolbasov@Sun.COM if (hw_util->pghw_rate_max < hw_util->pghw_rate)
168111389SAlexander.Kolbasov@Sun.COM hw_util->pghw_rate_max = hw_util->pghw_rate;
168211389SAlexander.Kolbasov@Sun.COM }
1683