277 lines
8.6 KiB
C
277 lines
8.6 KiB
C
// Copyright (c) 2021 Apple Inc. All rights reserved.
|
|
//
|
|
// @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
|
//
|
|
// This file contains Original Code and/or Modifications of Original Code
|
|
// as defined in and that are subject to the Apple Public Source License
|
|
// Version 2.0 (the 'License'). You may not use this file except in
|
|
// compliance with the License. The rights granted to you under the License
|
|
// may not be used to create, or enable the creation or redistribution of,
|
|
// unlawful or unlicensed copies of an Apple operating system, or to
|
|
// circumvent, violate, or enable the circumvention or violation of, any
|
|
// terms of an Apple operating system software license agreement.
|
|
//
|
|
// Please obtain a copy of the License at
|
|
// http://www.opensource.apple.com/apsl/ and read it before using this file.
|
|
//
|
|
// The Original Code and all software distributed under the License are
|
|
// distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
// EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
|
// INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
|
// Please see the License for the specific language governing rights and
|
|
// limitations under the License.
|
|
//
|
|
// @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
|
|
|
#include <kern/recount.h>
|
|
#include <machine/machine_routines.h>
|
|
#include <machine/smp.h>
|
|
#include <sys/proc_info.h>
|
|
#include <sys/resource_private.h>
|
|
#include <sys/sysproto.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/types.h>
|
|
|
|
// Recount's BSD-specific implementation for syscalls.
|
|
|
|
#if CONFIG_PERVASIVE_CPI
|
|
|
|
static struct thsc_cpi
|
|
_usage_to_cpi(struct recount_usage *usage)
|
|
{
|
|
return (struct thsc_cpi){
|
|
.tcpi_instructions = recount_usage_instructions(usage),
|
|
.tcpi_cycles = recount_usage_cycles(usage),
|
|
};
|
|
}
|
|
|
|
static struct thsc_time_cpi
|
|
_usage_to_time_cpi(struct recount_usage *usage)
|
|
{
|
|
return (struct thsc_time_cpi){
|
|
.ttci_instructions = recount_usage_instructions(usage),
|
|
.ttci_cycles = recount_usage_cycles(usage),
|
|
.ttci_system_time_mach = recount_usage_system_time_mach(usage),
|
|
.ttci_user_time_mach = usage->ru_metrics[RCT_LVL_USER].rm_time_mach,
|
|
};
|
|
}
|
|
|
|
static struct thsc_time_energy_cpi
|
|
_usage_to_time_energy_cpi(struct recount_usage *usage)
|
|
{
|
|
return (struct thsc_time_energy_cpi){
|
|
.ttec_instructions = recount_usage_instructions(usage),
|
|
.ttec_cycles = recount_usage_cycles(usage),
|
|
.ttec_system_time_mach = recount_usage_system_time_mach(usage),
|
|
.ttec_user_time_mach = usage->ru_metrics[RCT_LVL_USER].rm_time_mach,
|
|
#if CONFIG_PERVASIVE_ENERGY
|
|
.ttec_energy_nj = usage->ru_energy_nj,
|
|
#endif // CONFIG_PERVASIVE_ENERGY
|
|
};
|
|
}
|
|
|
|
static recount_cpu_kind_t
|
|
_perflevel_index_to_cpu_kind(unsigned int perflevel)
|
|
{
|
|
#if __AMP__
|
|
extern cluster_type_t cpu_type_for_perflevel(int perflevel);
|
|
cluster_type_t cluster = cpu_type_for_perflevel(perflevel);
|
|
#else // __AMP__
|
|
cluster_type_t cluster = CLUSTER_TYPE_SMP;
|
|
#endif // !__AMP__
|
|
|
|
switch (cluster) {
|
|
case CLUSTER_TYPE_SMP:
|
|
// Default to first index for SMP.
|
|
return (recount_cpu_kind_t)0;
|
|
#if __AMP__
|
|
case CLUSTER_TYPE_E:
|
|
return RCT_CPU_EFFICIENCY;
|
|
case CLUSTER_TYPE_P:
|
|
return RCT_CPU_PERFORMANCE;
|
|
#endif // __AMP__
|
|
default:
|
|
panic("recount: unexpected CPU type %d for perflevel %d", cluster,
|
|
perflevel);
|
|
}
|
|
}
|
|
|
|
static int
|
|
_selfcounts(thread_selfcounts_kind_t kind, user_addr_t buf, size_t size)
|
|
{
|
|
struct recount_usage usage = { 0 };
|
|
boolean_t interrupt_state = ml_set_interrupts_enabled(FALSE);
|
|
recount_current_thread_usage(&usage);
|
|
ml_set_interrupts_enabled(interrupt_state);
|
|
|
|
switch (kind) {
|
|
case THSC_CPI: {
|
|
struct thsc_cpi counts = _usage_to_cpi(&usage);
|
|
return copyout(&counts, buf, MIN(sizeof(counts), size));
|
|
}
|
|
case THSC_TIME_CPI: {
|
|
struct thsc_time_cpi counts = _usage_to_time_cpi(&usage);
|
|
return copyout(&counts, buf, MIN(sizeof(counts), size));
|
|
}
|
|
case THSC_TIME_ENERGY_CPI: {
|
|
struct thsc_time_energy_cpi counts = _usage_to_time_energy_cpi(&usage);
|
|
return copyout(&counts, buf, MIN(sizeof(counts), size));
|
|
}
|
|
default:
|
|
panic("recount: unexpected thread_selfcounts kind: %d", kind);
|
|
}
|
|
}
|
|
|
|
static int
|
|
_selfcounts_perf_level(thread_selfcounts_kind_t kind, user_addr_t buf,
|
|
size_t size)
|
|
{
|
|
struct recount_usage usages[RCT_CPU_KIND_COUNT] = { 0 };
|
|
boolean_t interrupt_state = ml_set_interrupts_enabled(FALSE);
|
|
recount_current_thread_perf_level_usage(usages);
|
|
ml_set_interrupts_enabled(interrupt_state);
|
|
|
|
unsigned int cpu_types = ml_get_cpu_types();
|
|
unsigned int level_count = __builtin_popcount(cpu_types);
|
|
const size_t counts_len = MIN(MIN(recount_topo_count(RCT_TOPO_CPU_KIND),
|
|
RCT_CPU_KIND_COUNT), level_count);
|
|
|
|
switch (kind) {
|
|
case THSC_CPI_PER_PERF_LEVEL: {
|
|
struct thsc_cpi counts[RCT_CPU_KIND_COUNT] = { 0 };
|
|
for (unsigned int i = 0; i < counts_len; i++) {
|
|
const recount_cpu_kind_t cpu_kind = _perflevel_index_to_cpu_kind(i);
|
|
counts[i] = _usage_to_cpi(&usages[cpu_kind]);
|
|
}
|
|
return copyout(&counts, buf, MIN(sizeof(counts[0]) * counts_len, size));
|
|
}
|
|
case THSC_TIME_CPI_PER_PERF_LEVEL: {
|
|
struct thsc_time_cpi counts[RCT_CPU_KIND_COUNT] = { 0 };
|
|
for (unsigned int i = 0; i < counts_len; i++) {
|
|
const recount_cpu_kind_t cpu_kind = _perflevel_index_to_cpu_kind(i);
|
|
counts[i] = _usage_to_time_cpi(&usages[cpu_kind]);
|
|
}
|
|
return copyout(&counts, buf, MIN(sizeof(counts[0]) * counts_len, size));
|
|
}
|
|
case THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL: {
|
|
struct thsc_time_energy_cpi counts[RCT_CPU_KIND_COUNT] = { 0 };
|
|
for (unsigned int i = 0; i < counts_len; i++) {
|
|
const recount_cpu_kind_t cpu_kind = _perflevel_index_to_cpu_kind(i);
|
|
counts[i] = _usage_to_time_energy_cpi(&usages[cpu_kind]);
|
|
}
|
|
return copyout(&counts, buf, MIN(sizeof(counts[0]) * counts_len, size));
|
|
}
|
|
default:
|
|
panic("recount: unexpected thread_selfcounts kind: %d", kind);
|
|
}
|
|
}
|
|
|
|
int
|
|
thread_selfcounts(__unused struct proc *p,
|
|
struct thread_selfcounts_args *uap, __unused int *ret_out)
|
|
{
|
|
switch (uap->kind) {
|
|
case THSC_CPI:
|
|
case THSC_TIME_CPI:
|
|
case THSC_TIME_ENERGY_CPI:
|
|
return _selfcounts(uap->kind, uap->buf, uap->size);
|
|
|
|
case THSC_CPI_PER_PERF_LEVEL:
|
|
case THSC_TIME_CPI_PER_PERF_LEVEL:
|
|
case THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL:
|
|
return _selfcounts_perf_level(uap->kind, uap->buf, uap->size);
|
|
|
|
default:
|
|
return ENOTSUP;
|
|
}
|
|
}
|
|
|
|
static struct proc_threadcounts_data
|
|
_usage_to_proc_threadcounts(struct recount_usage *usage)
|
|
{
|
|
return (struct proc_threadcounts_data){
|
|
.ptcd_instructions = recount_usage_instructions(usage),
|
|
.ptcd_cycles = recount_usage_cycles(usage),
|
|
.ptcd_system_time_mach = recount_usage_system_time_mach(usage),
|
|
.ptcd_user_time_mach = usage->ru_metrics[RCT_LVL_USER].rm_time_mach,
|
|
#if CONFIG_PERVASIVE_ENERGY
|
|
.ptcd_energy_nj = usage->ru_energy_nj,
|
|
#endif // CONFIG_PERVASIVE_ENERGY
|
|
};
|
|
}
|
|
|
|
int
|
|
proc_pidthreadcounts(
|
|
struct proc *p,
|
|
uint64_t tid,
|
|
user_addr_t uaddr,
|
|
size_t usize,
|
|
int *size_out)
|
|
{
|
|
struct recount_usage usages[RCT_CPU_KIND_COUNT] = { 0 };
|
|
// Keep this in sync with proc_threadcounts_data -- this one just has the
|
|
// array length hard-coded to the maximum.
|
|
struct {
|
|
uint16_t counts_len;
|
|
uint16_t reserved0;
|
|
uint32_t reserved1;
|
|
struct proc_threadcounts_data counts[RCT_CPU_KIND_COUNT];
|
|
} counts = { 0 };
|
|
|
|
task_t task = proc_task(p);
|
|
if (task == TASK_NULL) {
|
|
return ESRCH;
|
|
}
|
|
|
|
bool found = recount_task_thread_perf_level_usage(task, tid, usages);
|
|
if (!found) {
|
|
return ESRCH;
|
|
}
|
|
|
|
const size_t counts_len = MIN(recount_topo_count(RCT_TOPO_CPU_KIND),
|
|
RCT_CPU_KIND_COUNT);
|
|
counts.counts_len = (uint16_t)counts_len;
|
|
// The number of perflevels for this boot can be constrained by the `cpus=`
|
|
// boot-arg, so determine the runtime number to prevent unexpected calls
|
|
// into the machine-dependent layers from asserting.
|
|
unsigned int cpu_types = ml_get_cpu_types();
|
|
unsigned int level_count = __builtin_popcount(cpu_types);
|
|
|
|
for (unsigned int i = 0; i < counts_len; i++) {
|
|
if (i < level_count) {
|
|
const recount_cpu_kind_t cpu_kind = _perflevel_index_to_cpu_kind(i);
|
|
counts.counts[i] = _usage_to_proc_threadcounts(&usages[cpu_kind]);
|
|
}
|
|
}
|
|
size_t copyout_size = MIN(sizeof(uint64_t) +
|
|
counts_len * sizeof(struct proc_threadcounts_data), usize);
|
|
assert(copyout_size <= sizeof(counts));
|
|
int error = copyout(&counts, uaddr, copyout_size);
|
|
if (error == 0) {
|
|
*size_out = (int)copyout_size;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
#else // CONFIG_PERVASIVE_CPI
|
|
|
|
int
|
|
proc_pidthreadcounts(
|
|
__unused struct proc *p,
|
|
__unused uint64_t tid,
|
|
__unused user_addr_t uaddr,
|
|
__unused size_t usize,
|
|
__unused int *ret_out)
|
|
{
|
|
return ENOTSUP;
|
|
}
|
|
|
|
int
|
|
thread_selfcounts(__unused struct proc *p,
|
|
__unused struct thread_selfcounts_args *uap, __unused int *ret_out)
|
|
{
|
|
return ENOTSUP;
|
|
}
|
|
|
|
#endif // !CONFIG_PERVASIVE_CPI
|