cpuidle_sysfs.c 4.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 *  (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 "helpers/sysfs.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);
39

40 41
	dprint("%s: - timediff: %llu - statediff: %llu - percent: %f (%u)\n",
	       cpuidle_cstates[id].name, timediff, statediff, *percent, cpu);
42

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
	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] =
				sysfs_get_idlestate_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] =
				sysfs_get_idlestate_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)) {
85 86 87
		switch (num) {
		case 1:
			strcpy(tmp, "C1");
88
			break;
89 90
		case 2:
			strcpy(tmp, "C3");
91
			break;
92 93
		case 3:
			strcpy(tmp, "C6");
94 95 96
			break;
		}
	} else if (!strncmp(tmp, "SNB-", 4)) {
97 98 99
		switch (num) {
		case 1:
			strcpy(tmp, "C1");
100
			break;
101 102
		case 2:
			strcpy(tmp, "C3");
103
			break;
104 105
		case 3:
			strcpy(tmp, "C6");
106
			break;
107 108
		case 4:
			strcpy(tmp, "C7");
109 110 111
			break;
		}
	} else if (!strncmp(tmp, "ATM-", 4)) {
112 113 114
		switch (num) {
		case 1:
			strcpy(tmp, "C1");
115
			break;
116 117
		case 2:
			strcpy(tmp, "C2");
118
			break;
119 120
		case 3:
			strcpy(tmp, "C4");
121
			break;
122 123
		case 4:
			strcpy(tmp, "C6");
124 125 126 127 128
			break;
		}
	}
}

129
static struct cpuidle_monitor *cpuidle_register(void)
130 131 132 133 134 135 136
{
	int num;
	char *tmp;

	/* Assume idle state count is the same for all CPUs */
	cpuidle_sysfs_monitor.hw_states_num = sysfs_get_idlestate_count(0);

137
	if (cpuidle_sysfs_monitor.hw_states_num <= 0)
138 139
		return NULL;

140
	for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) {
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
		tmp = sysfs_get_idlestate_name(0, 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 = sysfs_get_idlestate_desc(0, 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;
157 158 159
		cpuidle_cstates[num].get_count_percent =
			cpuidle_get_count_percent;
	};
160 161

	/* Free this at program termination */
162 163
	previous_count = malloc(sizeof(long long *) * cpu_count);
	current_count = malloc(sizeof(long long *) * cpu_count);
164
	for (num = 0; num < cpu_count; num++) {
165 166 167 168
		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);
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
	}

	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,
};