/*************************************************************************** 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 #include #include #include #include #include #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; }