1/*
2 *  (C) 2010,2011       Thomas Renninger <trenn@suse.de>, Novell Inc
3 *
4 *  Licensed under the terms of the GNU GPL License version 2.
5 *
6 */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <stdint.h>
11#include <string.h>
12#include <limits.h>
13
14#include "helpers/sysfs.h"
15#include "helpers/helpers.h"
16#include "idle_monitor/cpupower-monitor.h"
17
18#define CPUIDLE_STATES_MAX 10
19static cstate_t cpuidle_cstates[CPUIDLE_STATES_MAX];
20struct cpuidle_monitor cpuidle_sysfs_monitor;
21
22static unsigned long long **previous_count;
23static unsigned long long **current_count;
24struct timespec start_time;
25static unsigned long long timediff;
26
27static int cpuidle_get_count_percent(unsigned int id, double *percent,
28				     unsigned int cpu)
29{
30	unsigned long long statediff = current_count[cpu][id]
31		- previous_count[cpu][id];
32	dprint("%s: - diff: %llu - percent: %f (%u)\n",
33	       cpuidle_cstates[id].name, timediff, *percent, cpu);
34
35	if (timediff == 0)
36		*percent = 0.0;
37	else
38		*percent = ((100.0 * statediff) / timediff);
39
40	dprint("%s: - timediff: %llu - statediff: %llu - percent: %f (%u)\n",
41	       cpuidle_cstates[id].name, timediff, statediff, *percent, cpu);
42
43	return 0;
44}
45
46static int cpuidle_start(void)
47{
48	int cpu, state;
49	clock_gettime(CLOCK_REALTIME, &start_time);
50	for (cpu = 0; cpu < cpu_count; cpu++) {
51		for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num;
52		     state++) {
53			previous_count[cpu][state] =
54				sysfs_get_idlestate_time(cpu, state);
55			dprint("CPU %d - State: %d - Val: %llu\n",
56			       cpu, state, previous_count[cpu][state]);
57		}
58	};
59	return 0;
60}
61
62static int cpuidle_stop(void)
63{
64	int cpu, state;
65	struct timespec end_time;
66	clock_gettime(CLOCK_REALTIME, &end_time);
67	timediff = timespec_diff_us(start_time, end_time);
68
69	for (cpu = 0; cpu < cpu_count; cpu++) {
70		for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num;
71		     state++) {
72			current_count[cpu][state] =
73				sysfs_get_idlestate_time(cpu, state);
74			dprint("CPU %d - State: %d - Val: %llu\n",
75			       cpu, state, previous_count[cpu][state]);
76		}
77	};
78	return 0;
79}
80
81void fix_up_intel_idle_driver_name(char *tmp, int num)
82{
83	/* fix up cpuidle name for intel idle driver */
84	if (!strncmp(tmp, "NHM-", 4)) {
85		switch (num) {
86		case 1:
87			strcpy(tmp, "C1");
88			break;
89		case 2:
90			strcpy(tmp, "C3");
91			break;
92		case 3:
93			strcpy(tmp, "C6");
94			break;
95		}
96	} else if (!strncmp(tmp, "SNB-", 4)) {
97		switch (num) {
98		case 1:
99			strcpy(tmp, "C1");
100			break;
101		case 2:
102			strcpy(tmp, "C3");
103			break;
104		case 3:
105			strcpy(tmp, "C6");
106			break;
107		case 4:
108			strcpy(tmp, "C7");
109			break;
110		}
111	} else if (!strncmp(tmp, "ATM-", 4)) {
112		switch (num) {
113		case 1:
114			strcpy(tmp, "C1");
115			break;
116		case 2:
117			strcpy(tmp, "C2");
118			break;
119		case 3:
120			strcpy(tmp, "C4");
121			break;
122		case 4:
123			strcpy(tmp, "C6");
124			break;
125		}
126	}
127}
128
129static struct cpuidle_monitor *cpuidle_register(void)
130{
131	int num;
132	char *tmp;
133
134	/* Assume idle state count is the same for all CPUs */
135	cpuidle_sysfs_monitor.hw_states_num = sysfs_get_idlestate_count(0);
136
137	if (cpuidle_sysfs_monitor.hw_states_num <= 0)
138		return NULL;
139
140	for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) {
141		tmp = sysfs_get_idlestate_name(0, num);
142		if (tmp == NULL)
143			continue;
144
145		fix_up_intel_idle_driver_name(tmp, num);
146		strncpy(cpuidle_cstates[num].name, tmp, CSTATE_NAME_LEN - 1);
147		free(tmp);
148
149		tmp = sysfs_get_idlestate_desc(0, num);
150		if (tmp == NULL)
151			continue;
152		strncpy(cpuidle_cstates[num].desc, tmp,	CSTATE_DESC_LEN - 1);
153		free(tmp);
154
155		cpuidle_cstates[num].range = RANGE_THREAD;
156		cpuidle_cstates[num].id = num;
157		cpuidle_cstates[num].get_count_percent =
158			cpuidle_get_count_percent;
159	};
160
161	/* Free this at program termination */
162	previous_count = malloc(sizeof(long long *) * cpu_count);
163	current_count = malloc(sizeof(long long *) * cpu_count);
164	for (num = 0; num < cpu_count; num++) {
165		previous_count[num] = malloc(sizeof(long long) *
166					cpuidle_sysfs_monitor.hw_states_num);
167		current_count[num] = malloc(sizeof(long long) *
168					cpuidle_sysfs_monitor.hw_states_num);
169	}
170
171	cpuidle_sysfs_monitor.name_len = strlen(cpuidle_sysfs_monitor.name);
172	return &cpuidle_sysfs_monitor;
173}
174
175void cpuidle_unregister(void)
176{
177	int num;
178
179	for (num = 0; num < cpu_count; num++) {
180		free(previous_count[num]);
181		free(current_count[num]);
182	}
183	free(previous_count);
184	free(current_count);
185}
186
187struct cpuidle_monitor cpuidle_sysfs_monitor = {
188	.name			= "Idle_Stats",
189	.hw_states		= cpuidle_cstates,
190	.start			= cpuidle_start,
191	.stop			= cpuidle_stop,
192	.do_register		= cpuidle_register,
193	.unregister		= cpuidle_unregister,
194	.needs_root		= 0,
195	.overflow_s		= UINT_MAX,
196};
197