4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
27 * Support for determining capacity and utilization of performance relevant
28 * hardware components in a computer
32 * The capacity and utilization of the performance relevant hardware components
33 * is needed to be able to optimize performance while minimizing the amount of
34 * power used on a system. The idea is to use hardware performance counters
35 * and potentially other means to determine the capacity and utilization of
36 * performance relevant hardware components (eg. execution pipeline, cache,
37 * memory, etc.) and attribute the utilization to the responsible CPU and the
38 * thread running there.
40 * This will help characterize the utilization of performance relevant
41 * components and how much is used by each CPU and each thread. With
42 * that data, the utilization can be aggregated to all the CPUs sharing each
43 * performance relevant hardware component to calculate the total utilization
44 * of each component and compare that with the component's capacity to
45 * essentially determine the actual hardware load of the component. The
46 * hardware utilization attributed to each running thread can also be
47 * aggregated to determine the total hardware utilization of each component to
50 * Once that is done, one can determine how much of each performance relevant
51 * hardware component is needed by a given thread or set of threads (eg. a
52 * workload) and size up exactly what hardware is needed by the threads and how
53 * much. With this info, we can better place threads among CPUs to match their
54 * exact hardware resource needs and potentially lower or raise the power based
55 * on their utilization or pack threads onto the fewest hardware components
56 * needed and power off any remaining unused components to minimize power
57 * without sacrificing performance.
61 * The code has been designed and implemented to make (un)programming and
62 * reading the counters for a given CPU as lightweight and fast as possible.
63 * This is very important because we need to read and potentially (un)program
64 * the counters very often and in performance sensitive code. Specifically,
65 * the counters may need to be (un)programmed during context switch and/or a
66 * cyclic handler when there are more counter events to count than existing
69 * Consequently, the code has been split up to allow allocating and
70 * initializing everything needed to program and read the counters on a given
71 * CPU once and make (un)programming and reading the counters for a given CPU
72 * not have to allocate/free memory or grab any locks. To do this, all the
73 * state needed to (un)program and read the counters on a CPU is kept per CPU
74 * and is made lock free by forcing any code that reads or manipulates the
75 * counters or the state needed to (un)program or read the counters to run on
76 * the target CPU and disable preemption while running on the target CPU to
77 * protect any critical sections. All counter manipulation on the target CPU is
78 * happening either from a cross-call to the target CPU or at the same PIL as
79 * used by the cross-call subsystem. This guarantees that counter manipulation
80 * is not interrupted by cross-calls from other CPUs.
82 * The synchronization has been made lock free or as simple as possible for
83 * performance and to avoid getting the locking all tangled up when we interpose
84 * on the CPC routines that (un)program the counters to manage the counters
85 * between the kernel and user on each CPU. When the user starts using the
86 * counters on a given CPU, the kernel will unprogram the counters that it is
87 * using on that CPU just before they are programmed for the user. Then the
88 * kernel will program the counters on a given CPU for its own use when the user
91 * There is a special interaction with DTrace cpc provider (dcpc). Before dcpc
92 * enables any probe, it requests to disable and unprogram all counters used for
93 * capacity and utilizations. These counters are never re-programmed back until
94 * dcpc completes. When all DTrace cpc probes are removed, dcpc notifies CU
95 * framework and it re-programs the counters.
97 * When a CPU is going offline, its CU counters are unprogrammed and disabled,
98 * so that they would not be re-programmed again by some other activity on the
99 * CPU that is going offline.
101 * The counters are programmed during boot. However, a flag is available to
102 * disable this if necessary (see cu_flag below). A handler is provided to
103 * (un)program the counters during CPU on/offline. Basic routines are provided
104 * to initialize and tear down this module, initialize and tear down any state
105 * needed for a given CPU, and (un)program the counters for a given CPU.
106 * Lastly, a handler is provided to read the counters and attribute the
107 * utilization to the responsible CPU.
109 #include <sys/types.h>
110 #include <sys/cmn_err.h>
111 #include <sys/cpuvar.h>
113 #include <sys/systm.h>
114 #include <sys/disp.h>
116 #include <sys/sunddi.h>
117 #include <sys/thread.h>
118 #include <sys/pghw.h>
120 #include <sys/policy.h>
121 #include <sys/x_call.h>
122 #include <sys/cap_util.h>
124 #include <sys/archsystm.h>
125 #include <sys/promif.h>
128 #include <sys/xc_levels.h>
133 * Default CPU hardware performance counter flags to use for measuring capacity
136 #define CU_CPC_FLAGS_DEFAULT \
137 (CPC_COUNT_USER|CPC_COUNT_SYSTEM|CPC_OVF_NOTIFY_EMT)
140 * Possible Flags for controlling this module.
142 #define CU_FLAG_ENABLE 1 /* Enable module */
143 #define CU_FLAG_READY 2 /* Ready to setup module */
144 #define CU_FLAG_ON 4 /* Module is on */
147 * pg_cpu kstats calculate utilization rate and maximum utilization rate for
148 * some CPUs. The rate is calculated based on data from two subsequent
149 * snapshots. When the time between such two snapshots is too small, the
150 * resulting rate may have low accuracy, so we only consider snapshots which
151 * are separated by SAMPLE_INTERVAL nanoseconds from one another. We do not
152 * update the rate if the interval is smaller than that.
154 * Use one tenth of a second as the minimum interval for utilization rate
157 * NOTE: The CU_SAMPLE_INTERVAL_MIN should be higher than the scaling factor in
158 * the CU_RATE() macro below to guarantee that we never divide by zero.
160 * Rate is the number of events per second. The rate is the number of events
161 * divided by time and multiplied by the number of nanoseconds in a second. We
162 * do not want time to be too small since it will cause large errors in
165 * We do not want to multiply two large numbers (the instruction count and
166 * NANOSEC) either since it may cause integer overflow. So we divide both the
167 * numerator and the denominator by the same value.
169 * NOTE: The scaling factor below should be less than CU_SAMPLE_INTERVAL_MIN
170 * above to guarantee that time divided by this value is always non-zero.
172 #define CU_RATE(val, time) \
173 (((val) * (NANOSEC / CU_SCALE)) / ((time) / CU_SCALE))
175 #define CU_SAMPLE_INTERVAL_MIN (NANOSEC / 10)
177 #define CU_SCALE (CU_SAMPLE_INTERVAL_MIN / 10000)
180 * When the time between two kstat reads for the same CPU is less than
181 * CU_UPDATE_THRESHOLD use the old counter data and skip updating counter values
182 * for the CPU. This helps reduce cross-calls when kstat consumers read data
183 * very often or when they read PG utilization data and then CPU utilization
184 * data quickly after that.
186 #define CU_UPDATE_THRESHOLD (NANOSEC / 10)
189 * The IS_HIPIL() macro verifies that the code is executed either from a
190 * cross-call or from high-PIL interrupt
193 #define IS_HIPIL() (getpil() >= XCALL_PIL)
199 typedef void (*cu_cpu_func_t
)(uintptr_t, int *);
203 * Flags to use for programming CPU hardware performance counters to measure
204 * capacity and utilization
206 int cu_cpc_flags
= CU_CPC_FLAGS_DEFAULT
;
209 * Initial value used for programming hardware counters
211 uint64_t cu_cpc_preset_value
= 0;
214 * List of CPC event requests for capacity and utilization.
216 static kcpc_request_list_t
*cu_cpc_reqs
= NULL
;
219 * When a CPU is a member of PG with a sharing relationship that is supported
220 * by the capacity/utilization framework, a kstat is created for that CPU and
221 * sharing relationship.
223 * These kstats are updated one at a time, so we can have a single scratch
224 * space to fill the data.
226 * CPU counter kstats fields:
228 * cu_cpu_id CPU ID for this kstat
230 * cu_pg_id PG ID for this kstat
232 * cu_generation Generation value that increases whenever any CPU goes
233 * offline or online. Two kstat snapshots for the same
234 * CPU may only be compared if they have the same
237 * cu_pg_id PG ID for the relationship described by this kstat
239 * cu_cpu_util Running value of CPU utilization for the sharing
242 * cu_cpu_time_running Total time spent collecting CU data. The time may be
243 * less than wall time if CU counters were stopped for
246 * cu_cpu_time_stopped Total time the CU counters were stopped.
248 * cu_cpu_rate Utilization rate, expressed in operations per second.
250 * cu_cpu_rate_max Maximum observed value of utilization rate.
252 * cu_cpu_relationship Name of sharing relationship for the PG in this kstat
254 struct cu_cpu_kstat
{
255 kstat_named_t cu_cpu_id
;
256 kstat_named_t cu_pg_id
;
257 kstat_named_t cu_generation
;
258 kstat_named_t cu_cpu_util
;
259 kstat_named_t cu_cpu_time_running
;
260 kstat_named_t cu_cpu_time_stopped
;
261 kstat_named_t cu_cpu_rate
;
262 kstat_named_t cu_cpu_rate_max
;
263 kstat_named_t cu_cpu_relationship
;
265 { "cpu_id", KSTAT_DATA_UINT32
},
266 { "pg_id", KSTAT_DATA_INT32
},
267 { "generation", KSTAT_DATA_UINT32
},
268 { "hw_util", KSTAT_DATA_UINT64
},
269 { "hw_util_time_running", KSTAT_DATA_UINT64
},
270 { "hw_util_time_stopped", KSTAT_DATA_UINT64
},
271 { "hw_util_rate", KSTAT_DATA_UINT64
},
272 { "hw_util_rate_max", KSTAT_DATA_UINT64
},
273 { "relationship", KSTAT_DATA_STRING
},
277 * Flags for controlling this module
279 uint_t cu_flags
= CU_FLAG_ENABLE
;
282 * Error return value for cu_init() since it can't return anything to be called
283 * from mp_init_tbl[] (:-(
285 static int cu_init_error
= 0;
287 hrtime_t cu_sample_interval_min
= CU_SAMPLE_INTERVAL_MIN
;
289 hrtime_t cu_update_threshold
= CU_UPDATE_THRESHOLD
;
291 static kmutex_t pg_cpu_kstat_lock
;
295 * Forward declaration of interface routines
297 void cu_disable(void);
298 void cu_enable(void);
300 void cu_cpc_program(cpu_t
*cp
, int *err
);
301 void cu_cpc_unprogram(cpu_t
*cp
, int *err
);
302 int cu_cpu_update(struct cpu
*cp
, boolean_t move_to
);
303 void cu_pg_update(pghw_t
*pg
);
307 * Forward declaration of private routines
309 static int cu_cpc_init(cpu_t
*cp
, kcpc_request_list_t
*reqs
, int nreqs
);
310 static void cu_cpc_program_xcall(uintptr_t arg
, int *err
);
311 static int cu_cpc_req_add(char *event
, kcpc_request_list_t
*reqs
,
312 int nreqs
, cu_cntr_stats_t
*stats
, int kmem_flags
, int *nevents
);
313 static int cu_cpu_callback(cpu_setup_t what
, int id
, void *arg
);
314 static void cu_cpu_disable(cpu_t
*cp
);
315 static void cu_cpu_enable(cpu_t
*cp
);
316 static int cu_cpu_init(cpu_t
*cp
, kcpc_request_list_t
*reqs
);
317 static int cu_cpu_fini(cpu_t
*cp
);
318 static void cu_cpu_kstat_create(pghw_t
*pg
, cu_cntr_info_t
*cntr_info
);
319 static int cu_cpu_kstat_update(kstat_t
*ksp
, int rw
);
320 static int cu_cpu_run(cpu_t
*cp
, cu_cpu_func_t func
, uintptr_t arg
);
321 static int cu_cpu_update_stats(cu_cntr_stats_t
*stats
,
322 uint64_t cntr_value
);
323 static void cu_cpu_info_detach_xcall(void);
326 * Disable or enable Capacity Utilization counters on all CPUs.
333 ASSERT(MUTEX_HELD(&cpu_lock
));
337 if (!(cp
->cpu_flags
& CPU_OFFLINE
))
339 } while ((cp
= cp
->cpu_next_onln
) != cpu_active
);
348 ASSERT(MUTEX_HELD(&cpu_lock
));
352 if (!(cp
->cpu_flags
& CPU_OFFLINE
))
354 } while ((cp
= cp
->cpu_next_onln
) != cpu_active
);
359 * Setup capacity and utilization support
367 if (!(cu_flags
& CU_FLAG_ENABLE
) || (cu_flags
& CU_FLAG_ON
)) {
372 if (kcpc_init() != 0) {
378 * Can't measure hardware capacity and utilization without CPU
379 * hardware performance counters
381 if (cpc_ncounters
<= 0) {
387 * Setup CPC event request queue
389 cu_cpc_reqs
= kcpc_reqs_init(cpc_ncounters
, KM_SLEEP
);
391 mutex_enter(&cpu_lock
);
394 * Mark flags to say that module is ready to be setup
396 cu_flags
|= CU_FLAG_READY
;
401 * Allocate and setup state needed to measure capacity and
404 if (cu_cpu_init(cp
, cu_cpc_reqs
) != 0)
408 * Reset list of counter event requests so its space can be
409 * reused for a different set of requests for next CPU
411 (void) kcpc_reqs_reset(cu_cpc_reqs
);
413 cp
= cp
->cpu_next_onln
;
414 } while (cp
!= cpu_active
);
417 * Mark flags to say that module is on now and counters are ready to be
418 * programmed on all active CPUs
420 cu_flags
|= CU_FLAG_ON
;
423 * Program counters on currently active CPUs
427 if (cu_cpu_run(cp
, cu_cpc_program_xcall
,
428 (uintptr_t)B_FALSE
) != 0)
431 cp
= cp
->cpu_next_onln
;
432 } while (cp
!= cpu_active
);
435 * Register callback for CPU state changes to enable and disable
436 * CPC counters as CPUs come on and offline
438 register_cpu_setup_func(cu_cpu_callback
, NULL
);
440 mutex_exit(&cpu_lock
);
445 * Return number of counter events needed to measure capacity and utilization
446 * for specified CPU and fill in list of CPC requests with each counter event
447 * needed if list where to add CPC requests is given
449 * NOTE: Use KM_NOSLEEP for kmem_{,z}alloc() since cpu_lock is held and free
450 * everything that has been successfully allocated if any memory
454 cu_cpc_init(cpu_t
*cp
, kcpc_request_list_t
*reqs
, int nreqs
)
457 cu_cntr_info_t
**cntr_info_array
;
459 cu_cpu_info_t
*cu_cpu_info
;
462 cu_cntr_stats_t
*stats
;
464 pghw_type_t pg_hw_type
;
467 ASSERT(MUTEX_HELD(&cpu_lock
));
470 * There has to be a target CPU for this
476 * Return 0 when CPU doesn't belong to any group
478 cpu_pgs
= cp
->cpu_pg
;
479 if (cpu_pgs
== NULL
|| GROUP_SIZE(&cpu_pgs
->cmt_pgs
) < 1)
482 cmt_pgs
= &cpu_pgs
->cmt_pgs
;
483 cu_cpu_info
= cp
->cpu_cu_info
;
486 * Grab counter statistics and info
490 cntr_info_array
= NULL
;
492 if (cu_cpu_info
== NULL
|| cu_cpu_info
->cu_cntr_stats
== NULL
)
495 stats
= cu_cpu_info
->cu_cntr_stats
;
496 cntr_info_array
= cu_cpu_info
->cu_cntr_info
;
500 * See whether platform (or processor) specific code knows which CPC
501 * events to request, etc. are needed to measure hardware capacity and
502 * utilization on this machine
504 nevents
= cu_plat_cpc_init(cp
, reqs
, nreqs
);
509 * Let common code decide which CPC events to request, etc. to measure
510 * capacity and utilization since platform (or processor) specific does
513 * Walk CPU's PG lineage and do following:
515 * - Setup CPC request, counter info, and stats needed for each counter
516 * event to measure capacity and and utilization for each of CPU's PG
517 * hardware sharing relationships
519 * - Create PG CPU kstats to export capacity and utilization for each PG
522 group_iter_init(&iter
);
523 while ((pg_cmt
= group_iterate(cmt_pgs
, &iter
)) != NULL
) {
524 cu_cntr_info_t
*cntr_info
;
528 pg_hw
= (pghw_t
*)pg_cmt
;
529 pg_hw_type
= pg_hw
->pghw_hw
;
530 nevents_save
= nevents
;
533 switch (pg_hw_type
) {
535 if (cu_cpc_req_add("PAPI_tot_ins", reqs
, nreqs
, stats
,
536 KM_NOSLEEP
, &nevents
) != 0)
542 if (cu_cpc_req_add("PAPI_fp_ins", reqs
, nreqs
, stats
,
543 KM_NOSLEEP
, &nevents
) != 0)
550 * Don't measure capacity and utilization for this kind
551 * of PG hardware relationship so skip to next PG in
557 cntr_info
= cntr_info_array
[pg_hw_type
];
560 * Nothing to measure for this hardware sharing relationship
562 if (nevents
- nevents_save
== 0) {
563 if (cntr_info
!= NULL
) {
564 kmem_free(cntr_info
, sizeof (cu_cntr_info_t
));
565 cntr_info_array
[pg_hw_type
] = NULL
;
571 * Fill in counter info for this PG hardware relationship
573 if (cntr_info
== NULL
) {
574 cntr_info
= kmem_zalloc(sizeof (cu_cntr_info_t
),
576 if (cntr_info
== NULL
)
578 cntr_info_array
[pg_hw_type
] = cntr_info
;
580 cntr_info
->ci_cpu
= cp
;
581 cntr_info
->ci_pg
= pg_hw
;
582 cntr_info
->ci_stats
= &stats
[nevents_save
];
583 cntr_info
->ci_nstats
= nstats
;
586 * Create PG CPU kstats for this hardware relationship
588 cu_cpu_kstat_create(pg_hw
, cntr_info
);
596 * Program counters for capacity and utilization on given CPU
598 * If any of the following conditions is true, the counters are not programmed:
600 * - CU framework is disabled
601 * - The cpu_cu_info field of the cpu structure is NULL
603 * - Counters are programmed already
604 * - Counters are disabled (by calls to cu_cpu_disable())
607 cu_cpc_program(cpu_t
*cp
, int *err
)
609 cu_cpc_ctx_t
*cpu_ctx
;
611 cu_cpu_info_t
*cu_cpu_info
;
615 * Should be running on given CPU. We disable preemption to keep CPU
616 * from disappearing and make sure flags and CPC context don't change
623 * Module not ready to program counters
625 if (!(cu_flags
& CU_FLAG_ON
)) {
637 cu_cpu_info
= cp
->cpu_cu_info
;
638 if (cu_cpu_info
== NULL
) {
645 * If DTrace CPC is active or counters turned on already or are
646 * disabled, just return.
648 if (dtrace_cpc_in_use
|| (cu_cpu_info
->cu_flag
& CU_CPU_CNTRS_ON
) ||
649 cu_cpu_info
->cu_disabled
) {
655 if ((CPU
->cpu_cpc_ctx
!= NULL
) &&
656 !(CPU
->cpu_cpc_ctx
->kc_flags
& KCPC_CTX_INVALID_STOPPED
)) {
663 * Get CPU's CPC context needed for capacity and utilization
665 cpu_ctx
= &cu_cpu_info
->cu_cpc_ctx
;
666 ASSERT(cpu_ctx
!= NULL
);
667 ASSERT(cpu_ctx
->nctx
>= 0);
669 ASSERT(cpu_ctx
->ctx_ptr_array
== NULL
|| cpu_ctx
->ctx_ptr_array_sz
> 0);
670 ASSERT(cpu_ctx
->nctx
<= cpu_ctx
->ctx_ptr_array_sz
);
671 if (cpu_ctx
->nctx
<= 0 || cpu_ctx
->ctx_ptr_array
== NULL
||
672 cpu_ctx
->ctx_ptr_array_sz
<= 0) {
679 * Increment index in CPU's CPC context info to point at next context
682 * NOTE: Do this now instead of after programming counters to ensure
683 * that index will always point at *current* context so we will
684 * always be able to unprogram *current* context if necessary
686 cpu_ctx
->cur_index
= (cpu_ctx
->cur_index
+ 1) % cpu_ctx
->nctx
;
688 ctx
= cpu_ctx
->ctx_ptr_array
[cpu_ctx
->cur_index
];
691 * Clear KCPC_CTX_INVALID and KCPC_CTX_INVALID_STOPPED from CPU's CPC
692 * context before programming counters
694 * Context is marked with KCPC_CTX_INVALID_STOPPED when context is
695 * unprogrammed and may be marked with KCPC_CTX_INVALID when
696 * kcpc_invalidate_all() is called by cpustat(8) and dtrace CPC to
697 * invalidate all CPC contexts before they take over all the counters.
699 * This isn't necessary since these flags are only used for thread bound
700 * CPC contexts not CPU bound CPC contexts like ones used for capacity
703 * There is no need to protect the flag update since no one is using
706 ctx
->kc_flags
&= ~(KCPC_CTX_INVALID
| KCPC_CTX_INVALID_STOPPED
);
709 * Program counters on this CPU
711 kcpc_program(ctx
, B_FALSE
, B_FALSE
);
713 cp
->cpu_cpc_ctx
= ctx
;
716 * Set state in CPU structure to say that CPU's counters are programmed
717 * for capacity and utilization now and that they are transitioning from
718 * off to on state. This will cause cu_cpu_update to update stop times
719 * for all programmed counters.
721 cu_cpu_info
->cu_flag
|= CU_CPU_CNTRS_ON
| CU_CPU_CNTRS_OFF_ON
;
724 * Update counter statistics
726 (void) cu_cpu_update(cp
, B_FALSE
);
728 cu_cpu_info
->cu_flag
&= ~CU_CPU_CNTRS_OFF_ON
;
736 * Cross call wrapper routine for cu_cpc_program()
738 * Checks to make sure that counters on CPU aren't being used by someone else
739 * before calling cu_cpc_program() since cu_cpc_program() needs to assert that
740 * nobody else is using the counters to catch and prevent any broken code.
741 * Also, this check needs to happen on the target CPU since the CPU's CPC
742 * context can only be changed while running on the CPU.
744 * If the first argument is TRUE, cu_cpc_program_xcall also checks that there is
745 * no valid thread bound cpc context. This is important to check to prevent
746 * re-programming thread counters with CU counters when CPU is coming on-line.
749 cu_cpc_program_xcall(uintptr_t arg
, int *err
)
751 boolean_t avoid_thread_context
= (boolean_t
)arg
;
755 if (CPU
->cpu_cpc_ctx
!= NULL
&&
756 !(CPU
->cpu_cpc_ctx
->kc_flags
& KCPC_CTX_INVALID_STOPPED
)) {
762 if (avoid_thread_context
&& (curthread
->t_cpc_ctx
!= NULL
) &&
763 !(curthread
->t_cpc_ctx
->kc_flags
& KCPC_CTX_INVALID_STOPPED
)) {
769 cu_cpc_program(CPU
, err
);
775 * Unprogram counters for capacity and utilization on given CPU
776 * This function should be always executed on the target CPU at high PIL
779 cu_cpc_unprogram(cpu_t
*cp
, int *err
)
781 cu_cpc_ctx_t
*cpu_ctx
;
783 cu_cpu_info_t
*cu_cpu_info
;
787 * Should be running on given CPU with preemption disabled to keep CPU
788 * from disappearing and make sure flags and CPC context don't change
797 if (!(cu_flags
& CU_FLAG_ON
)) {
803 cu_cpu_info
= cp
->cpu_cu_info
;
804 if (cu_cpu_info
== NULL
) {
811 * Counters turned off already
813 if (!(cu_cpu_info
->cu_flag
& CU_CPU_CNTRS_ON
)) {
820 * Update counter statistics
822 (void) cu_cpu_update(cp
, B_FALSE
);
825 * Get CPU's CPC context needed for capacity and utilization
827 cpu_ctx
= &cu_cpu_info
->cu_cpc_ctx
;
828 if (cpu_ctx
->nctx
<= 0 || cpu_ctx
->ctx_ptr_array
== NULL
||
829 cpu_ctx
->ctx_ptr_array_sz
<= 0) {
834 ctx
= cpu_ctx
->ctx_ptr_array
[cpu_ctx
->cur_index
];
837 * CPU's CPC context should be current capacity and utilization CPC
840 ASSERT(cp
->cpu_cpc_ctx
== ctx
);
841 if (cp
->cpu_cpc_ctx
!= ctx
) {
848 * Unprogram counters on CPU.
850 kcpc_unprogram(ctx
, B_FALSE
);
852 ASSERT(ctx
->kc_flags
& KCPC_CTX_INVALID_STOPPED
);
855 * Unset state in CPU structure saying that CPU's counters are
858 cp
->cpu_cpc_ctx
= NULL
;
859 cu_cpu_info
->cu_flag
&= ~CU_CPU_CNTRS_ON
;
867 * Add given counter event to list of CPC requests
870 cu_cpc_req_add(char *event
, kcpc_request_list_t
*reqs
, int nreqs
,
871 cu_cntr_stats_t
*stats
, int kmem_flags
, int *nevents
)
878 * Return error when no counter event specified, counter event not
879 * supported by CPC's PCBE, or number of events not given
881 if (event
== NULL
|| kcpc_event_supported(event
) == B_FALSE
||
888 * Only count number of counter events needed if list
889 * where to add CPC requests not given
898 * Return error when stats not given or not enough room on list of CPC
899 * requests for more counter events
901 if (stats
== NULL
|| (nreqs
<= 0 && n
>= nreqs
))
905 * Use flags in cu_cpc_flags to program counters and enable overflow
906 * interrupts/traps (unless PCBE can't handle overflow interrupts) so
907 * PCBE can catch counters before they wrap to hopefully give us an
908 * accurate (64-bit) virtualized counter
910 flags
= cu_cpc_flags
;
911 if ((kcpc_pcbe_capabilities() & CPC_CAP_OVERFLOW_INTERRUPT
) == 0)
912 flags
&= ~CPC_OVF_NOTIFY_EMT
;
915 * Add CPC request to list
917 retval
= kcpc_reqs_add(reqs
, event
, cu_cpc_preset_value
,
918 flags
, 0, NULL
, &stats
[n
], kmem_flags
);
929 cu_cpu_info_detach_xcall(void)
933 CPU
->cpu_cu_info
= NULL
;
938 * Enable or disable collection of capacity/utilization data for a current CPU.
939 * Counters are enabled if 'on' argument is True and disabled if it is False.
940 * This function should be always executed at high PIL
943 cu_cpc_trigger(uintptr_t arg1
, uintptr_t arg2
)
945 cpu_t
*cp
= (cpu_t
*)arg1
;
946 boolean_t on
= (boolean_t
)arg2
;
948 cu_cpu_info_t
*cu_cpu_info
;
954 if (!(cu_flags
& CU_FLAG_ON
)) {
959 cu_cpu_info
= cp
->cpu_cu_info
;
960 if (cu_cpu_info
== NULL
) {
965 ASSERT(!cu_cpu_info
->cu_disabled
||
966 !(cu_cpu_info
->cu_flag
& CU_CPU_CNTRS_ON
));
970 * Decrement the cu_disabled counter.
971 * Once it drops to zero, call cu_cpc_program.
973 if (cu_cpu_info
->cu_disabled
> 0)
974 cu_cpu_info
->cu_disabled
--;
975 if (cu_cpu_info
->cu_disabled
== 0)
976 cu_cpc_program(CPU
, &error
);
977 } else if (cu_cpu_info
->cu_disabled
++ == 0) {
979 * This is the first attempt to disable CU, so turn it off
981 cu_cpc_unprogram(cp
, &error
);
982 ASSERT(!(cu_cpu_info
->cu_flag
& CU_CPU_CNTRS_ON
));
990 * Callback for changes in CPU states
991 * Used to enable or disable hardware performance counters on CPUs that are
994 * NOTE: cpc should be programmed/unprogrammed while running on the target CPU.
995 * We have to use thread_affinity_set to hop to the right CPU because these
996 * routines expect cpu_lock held, so we can't cross-call other CPUs while
1000 cu_cpu_callback(cpu_setup_t what
, int id
, void *arg
)
1005 ASSERT(MUTEX_HELD(&cpu_lock
));
1007 if (!(cu_flags
& CU_FLAG_ON
))
1017 * Setup counters on CPU being turned on
1019 retval
= cu_cpu_init(cp
, cu_cpc_reqs
);
1022 * Reset list of counter event requests so its space can be
1023 * reused for a different set of requests for next CPU
1025 (void) kcpc_reqs_reset(cu_cpc_reqs
);
1029 * Setup counters on CPU being turned on.
1031 retval
= cu_cpu_run(cp
, cu_cpc_program_xcall
,
1036 * Disable counters on CPU being turned off. Counters will not
1037 * be re-enabled on this CPU until it comes back online.
1040 ASSERT(!CU_CPC_ON(cp
));
1041 retval
= cu_cpu_fini(cp
);
1051 * Disable or enable Capacity Utilization counters on a given CPU. This function
1052 * can be called from any CPU to disable counters on the given CPU.
1055 cu_cpu_disable(cpu_t
*cp
)
1057 cpu_call(cp
, cu_cpc_trigger
, (uintptr_t)cp
, (uintptr_t)B_FALSE
);
1062 cu_cpu_enable(cpu_t
*cp
)
1064 cpu_call(cp
, cu_cpc_trigger
, (uintptr_t)cp
, (uintptr_t)B_TRUE
);
1069 * Setup capacity and utilization support for given CPU
1071 * NOTE: Use KM_NOSLEEP for kmem_{,z}alloc() since cpu_lock is held and free
1072 * everything that has been successfully allocated including cpu_cu_info
1073 * if any memory allocation fails
1076 cu_cpu_init(cpu_t
*cp
, kcpc_request_list_t
*reqs
)
1078 kcpc_ctx_t
**ctx_ptr_array
;
1079 size_t ctx_ptr_array_sz
;
1080 cu_cpc_ctx_t
*cpu_ctx
;
1081 cu_cpu_info_t
*cu_cpu_info
;
1085 * cpu_lock should be held and protect against CPU going away and races
1086 * with cu_{init,fini,cpu_fini}()
1088 ASSERT(MUTEX_HELD(&cpu_lock
));
1091 * Return if not ready to setup counters yet
1093 if (!(cu_flags
& CU_FLAG_READY
))
1096 if (cp
->cpu_cu_info
== NULL
) {
1097 cp
->cpu_cu_info
= kmem_zalloc(sizeof (cu_cpu_info_t
),
1099 if (cp
->cpu_cu_info
== NULL
)
1104 * Get capacity and utilization CPC context for CPU and check to see
1105 * whether it has been setup already
1107 cu_cpu_info
= cp
->cpu_cu_info
;
1108 cu_cpu_info
->cu_cpu
= cp
;
1109 cu_cpu_info
->cu_disabled
= dtrace_cpc_in_use
? 1 : 0;
1111 cpu_ctx
= &cu_cpu_info
->cu_cpc_ctx
;
1112 if (cpu_ctx
->nctx
> 0 && cpu_ctx
->ctx_ptr_array
!= NULL
&&
1113 cpu_ctx
->ctx_ptr_array_sz
> 0) {
1118 * Should have no contexts since it hasn't been setup already
1120 ASSERT(cpu_ctx
->nctx
== 0 && cpu_ctx
->ctx_ptr_array
== NULL
&&
1121 cpu_ctx
->ctx_ptr_array_sz
== 0);
1124 * Determine how many CPC events needed to measure capacity and
1125 * utilization for this CPU, allocate space for counter statistics for
1126 * each event, and fill in list of CPC event requests with corresponding
1127 * counter stats for each request to make attributing counter data
1130 n
= cu_cpc_init(cp
, NULL
, 0);
1132 (void) cu_cpu_fini(cp
);
1136 cu_cpu_info
->cu_cntr_stats
= kmem_zalloc(n
* sizeof (cu_cntr_stats_t
),
1138 if (cu_cpu_info
->cu_cntr_stats
== NULL
) {
1139 (void) cu_cpu_fini(cp
);
1143 cu_cpu_info
->cu_ncntr_stats
= n
;
1145 n
= cu_cpc_init(cp
, reqs
, n
);
1147 (void) cu_cpu_fini(cp
);
1152 * Create CPC context with given requests
1154 ctx_ptr_array
= NULL
;
1155 ctx_ptr_array_sz
= 0;
1156 n
= kcpc_cpu_ctx_create(cp
, reqs
, KM_NOSLEEP
, &ctx_ptr_array
,
1159 (void) cu_cpu_fini(cp
);
1164 * Should have contexts
1166 ASSERT(n
> 0 && ctx_ptr_array
!= NULL
&& ctx_ptr_array_sz
> 0);
1167 if (ctx_ptr_array
== NULL
|| ctx_ptr_array_sz
<= 0) {
1168 (void) cu_cpu_fini(cp
);
1173 * Fill in CPC context info for CPU needed for capacity and utilization
1175 cpu_ctx
->cur_index
= 0;
1177 cpu_ctx
->ctx_ptr_array
= ctx_ptr_array
;
1178 cpu_ctx
->ctx_ptr_array_sz
= ctx_ptr_array_sz
;
1183 * Tear down capacity and utilization support for given CPU
1186 cu_cpu_fini(cpu_t
*cp
)
1189 cu_cpc_ctx_t
*cpu_ctx
;
1190 cu_cpu_info_t
*cu_cpu_info
;
1192 pghw_type_t pg_hw_type
;
1195 * cpu_lock should be held and protect against CPU going away and races
1196 * with cu_{init,fini,cpu_init}()
1198 ASSERT(MUTEX_HELD(&cpu_lock
));
1201 * Have to at least be ready to setup counters to have allocated
1202 * anything that needs to be deallocated now
1204 if (!(cu_flags
& CU_FLAG_READY
))
1208 * Nothing to do if CPU's capacity and utilization info doesn't exist
1210 cu_cpu_info
= cp
->cpu_cu_info
;
1211 if (cu_cpu_info
== NULL
)
1215 * Tear down any existing kstats and counter info for each hardware
1216 * sharing relationship
1218 for (pg_hw_type
= PGHW_START
; pg_hw_type
< PGHW_NUM_COMPONENTS
;
1220 cu_cntr_info_t
*cntr_info
;
1222 cntr_info
= cu_cpu_info
->cu_cntr_info
[pg_hw_type
];
1223 if (cntr_info
== NULL
)
1226 if (cntr_info
->ci_kstat
!= NULL
) {
1227 kstat_delete(cntr_info
->ci_kstat
);
1228 cntr_info
->ci_kstat
= NULL
;
1230 kmem_free(cntr_info
, sizeof (cu_cntr_info_t
));
1234 * Free counter statistics for CPU
1236 ASSERT(cu_cpu_info
->cu_cntr_stats
== NULL
||
1237 cu_cpu_info
->cu_ncntr_stats
> 0);
1238 if (cu_cpu_info
->cu_cntr_stats
!= NULL
&&
1239 cu_cpu_info
->cu_ncntr_stats
> 0) {
1240 kmem_free(cu_cpu_info
->cu_cntr_stats
,
1241 cu_cpu_info
->cu_ncntr_stats
* sizeof (cu_cntr_stats_t
));
1242 cu_cpu_info
->cu_cntr_stats
= NULL
;
1243 cu_cpu_info
->cu_ncntr_stats
= 0;
1247 * Get capacity and utilization CPC contexts for given CPU and check to
1248 * see whether they have been freed already
1250 cpu_ctx
= &cu_cpu_info
->cu_cpc_ctx
;
1251 if (cpu_ctx
!= NULL
&& cpu_ctx
->ctx_ptr_array
!= NULL
&&
1252 cpu_ctx
->ctx_ptr_array_sz
> 0) {
1254 * Free CPC contexts for given CPU
1256 for (i
= 0; i
< cpu_ctx
->nctx
; i
++) {
1257 ctx
= cpu_ctx
->ctx_ptr_array
[i
];
1264 * Free CPC context pointer array
1266 kmem_free(cpu_ctx
->ctx_ptr_array
, cpu_ctx
->ctx_ptr_array_sz
);
1269 * Zero CPC info for CPU
1271 bzero(cpu_ctx
, sizeof (cu_cpc_ctx_t
));
1275 * Set cp->cpu_cu_info pointer to NULL. Go through cross-call to ensure
1276 * that no one is going to access the cpu_cu_info whicch we are going to
1279 if (cpu_is_online(cp
))
1280 cpu_call(cp
, (cpu_call_func_t
)cu_cpu_info_detach_xcall
, 0, 0);
1282 cp
->cpu_cu_info
= NULL
;
1285 * Free CPU's capacity and utilization info
1287 kmem_free(cu_cpu_info
, sizeof (cu_cpu_info_t
));
1293 * Create capacity & utilization kstats for given PG CPU hardware sharing
1297 cu_cpu_kstat_create(pghw_t
*pg
, cu_cntr_info_t
*cntr_info
)
1300 char *sharing
= pghw_type_string(pg
->pghw_hw
);
1301 char name
[KSTAT_STRLEN
+ 1];
1304 * Just return when no counter info or CPU
1306 if (cntr_info
== NULL
|| cntr_info
->ci_cpu
== NULL
)
1310 * Canonify PG name to conform to kstat name rules
1312 (void) strncpy(name
, pghw_type_string(pg
->pghw_hw
), KSTAT_STRLEN
+ 1);
1313 strident_canon(name
, TASKQ_NAMELEN
+ 1);
1315 if ((ks
= kstat_create_zone("pg_hw_perf_cpu",
1316 cntr_info
->ci_cpu
->cpu_id
,
1317 name
, "processor_group", KSTAT_TYPE_NAMED
,
1318 sizeof (cu_cpu_kstat
) / sizeof (kstat_named_t
),
1319 KSTAT_FLAG_VIRTUAL
, GLOBAL_ZONEID
)) == NULL
)
1322 ks
->ks_lock
= &pg_cpu_kstat_lock
;
1323 ks
->ks_data
= &cu_cpu_kstat
;
1324 ks
->ks_update
= cu_cpu_kstat_update
;
1325 ks
->ks_data_size
+= strlen(sharing
) + 1;
1327 ks
->ks_private
= cntr_info
;
1328 cntr_info
->ci_kstat
= ks
;
1329 kstat_install(cntr_info
->ci_kstat
);
1334 * Propagate values from CPU capacity & utilization stats to kstats
1337 cu_cpu_kstat_update(kstat_t
*ksp
, int rw
)
1340 cu_cntr_info_t
*cntr_info
= ksp
->ks_private
;
1341 struct cu_cpu_kstat
*kstat
= &cu_cpu_kstat
;
1343 cu_cntr_stats_t
*stats
;
1345 if (rw
== KSTAT_WRITE
)
1348 cp
= cntr_info
->ci_cpu
;
1349 pg
= cntr_info
->ci_pg
;
1350 kstat
->cu_cpu_id
.value
.ui32
= cp
->cpu_id
;
1351 kstat
->cu_pg_id
.value
.i32
= ((pg_t
*)pg
)->pg_id
;
1354 * The caller should have priv_cpc_cpu privilege to get utilization
1355 * data. Callers who do not have the privilege will see zeroes as the
1358 if (secpolicy_cpc_cpu(crgetcred()) != 0) {
1359 kstat
->cu_generation
.value
.ui32
= cp
->cpu_generation
;
1360 kstat_named_setstr(&kstat
->cu_cpu_relationship
,
1361 pghw_type_string(pg
->pghw_hw
));
1363 kstat
->cu_cpu_util
.value
.ui64
= 0;
1364 kstat
->cu_cpu_rate
.value
.ui64
= 0;
1365 kstat
->cu_cpu_rate_max
.value
.ui64
= 0;
1366 kstat
->cu_cpu_time_running
.value
.ui64
= 0;
1367 kstat
->cu_cpu_time_stopped
.value
.ui64
= 0;
1375 * Update capacity and utilization statistics needed for CPU's PG (CPU)
1379 (void) cu_cpu_update(cp
, B_TRUE
);
1381 stats
= cntr_info
->ci_stats
;
1382 kstat
->cu_generation
.value
.ui32
= cp
->cpu_generation
;
1383 kstat_named_setstr(&kstat
->cu_cpu_relationship
,
1384 pghw_type_string(pg
->pghw_hw
));
1386 kstat
->cu_cpu_util
.value
.ui64
= stats
->cs_value_total
;
1387 kstat
->cu_cpu_rate
.value
.ui64
= stats
->cs_rate
;
1388 kstat
->cu_cpu_rate_max
.value
.ui64
= stats
->cs_rate_max
;
1389 kstat
->cu_cpu_time_running
.value
.ui64
= stats
->cs_time_running
;
1390 kstat
->cu_cpu_time_stopped
.value
.ui64
= stats
->cs_time_stopped
;
1393 * Counters are stopped now, so the cs_time_stopped was last
1394 * updated at cs_time_start time. Add the time passed since then
1395 * to the stopped time.
1397 if (!(cp
->cpu_cu_info
->cu_flag
& CU_CPU_CNTRS_ON
))
1398 kstat
->cu_cpu_time_stopped
.value
.ui64
+=
1399 gethrtime() - stats
->cs_time_start
;
1407 * Run specified function with specified argument on a given CPU and return
1408 * whatever the function returns
1411 cu_cpu_run(cpu_t
*cp
, cu_cpu_func_t func
, uintptr_t arg
)
1416 * cpu_call() will call func on the CPU specified with given argument
1417 * and return func's return value in last argument
1419 cpu_call(cp
, (cpu_call_func_t
)func
, arg
, (uintptr_t)&error
);
1425 * Update counter statistics on a given CPU.
1427 * If move_to argument is True, execute the function on the CPU specified
1428 * Otherwise, assume that it is already runninng on the right CPU
1430 * If move_to is specified, the caller should hold cpu_lock or have preemption
1431 * disabled. Otherwise it is up to the caller to guarantee that things do not
1432 * change in the process.
1435 cu_cpu_update(struct cpu
*cp
, boolean_t move_to
)
1438 cu_cpu_info_t
*cu_cpu_info
= cp
->cpu_cu_info
;
1441 ASSERT(!move_to
|| MUTEX_HELD(&cpu_lock
) || curthread
->t_preempt
> 0);
1444 * Nothing to do if counters are not programmed
1446 if (!(cu_flags
& CU_FLAG_ON
) ||
1447 (cu_cpu_info
== NULL
) ||
1448 !(cu_cpu_info
->cu_flag
& CU_CPU_CNTRS_ON
))
1452 * Don't update CPU statistics if it was updated recently
1453 * and provide old results instead
1455 time_snap
= gethrtime();
1456 if ((time_snap
- cu_cpu_info
->cu_sample_time
) < cu_update_threshold
) {
1457 DTRACE_PROBE1(cu__drop__sample
, cpu_t
*, cp
);
1461 cu_cpu_info
->cu_sample_time
= time_snap
;
1464 * CPC counter should be read on the CPU that is running the counter. We
1465 * either have to move ourselves to the target CPU or insure that we
1466 * already run there.
1468 * We use cross-call to the target CPU to execute kcpc_read() and
1469 * cu_cpu_update_stats() there.
1473 (void) cu_cpu_run(cp
, (cu_cpu_func_t
)kcpc_read
,
1474 (uintptr_t)cu_cpu_update_stats
);
1476 retval
= kcpc_read((kcpc_update_func_t
)cu_cpu_update_stats
);
1478 * Offset negative return value by -10 so we can distinguish it
1479 * from error return values of this routine vs kcpc_read()
1490 * Update CPU counter statistics for current CPU.
1491 * This function may be called from a cross-call
1494 cu_cpu_update_stats(cu_cntr_stats_t
*stats
, uint64_t cntr_value
)
1496 cu_cpu_info_t
*cu_cpu_info
= CPU
->cpu_cu_info
;
1499 hrtime_t time_delta
;
1506 * Nothing to do if counters are not programmed. This should not happen,
1507 * but we check just in case.
1509 ASSERT(cu_flags
& CU_FLAG_ON
);
1510 ASSERT(cu_cpu_info
!= NULL
);
1511 if (!(cu_flags
& CU_FLAG_ON
) ||
1512 (cu_cpu_info
== NULL
))
1515 flags
= cu_cpu_info
->cu_flag
;
1516 ASSERT(flags
& CU_CPU_CNTRS_ON
);
1517 if (!(flags
& CU_CPU_CNTRS_ON
))
1521 * Take snapshot of high resolution timer
1523 time_snap
= gethrtime();
1526 * CU counters have just been programmed. We cannot assume that the new
1527 * cntr_value continues from where we left off, so use the cntr_value as
1528 * the new initial value.
1530 if (flags
& CU_CPU_CNTRS_OFF_ON
)
1531 stats
->cs_value_start
= cntr_value
;
1534 * Calculate delta in counter values between start of sampling period
1537 delta
= cntr_value
- stats
->cs_value_start
;
1540 * Calculate time between start of sampling period and now
1542 time_delta
= stats
->cs_time_start
?
1543 time_snap
- stats
->cs_time_start
:
1545 stats
->cs_time_start
= time_snap
;
1546 stats
->cs_value_start
= cntr_value
;
1548 if (time_delta
> 0) { /* wrap shouldn't happen */
1550 * Update either running or stopped time based on the transition
1553 if (flags
& CU_CPU_CNTRS_OFF_ON
)
1554 stats
->cs_time_stopped
+= time_delta
;
1556 stats
->cs_time_running
+= time_delta
;
1560 * Update rest of counter statistics if counter value didn't wrap
1564 * Update utilization rate if the interval between samples is
1567 ASSERT(cu_sample_interval_min
> CU_SCALE
);
1568 if (time_delta
> cu_sample_interval_min
)
1569 stats
->cs_rate
= CU_RATE(delta
, time_delta
);
1570 if (stats
->cs_rate_max
< stats
->cs_rate
)
1571 stats
->cs_rate_max
= stats
->cs_rate
;
1573 stats
->cs_value_last
= delta
;
1574 stats
->cs_value_total
+= delta
;
1581 * Update CMT PG utilization data.
1583 * This routine computes the running total utilization and times for the
1584 * specified PG by adding up the total utilization and counter running and
1585 * stopped times of all CPUs in the PG and calculates the utilization rate and
1586 * maximum rate for all CPUs in the PG.
1589 cu_pg_update(pghw_t
*pg
)
1591 pg_cpu_itr_t cpu_iter
;
1592 pghw_type_t pg_hwtype
;
1594 pghw_util_t
*hw_util
= &pg
->pghw_stats
;
1595 uint64_t old_utilization
= hw_util
->pghw_util
;
1597 hrtime_t time_delta
;
1598 uint64_t utilization_delta
;
1600 ASSERT(MUTEX_HELD(&cpu_lock
));
1604 pg_hwtype
= pg
->pghw_hw
;
1607 * Initialize running total utilization and times for PG to 0
1609 hw_util
->pghw_util
= 0;
1610 hw_util
->pghw_time_running
= 0;
1611 hw_util
->pghw_time_stopped
= 0;
1614 * Iterate over all CPUs in the PG and aggregate utilization, running
1615 * time and stopped time.
1617 PG_CPU_ITR_INIT(pg
, cpu_iter
);
1618 while ((cpu
= pg_cpu_next(&cpu_iter
)) != NULL
) {
1619 cu_cpu_info_t
*cu_cpu_info
= cpu
->cpu_cu_info
;
1620 cu_cntr_info_t
*cntr_info
;
1621 cu_cntr_stats_t
*stats
;
1623 if (cu_cpu_info
== NULL
)
1627 * Update utilization data for the CPU and then
1628 * aggregate per CPU running totals for PG
1630 (void) cu_cpu_update(cpu
, B_TRUE
);
1631 cntr_info
= cu_cpu_info
->cu_cntr_info
[pg_hwtype
];
1633 if (cntr_info
== NULL
|| (stats
= cntr_info
->ci_stats
) == NULL
)
1636 hw_util
->pghw_util
+= stats
->cs_value_total
;
1637 hw_util
->pghw_time_running
+= stats
->cs_time_running
;
1638 hw_util
->pghw_time_stopped
+= stats
->cs_time_stopped
;
1641 * If counters are stopped now, the pg_time_stopped was last
1642 * updated at cs_time_start time. Add the time passed since then
1643 * to the stopped time.
1645 if (!(cu_cpu_info
->cu_flag
& CU_CPU_CNTRS_ON
))
1646 hw_util
->pghw_time_stopped
+=
1647 now
- stats
->cs_time_start
;
1651 * Compute per PG instruction rate and maximum rate
1653 time_delta
= now
- hw_util
->pghw_time_stamp
;
1654 hw_util
->pghw_time_stamp
= now
;
1656 if (old_utilization
== 0)
1660 * Calculate change in utilization over sampling period and set this to
1661 * 0 if the delta would be 0 or negative which may happen if any CPUs go
1662 * offline during the sampling period
1664 if (hw_util
->pghw_util
> old_utilization
)
1665 utilization_delta
= hw_util
->pghw_util
- old_utilization
;
1667 utilization_delta
= 0;
1670 * Update utilization rate if the interval between samples is
1673 ASSERT(cu_sample_interval_min
> CU_SCALE
);
1674 if (time_delta
> CU_SAMPLE_INTERVAL_MIN
)
1675 hw_util
->pghw_rate
= CU_RATE(utilization_delta
, time_delta
);
1678 * Update the maximum observed rate
1680 if (hw_util
->pghw_rate_max
< hw_util
->pghw_rate
)
1681 hw_util
->pghw_rate_max
= hw_util
->pghw_rate
;