/*
* This file is part of PowerTOP
*
* This program file is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program in a file named COPYING; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* Authors:
* Arjan van de Ven <arjan@linux.intel.com>
* Eric C Saxe <eric.saxe@sun.com>
* Aubrey Li <aubrey.li@intel.com>
*/
/*
* Copyright (c) 2009, Intel Corporation.
* All Rights Reserved.
*/
/*
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
*/
/*
* GPL Disclaimer
*
* For the avoidance of doubt, except that if any license choice other
* than GPL or LGPL is available it will apply instead, Sun elects to
* use only the General Public License version 2 (GPLv2) at this time
* for any software where a choice of GPL license versions is made
* available with the language indicating that GPLv2 or any later
* version may be used, or where a choice of which version of the GPL
* is applied is otherwise unspecified.
*/
#include <stdlib.h>
#include <string.h>
#include <dtrace.h>
#include <kstat.h>
#include <errno.h>
#include "powertop.h"
#include <stdio.h>
#include <unistd.h>
#include <stropts.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/pm.h>
static boolean_t pt_check_cpupm_enabled(void);
static void pt_cpufreq_null_enable(void);
#define DTP_ARG_LENGTH 5
static uint64_t max_cpufreq = 0;
static int pt_cpufreq_check_pm(void);
/*
* Perform setup necessary to enumerate and track CPU speed changes
*/
int
pt_cpufreq_stat_prepare(void)
{
kstat_t *ksp;
kstat_named_t *knp;
pstate_info_t *state;
char *s, *token;
int err;
state = g_pstate_info;
if ((g_cpu_power_states = calloc((size_t)g_ncpus,
sizeof (cpu_power_info_t))) == NULL)
return (-1);
/*
* Enumerate the CPU frequencies
*/
ksp = kstat_lookup(g_kc, "cpu_info", g_cpu_table[g_observed_cpu], NULL);
if (ksp == NULL) {
err = errno;
return (err);
}
(void) kstat_read(g_kc, ksp, NULL);
knp = pt_kstat_data_lookup(ksp, "supported_frequencies_Hz",
&g_supported_freq_index);
s = knp->value.str.addr.ptr;
g_max_pstates = 0;
for (token = strtok(s, ":"), s = NULL;
token != NULL && g_max_pstates < CPU_MAX_PSTATES;
token = strtok(NULL, ":")) {
state->speed = HZ2MHZ(atoll(token));
if (state->speed > max_cpufreq)
max_cpufreq = state->speed;
state->total_time = (uint64_t)0;
g_max_pstates++;
state++;
}
if (token != NULL) {
pt_error("CPU exceeds the supported number of %s\n",
g_msg_pstate);
}
/*
* Return if speed transition is not supported
*/
if (g_max_pstates < 2)
return (-1);
return (0);
}
/*
* The administrative interface offered by libpower is not especially
* suited to determine with certainty that any given specific PM
* feature (in this case CPU PM) is enabled, nor for being sure that
* it *has* been enabled if we try to set it using those
* responsiveness abstractions.
*
* For the time-being, therefore, we do not do this using the
* user-level administrative abstractions offered by libpower,
* instead, we will use a private interface offered directly by the pm
* device driver (see corresponding functions below)
*/
/*
* pt_cpufreq_check_pm() -
*
* This routine is called repeatedly (via pt_cpufreq_suggest())
* from the main loop of pmconfig.c to see whether CPUPM is on or not.
*
* This implementation now checks directly (using /dev/pm), if CPUPM
* is enabled (thus allowing the use of P-states)
*
* returns:
*
* 0 if PM is disabled or if CPUPM is not enabled
*
* 1 if there's nothing for us to do because:
* (a) the system does not support frequency scaling
* (b) we cannot open /dev/pm (need check for proper
* authorization above)
*
* 2 if CPUPM is enabled
*
* Notice the ordering of the return values, they will be picked up and
* switched upon ascendingly.
*/
static int
pt_cpufreq_check_pm(void)
{
if (g_max_pstates < 2) {
return (1);
}
/*
* Now, let's see if CPUPM is enabled
*/
if (pt_check_cpupm_enabled() == B_FALSE) {
return (0);
}
return (2);
}
/*
* We define a g_cpufreq_msg here, to say that CPUPM is disabled
* and a null function, which we need to associate with the
* suggestion that we will post when CPUPM is disabled.
*/
static char *g_cpufreq_msg = "(CPU PM is disabled)";
static void
pt_cpufreq_null_enable(void)
{
}
/*
* pt_check_cpupm_enabled() -
* access the kernel directly (using /dev/pm) to determine
* whether CPUPM is enabled or not.
*/
static boolean_t pt_check_cpupm_enabled(void)
{
int ioc_ret = 0;
boolean_t cpupm_enabled; /* kernel CPUPM operating or not */
/*
* Check that CPUPM is enabled.
*/
ioc_ret = ioctl(g_pm_fd, PM_GET_CPUPM_STATE, NULL);
if (ioc_ret < 0) {
return (B_FALSE); /* Should be a sep. error condition really */
}
switch (ioc_ret) {
case PM_CPU_PM_ENABLED:
cpupm_enabled = B_TRUE;
break;
case PM_CPU_PM_DISABLED:
cpupm_enabled = B_FALSE;
break;
default:
cpupm_enabled = B_FALSE;
break;
}
return (cpupm_enabled);
}
/*
* pt_cpufreq_suggest() -
*
* This is an extern that is called by powertop.c
* It installs a suggestion if CPUPM is disabled.
*
* If there is a null function, then nothing gets called when the
* associated key ('X' here) is struck.
*/
void
pt_cpufreq_suggest(void)
{
int result = pt_cpufreq_check_pm();
switch (result) {
/*
* 0 - means that CPUPM (P-states) is not enabled.
* Put up a suggestion to let the user know that they
* can enable it using poweradm(1m).
*/
case 0:
pt_sugg_add("Suggestion: enable CPU power management "
"using poweradm(1m)", 40, 'X', (char *)g_cpufreq_msg,
pt_cpufreq_null_enable);
break;
/*
* 2 - means that CPUPM (P-states) is enabled.
* Take down the suggestion.
*/
case 2:
(void) pt_sugg_remove(pt_cpufreq_null_enable);
break;
}
}