1/*
2 * Copyright 2009, Intel Corporation
3 * Copyright 2009, Sun Microsystems, Inc
4 *
5 * This file is part of PowerTOP
6 *
7 * This program file is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program in a file named COPYING; if not, write to the
18 * Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301 USA
21 *
22 * Authors:
23 *	Arjan van de Ven <arjan@linux.intel.com>
24 *	Eric C Saxe <eric.saxe@sun.com>
25 *	Aubrey Li <aubrey.li@intel.com>
26 */
27
28/*
29 * GPL Disclaimer
30 *
31 * For the avoidance of doubt, except that if any license choice other
32 * than GPL or LGPL is available it will apply instead, Sun elects to
33 * use only the General Public License version 2 (GPLv2) at this time
34 * for any software where a choice of GPL license versions is made
35 * available with the language indicating that GPLv2 or any later
36 * version may be used, or where a choice of which version of the GPL
37 * is applied is otherwise unspecified.
38 */
39
40#include <stdlib.h>
41#include <string.h>
42#include <dtrace.h>
43#include <kstat.h>
44#include <errno.h>
45#include "powertop.h"
46
47/*
48 * Global turbo related variables definitions
49 */
50boolean_t		g_turbo_supported;
51double			g_turbo_ratio;
52
53/*
54 * The variables to store kstat snapshot
55 */
56static turbo_info_t	*cpu_turbo_info = NULL;
57static turbo_info_t	*t_new = NULL;
58
59/*
60 * Perform setup necessary to enumerate and track CPU turbo information
61 */
62static int
63pt_turbo_init(void)
64{
65	kstat_ctl_t 		*kc;
66	kstat_t 		*ksp;
67	kstat_named_t 		*knp;
68
69	/*
70	 * check if the CPU turbo is supported
71	 */
72	if ((kc = kstat_open()) == NULL) {
73		g_turbo_supported = B_FALSE;
74		return (errno);
75	}
76
77	ksp = kstat_lookup(kc, "turbo", 0, NULL);
78	if (ksp == NULL) {
79		g_turbo_supported = B_FALSE;
80		(void) kstat_close(kc);
81		return (-1);
82	}
83
84	(void) kstat_read(kc, ksp, NULL);
85
86	knp = kstat_data_lookup(ksp, "turbo_supported");
87	if (knp == NULL) {
88		pt_error("couldn't find 'turbo_supported' kstat\n");
89		g_turbo_supported = B_FALSE;
90		(void) kstat_close(kc);
91		return (-2);
92	}
93
94	/*
95	 * Initialize turbo information structure if turbo mode is supported
96	 */
97	if (knp->value.ui32) {
98		g_turbo_supported = B_TRUE;
99		cpu_turbo_info = calloc((size_t)g_ncpus, sizeof (turbo_info_t));
100		t_new = calloc((size_t)g_ncpus, sizeof (turbo_info_t));
101	}
102
103	(void) kstat_close(kc);
104	return (0);
105}
106
107/*
108 * Take a snapshot of each CPU's turbo information
109 * by looking through the turbo kstats.
110 */
111static int
112pt_turbo_snapshot(turbo_info_t *turbo_snapshot)
113{
114	kstat_ctl_t 		*kc;
115	kstat_t 		*ksp;
116	kstat_named_t 		*knp;
117	int 			cpu;
118	turbo_info_t		*turbo_info;
119
120	if ((kc = kstat_open()) == NULL)
121		return (errno);
122
123	for (cpu = 0; cpu < g_ncpus; cpu++) {
124		turbo_info = &turbo_snapshot[cpu];
125		ksp = kstat_lookup(kc, "turbo", g_cpu_table[cpu], NULL);
126		if (ksp == NULL) {
127			pt_error("couldn't find 'turbo' kstat for CPU %d\n",
128			    cpu);
129			(void) kstat_close(kc);
130			return (-1);
131		}
132
133		if (kstat_read(kc, ksp, NULL) == -1) {
134			pt_error("couldn't read 'turbo' kstat for CPU %d\n",
135			    cpu);
136			(void) kstat_close(kc);
137			return (-2);
138		}
139
140		knp = kstat_data_lookup(ksp, "turbo_mcnt");
141		if (knp == NULL) {
142			pt_error("couldn't find 'turbo_mcnt' kstat for CPU "
143			    "%d\n", cpu);
144			(void) kstat_close(kc);
145			return (-3);
146		}
147
148		/*
149		 * snapshot IA32_MPERF_MSR
150		 */
151		turbo_info->t_mcnt = knp->value.ui64;
152
153		knp = kstat_data_lookup(ksp, "turbo_acnt");
154		if (knp == NULL) {
155			pt_error("couldn't find 'turbo_acnt' kstat for CPU "
156			    "%d\n", cpu);
157			(void) kstat_close(kc);
158			return (-4);
159		}
160
161		/*
162		 * snapshot IA32_APERF_MSR
163		 */
164		turbo_info->t_acnt = knp->value.ui64;
165	}
166
167	if (kstat_close(kc) != 0)
168		pt_error("couldn't close 'turbo' kstat\n");
169
170	return (0);
171}
172
173/*
174 * Turbo support checking and information initialization
175 */
176int
177pt_turbo_stat_prepare(void)
178{
179	int ret = 0;
180
181	if ((ret = pt_turbo_init()) != 0)
182		return (ret);
183
184	if ((ret = pt_turbo_snapshot(cpu_turbo_info)) != 0)
185		pt_error("failed to snapshot 'turbo' kstat\n");
186
187	return (ret);
188}
189
190/*
191 * When doing the statistics collection, we compare two kstat snapshot
192 * and get a delta. the final ratio of performance boost will be worked
193 * out according to the kstat delta
194 */
195int
196pt_turbo_stat_collect(void)
197{
198	int		cpu;
199	uint64_t	delta_mcnt, delta_acnt;
200	double		ratio;
201	int		ret;
202
203	/*
204	 * Take a snapshot of turbo information to setup turbo_info_t
205	 * structure
206	 */
207	if ((ret = pt_turbo_snapshot(t_new)) != 0) {
208		pt_error("failed to snapshot 'turbo' kstat\n");
209		return (ret);
210	}
211
212	/*
213	 * Calculate the kstat delta and work out the performance boost ratio
214	 */
215	for (cpu = 0; cpu < g_ncpus; cpu++) {
216		delta_mcnt = t_new[cpu].t_mcnt - cpu_turbo_info[cpu].t_mcnt;
217		delta_acnt = t_new[cpu].t_acnt - cpu_turbo_info[cpu].t_acnt;
218
219		if ((delta_mcnt > delta_acnt) || (delta_mcnt == 0))
220			ratio = 1.0;
221		else
222			ratio = (double)delta_acnt / (double)delta_mcnt;
223		g_turbo_ratio += ratio;
224	}
225
226	g_turbo_ratio = g_turbo_ratio / (double)g_ncpus;
227
228	/*
229	 * Update the structure of the kstat for the next time calculation
230	 */
231	(void) memcpy(cpu_turbo_info, t_new, g_ncpus * (sizeof (turbo_info_t)));
232
233	return (0);
234}
235