213 lines
4.9 KiB
C
213 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <cpuidle.h>
|
|
|
|
#include "helpers/helpers.h"
|
|
#include "idle_monitor/cpupower-monitor.h"
|
|
|
|
#define CPUIDLE_STATES_MAX 10
|
|
static cstate_t cpuidle_cstates[CPUIDLE_STATES_MAX];
|
|
struct cpuidle_monitor cpuidle_sysfs_monitor;
|
|
|
|
static unsigned long long **previous_count;
|
|
static unsigned long long **current_count;
|
|
static struct timespec start_time;
|
|
static unsigned long long timediff;
|
|
|
|
static int cpuidle_get_count_percent(unsigned int id, double *percent,
|
|
unsigned int cpu)
|
|
{
|
|
unsigned long long statediff = current_count[cpu][id]
|
|
- previous_count[cpu][id];
|
|
dprint("%s: - diff: %llu - percent: %f (%u)\n",
|
|
cpuidle_cstates[id].name, timediff, *percent, cpu);
|
|
|
|
if (timediff == 0)
|
|
*percent = 0.0;
|
|
else
|
|
*percent = ((100.0 * statediff) / timediff);
|
|
|
|
dprint("%s: - timediff: %llu - statediff: %llu - percent: %f (%u)\n",
|
|
cpuidle_cstates[id].name, timediff, statediff, *percent, cpu);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cpuidle_start(void)
|
|
{
|
|
int cpu, state;
|
|
clock_gettime(CLOCK_REALTIME, &start_time);
|
|
for (cpu = 0; cpu < cpu_count; cpu++) {
|
|
for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num;
|
|
state++) {
|
|
previous_count[cpu][state] =
|
|
cpuidle_state_time(cpu, state);
|
|
dprint("CPU %d - State: %d - Val: %llu\n",
|
|
cpu, state, previous_count[cpu][state]);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int cpuidle_stop(void)
|
|
{
|
|
int cpu, state;
|
|
struct timespec end_time;
|
|
clock_gettime(CLOCK_REALTIME, &end_time);
|
|
timediff = timespec_diff_us(start_time, end_time);
|
|
|
|
for (cpu = 0; cpu < cpu_count; cpu++) {
|
|
for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num;
|
|
state++) {
|
|
current_count[cpu][state] =
|
|
cpuidle_state_time(cpu, state);
|
|
dprint("CPU %d - State: %d - Val: %llu\n",
|
|
cpu, state, previous_count[cpu][state]);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void fix_up_intel_idle_driver_name(char *tmp, int num)
|
|
{
|
|
/* fix up cpuidle name for intel idle driver */
|
|
if (!strncmp(tmp, "NHM-", 4)) {
|
|
switch (num) {
|
|
case 1:
|
|
strcpy(tmp, "C1");
|
|
break;
|
|
case 2:
|
|
strcpy(tmp, "C3");
|
|
break;
|
|
case 3:
|
|
strcpy(tmp, "C6");
|
|
break;
|
|
}
|
|
} else if (!strncmp(tmp, "SNB-", 4)) {
|
|
switch (num) {
|
|
case 1:
|
|
strcpy(tmp, "C1");
|
|
break;
|
|
case 2:
|
|
strcpy(tmp, "C3");
|
|
break;
|
|
case 3:
|
|
strcpy(tmp, "C6");
|
|
break;
|
|
case 4:
|
|
strcpy(tmp, "C7");
|
|
break;
|
|
}
|
|
} else if (!strncmp(tmp, "ATM-", 4)) {
|
|
switch (num) {
|
|
case 1:
|
|
strcpy(tmp, "C1");
|
|
break;
|
|
case 2:
|
|
strcpy(tmp, "C2");
|
|
break;
|
|
case 3:
|
|
strcpy(tmp, "C4");
|
|
break;
|
|
case 4:
|
|
strcpy(tmp, "C6");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef __powerpc__
|
|
void map_power_idle_state_name(char *tmp)
|
|
{
|
|
if (!strncmp(tmp, "stop0_lite", CSTATE_NAME_LEN))
|
|
strcpy(tmp, "stop0L");
|
|
else if (!strncmp(tmp, "stop1_lite", CSTATE_NAME_LEN))
|
|
strcpy(tmp, "stop1L");
|
|
else if (!strncmp(tmp, "stop2_lite", CSTATE_NAME_LEN))
|
|
strcpy(tmp, "stop2L");
|
|
}
|
|
#else
|
|
void map_power_idle_state_name(char *tmp) { }
|
|
#endif
|
|
|
|
static struct cpuidle_monitor *cpuidle_register(void)
|
|
{
|
|
int num;
|
|
char *tmp;
|
|
int this_cpu;
|
|
|
|
this_cpu = sched_getcpu();
|
|
|
|
/* Assume idle state count is the same for all CPUs */
|
|
cpuidle_sysfs_monitor.hw_states_num = cpuidle_state_count(this_cpu);
|
|
|
|
if (cpuidle_sysfs_monitor.hw_states_num <= 0)
|
|
return NULL;
|
|
|
|
for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) {
|
|
tmp = cpuidle_state_name(this_cpu, num);
|
|
if (tmp == NULL)
|
|
continue;
|
|
|
|
map_power_idle_state_name(tmp);
|
|
fix_up_intel_idle_driver_name(tmp, num);
|
|
strncpy(cpuidle_cstates[num].name, tmp, CSTATE_NAME_LEN - 1);
|
|
free(tmp);
|
|
|
|
tmp = cpuidle_state_desc(this_cpu, num);
|
|
if (tmp == NULL)
|
|
continue;
|
|
strncpy(cpuidle_cstates[num].desc, tmp, CSTATE_DESC_LEN - 1);
|
|
free(tmp);
|
|
|
|
cpuidle_cstates[num].range = RANGE_THREAD;
|
|
cpuidle_cstates[num].id = num;
|
|
cpuidle_cstates[num].get_count_percent =
|
|
cpuidle_get_count_percent;
|
|
}
|
|
|
|
/* Free this at program termination */
|
|
previous_count = malloc(sizeof(long long *) * cpu_count);
|
|
current_count = malloc(sizeof(long long *) * cpu_count);
|
|
for (num = 0; num < cpu_count; num++) {
|
|
previous_count[num] = malloc(sizeof(long long) *
|
|
cpuidle_sysfs_monitor.hw_states_num);
|
|
current_count[num] = malloc(sizeof(long long) *
|
|
cpuidle_sysfs_monitor.hw_states_num);
|
|
}
|
|
|
|
cpuidle_sysfs_monitor.name_len = strlen(cpuidle_sysfs_monitor.name);
|
|
return &cpuidle_sysfs_monitor;
|
|
}
|
|
|
|
void cpuidle_unregister(void)
|
|
{
|
|
int num;
|
|
|
|
for (num = 0; num < cpu_count; num++) {
|
|
free(previous_count[num]);
|
|
free(current_count[num]);
|
|
}
|
|
free(previous_count);
|
|
free(current_count);
|
|
}
|
|
|
|
struct cpuidle_monitor cpuidle_sysfs_monitor = {
|
|
.name = "Idle_Stats",
|
|
.hw_states = cpuidle_cstates,
|
|
.start = cpuidle_start,
|
|
.stop = cpuidle_stop,
|
|
.do_register = cpuidle_register,
|
|
.unregister = cpuidle_unregister,
|
|
.flags.needs_root = 0,
|
|
.overflow_s = UINT_MAX,
|
|
};
|