Files
linux/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
Abhishek Goel dbdc468f35 cpupower : Fix cpupower working when cpu0 is offline
cpuidle_monitor used to assume that cpu0 is always online which is not
a valid assumption on POWER machines. This patch fixes this by getting
the cpu on which the current thread is running, instead of always using
cpu0 for monitoring which may not be online.

Signed-off-by: Abhishek Goel <huntbag@linux.vnet.ibm.com>
Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com>
2017-11-15 08:37:42 -07:00

200 lines
4.5 KiB
C

/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc
*
* Licensed under the terms of the GNU GPL License version 2.
*
*/
#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;
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;
}
}
}
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;
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,
.needs_root = 0,
.overflow_s = UINT_MAX,
};