378 lines
9.5 KiB
C
378 lines
9.5 KiB
C
/***************************************************************************
|
|
cpufreq.c - description
|
|
-------------------
|
|
begin : Feb 10 2003
|
|
copyright : (C) 2003,2004,2005 by Noberasco Michele
|
|
e-mail : s4t4n@gentoo.org
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program 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; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* 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; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
|
|
#define SYSFS_CPU_BASE_DIR "/sys/devices/system/cpu"
|
|
#define SYSFS_CPUFREQ_TEST "affected_cpus"
|
|
#define SYSFS_CPUFREQ_AVAIL_GOV "scaling_available_governors"
|
|
#define SYSFS_CPUFREQ_SET_GOV "scaling_governor"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include "power_management.h"
|
|
#include "lib_utils.h"
|
|
|
|
|
|
/* This will contain a list of CPU that we will manage through cpufreq */
|
|
struct cpufreq_available_cpus
|
|
{
|
|
int cpu_number;
|
|
char *dir_name;
|
|
struct cpufreq_available_cpus *next;
|
|
} *available_cpus = NULL;
|
|
|
|
|
|
/* Add one element to the list of CPUs available for frequency scaling */
|
|
void add_to_cpus_list(int cpu_number, char *path)
|
|
{
|
|
struct cpufreq_available_cpus *new_cpu;
|
|
|
|
if (!path) return;
|
|
|
|
new_cpu = (struct cpufreq_available_cpus *) calloc(1, sizeof(struct cpufreq_available_cpus));
|
|
if (!new_cpu)
|
|
{
|
|
fprintf(stderr, "allocation failure!\n");
|
|
abort();
|
|
}
|
|
new_cpu->cpu_number = cpu_number;
|
|
new_cpu->dir_name = path;
|
|
new_cpu->next = NULL;
|
|
|
|
if (available_cpus)
|
|
{ /* we navigate to the bottom of the list... */
|
|
struct cpufreq_available_cpus *curr_cpu = available_cpus;
|
|
for (; curr_cpu->next != NULL; curr_cpu=curr_cpu->next);
|
|
curr_cpu->next = new_cpu;
|
|
return;
|
|
}
|
|
|
|
available_cpus = new_cpu;
|
|
}
|
|
|
|
/* check CPUfreq support via the sysfs file system */
|
|
int check_cpufreq_2_6(void)
|
|
{
|
|
DIR *dir = opendir(SYSFS_CPU_BASE_DIR);
|
|
struct dirent *entry;
|
|
|
|
/* How many CPUs do we have? */
|
|
if (!dir) return 0;
|
|
while ((entry= readdir(dir)))
|
|
{
|
|
size_t len = 0;
|
|
char *line = NULL;
|
|
char *test;
|
|
char *filename;
|
|
FILE *fp;
|
|
|
|
if (!strcmp(entry->d_name, "." )) continue;
|
|
if (!strcmp(entry->d_name, "..")) continue;
|
|
if (strncmp(entry->d_name, "cpu", 3)) continue;
|
|
|
|
/* let's see wether this particular CPU has CPUfreq support... */
|
|
filename = StrApp((char**)NULL, SYSFS_CPU_BASE_DIR, "/", entry->d_name, "/cpufreq/", SYSFS_CPUFREQ_TEST, (char*)NULL);
|
|
fp = fopen(filename, "r");
|
|
if (!fp)
|
|
{ /* no good... */
|
|
free(filename);
|
|
continue;
|
|
}
|
|
if (getline(&line, &len, fp) == -1)
|
|
{ /* no good... */
|
|
free(filename);
|
|
fclose(fp);
|
|
continue;
|
|
}
|
|
fclose(fp);
|
|
test = (char *) calloc(strlen(line)+1, sizeof(char));
|
|
if (!test)
|
|
{
|
|
fprintf(stderr, "allocation failure!\n");
|
|
abort();
|
|
}
|
|
while (sscanf(line, "%s", test) != EOF)
|
|
/* let's see wether we can find a cpu number equal to our one in test file... */
|
|
if (!strcmp((entry->d_name)+3, test))
|
|
{ /* Got it! */
|
|
int cpu_number = atoi(entry->d_name + 3) + 1;
|
|
add_to_cpus_list(cpu_number, StrApp((char**)NULL, SYSFS_CPU_BASE_DIR, "/", entry->d_name, "/cpufreq/", (char*)NULL));
|
|
break;
|
|
}
|
|
/* no good.. */
|
|
free(filename);
|
|
free(test);
|
|
free(line);
|
|
}
|
|
closedir(dir);
|
|
|
|
/* did we get results? */
|
|
if (available_cpus) return 1;
|
|
|
|
/* What a shame, all these CPU cycles for nothing! */
|
|
return 0;
|
|
}
|
|
|
|
/* Checks wether this machine supports CPU frequency scaling,
|
|
* and builds a list of CPUs available for that...
|
|
*/
|
|
int check_cpufreq(void)
|
|
{
|
|
if (kernel_version == IS_2_6)
|
|
return check_cpufreq_2_6();
|
|
|
|
/* 2.4 kernels are not supported at the moment... */
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *cpufreq_get_governor_2_6(int cpu)
|
|
{
|
|
struct cpufreq_available_cpus *curr_cpu = available_cpus;
|
|
char *filename;
|
|
FILE *fp;
|
|
size_t len = 0;
|
|
char *line = NULL;
|
|
char *test;
|
|
|
|
for (; curr_cpu!=NULL; curr_cpu=curr_cpu->next)
|
|
if (curr_cpu->cpu_number == cpu)
|
|
break;
|
|
|
|
if (curr_cpu->cpu_number != cpu) return NULL;
|
|
|
|
filename = StrApp((char**)NULL, curr_cpu->dir_name, SYSFS_CPUFREQ_SET_GOV, (char*)NULL);
|
|
fp = fopen(filename, "r");
|
|
|
|
if (!fp)
|
|
{
|
|
free(filename);
|
|
return NULL;
|
|
}
|
|
|
|
if (getline(&line, &len, fp) == -1)
|
|
{
|
|
fprintf(stderr, "could not read from file %s!\n", filename);
|
|
free(filename);
|
|
fclose(fp);
|
|
return NULL;
|
|
}
|
|
fclose(fp);
|
|
free(filename);
|
|
|
|
test = (char*) calloc(strlen(line)+1,sizeof(char));
|
|
if (!test)
|
|
{
|
|
fprintf(stderr, "failure allocating memory!\n");
|
|
abort();
|
|
}
|
|
if (sscanf(line, "%s", test) != 1)
|
|
{
|
|
free(test);
|
|
free(line);
|
|
return NULL;
|
|
}
|
|
free(line);
|
|
|
|
return test;
|
|
}
|
|
|
|
/* Get cpufreq governor of CPU #n, where 1<=n<=MAX, where MAX is
|
|
* the number of CPUs you have in your system
|
|
*/
|
|
char *cpufreq_get_governor(int cpu)
|
|
{
|
|
if (cpu < 1) return NULL;
|
|
|
|
if (kernel_version == IS_2_6)
|
|
return cpufreq_get_governor_2_6(cpu);
|
|
|
|
/* 2.4 kernels are not supported at the moment... */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* set CPUfreq governor via the sysfs file system */
|
|
int set_cpufreq_governor_2_6(char *governor)
|
|
{
|
|
struct cpufreq_available_cpus *curr_cpu=available_cpus;
|
|
int retval = 1;
|
|
|
|
/* Set desired governor for all available CPUs */
|
|
for (; curr_cpu!=NULL; curr_cpu=curr_cpu->next)
|
|
{
|
|
FILE *fp;
|
|
char *filename;
|
|
int supported = 0;
|
|
|
|
fprintf(stderr, "Setting CPU scaling governor \"%s\" for CPU %d...\n", governor, curr_cpu->cpu_number);
|
|
|
|
/* check wether this CPU supports the specified governor */
|
|
filename = StrApp((char**)NULL, curr_cpu->dir_name, SYSFS_CPUFREQ_AVAIL_GOV, (char*)NULL);
|
|
fp = fopen(filename, "r");
|
|
if (!fp)
|
|
{
|
|
fprintf(stderr, "could not open file %s!\n", filename);
|
|
free(filename);
|
|
retval = 0;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
size_t len = 0;
|
|
char *line = NULL;
|
|
char *test;
|
|
int cont = 0;
|
|
|
|
if (getline(&line, &len, fp) == -1)
|
|
{
|
|
fprintf(stderr, "could not read from file %s!\n", filename);
|
|
free(filename);
|
|
fclose(fp);
|
|
retval = 0;
|
|
continue;
|
|
}
|
|
fclose(fp);
|
|
|
|
test = (char *) calloc(strlen(line)+1, sizeof(char));
|
|
if (!test)
|
|
{
|
|
fprintf(stderr, "failure allocating memory!\n");
|
|
abort();
|
|
}
|
|
|
|
while (sscanf(line+cont, "%s", test) != EOF)
|
|
{
|
|
if (!strcmp(test, governor))
|
|
{
|
|
supported = 1;
|
|
break;
|
|
}
|
|
cont = strstr(line, test) - line + strlen(test);
|
|
}
|
|
free(test);
|
|
free(line);
|
|
}
|
|
free(filename);
|
|
if (!supported)
|
|
{
|
|
fprintf(stderr, "sorry, this CPU does not support the speficied CPUfreq governor...\n");
|
|
retval = 0;
|
|
continue;
|
|
}
|
|
|
|
/* OK, let's set the specified governor for this CPU */
|
|
filename = StrApp((char**)NULL, curr_cpu->dir_name, SYSFS_CPUFREQ_SET_GOV, (char*)NULL);
|
|
|
|
fp = fopen(filename, "w");
|
|
if (!fp)
|
|
{
|
|
fprintf(stderr, "could not open file %s for writing!\n", filename);
|
|
free(filename);
|
|
retval = 0;
|
|
continue;
|
|
}
|
|
if (fprintf(fp, "%s", governor) <= 0)
|
|
{
|
|
fprintf(stderr, "failed writing to file %s!\n", filename);
|
|
free(filename);
|
|
fclose(fp);
|
|
retval = 0;
|
|
continue;
|
|
}
|
|
fclose(fp);
|
|
/* OK, we supposedly set our governor on this CPU, let's see wether it actually went through... */
|
|
fp = fopen(filename, "r");
|
|
if (!fp)
|
|
{
|
|
fprintf(stderr, "could not open file %s for reading!\n", filename);
|
|
free(filename);
|
|
retval = 0;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
size_t len = 0;
|
|
char *line = NULL;
|
|
char *test;
|
|
|
|
if (getline(&line, &len, fp) == -1)
|
|
{
|
|
fprintf(stderr, "could not read from file %s!\n", filename);
|
|
free(filename);
|
|
fclose(fp);
|
|
retval = 0;
|
|
continue;
|
|
}
|
|
fclose(fp);
|
|
test = (char *) calloc(strlen(line)+1,sizeof(char));
|
|
if (!test)
|
|
{
|
|
fprintf(stderr, "failure allocating memory!\n");
|
|
abort();
|
|
}
|
|
if (sscanf(line, "%s", test) != 1)
|
|
{
|
|
fprintf(stderr, "could not read from file %s!\n", filename);
|
|
free(filename);
|
|
free(test);
|
|
free(line);
|
|
retval = 0;
|
|
continue;
|
|
}
|
|
free(line);
|
|
if (strcmp(test, governor))
|
|
{
|
|
fprintf(stderr, "failed setting specified CPUfreq governor!\n");
|
|
retval = 0;
|
|
}
|
|
free(test);
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
/* Set cpufreq governor */
|
|
int cpufreq_set_governor(char *governor)
|
|
{
|
|
if (!governor) return 0;
|
|
if (!available_cpus) return 0;
|
|
if (!strlen(governor)) return 0;
|
|
|
|
if (kernel_version == IS_2_6)
|
|
return set_cpufreq_governor_2_6(governor);
|
|
|
|
|
|
/* 2.4 kernels are not supported at the moment */
|
|
|
|
return 0;
|
|
}
|