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