/* * The Versatile Governor * Copyright (C) 2004 Joseph Pingenot This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Right now, this is a quick hack. More functionality and completeness as we need. * The frequency scaling stuff needs to be moved out to libsys. * */ #include #include #include #include #include #include #include #include #include #include #define DBUS_API_SUBJECT_TO_CHANGE #include /*Our own includes.*/ #include "libproc/libproc.h" #include "vsgov-locator.h" #include "vsgov-acpiscan.h" #include "config.h" /*Global vars. Too inefficient to regenerate them whenever they're needed.*/ char *file_cpuMinFreq; char *file_cpuMaxFreq; char *file_scaleMinFreq; char *file_scaleMaxFreq; char *file_governorFile; char *file_setSpeedFile; /*Linked list of frequencies*/ struct freq { /*The actual frequency*/ long unsigned int frequency; /*This gives how hard it is to dislodge the cpu from a frequency*/ unsigned int weight; struct freq *next; }; /*Stores pointers and such for the timer handler.*/ struct data_param { struct freq *head; long unsigned int min; long unsigned int max; unsigned int numcpus; }; int set_frequency(const char *filename, long unsigned int value); long unsigned int get_frequency(const char *filename); int scan_frequencies(struct freq **freqs); void set_files(); void push_freq(struct freq *head, long unsigned int freq, unsigned int weight); long unsigned int find_nearest_freq(struct freq *head, long unsigned int freq); void write_pid(pid_t pid); static DBusHandlerResult handle_signal(DBusConnection *connection, DBusMessage *message, void *user_data); static gboolean check_n_set(struct data_param *data); int main(void){ struct data_param internal_data; pid_t pid; struct freq *current; GMainLoop *loop; DBusConnection *bus; DBusError error; set_files(); scan_frequencies(&(internal_data.head)); internal_data.numcpus = get_numcpus(); /*printf("valid frequencies\n");*/ current = internal_data.head; internal_data.min = internal_data.max = 0; while(current != NULL){ /*printf(" %lu\n", current->frequency);*/ if((current->frequency < internal_data.min) || (internal_data.min == 0)){ internal_data.min = current->frequency; } if(current->frequency > internal_data.max){ internal_data.max = current->frequency; } current = current->next; } /*fprintf(stderr, "min freq=%lu, max freq=%lu\n", min, max);*/ if(internal_data.max < internal_data.min){ fprintf(stderr, "min freq. greater than max?!\n"); exit(1); } /*open the syslog.*/ openlog(SYSLOGIDENT, SYSLOGOPTS, SYSLOGFAC); //syslog(LOG_WARNING, "vsgovernor: number of cpus is %d.\n", numcpus); /*Now set up the main loop.*/ loop = g_main_loop_new(NULL, FALSE); dbus_error_init(&error); if(!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) { syslog(LOG_ERR, "vsgovernor: unable to listen to the system bus (%s)", error.message); dbus_error_free(&error); exit(2); } fprintf(stderr, "bus is %x\n", (int)bus); /*Now we can switch to daemon (before this, we should be ready for most stuff!*/ if((pid = fork()) != 0){ /*We're the parent. We write the child's id out to the file, and exit.*/ write_pid(pid); exit(0); } dbus_connection_setup_with_g_main(bus, NULL); dbus_bus_add_match(bus, "type='signal',interface='net.digitasaru.vsgov.Signal'", &error); dbus_connection_add_filter(bus, handle_signal, loop, NULL); /*Set up the timeout*/ g_timeout_add(DEFSLEEPTIME, (GSourceFunc)check_n_set, &internal_data); /*And GO!*/ g_main_loop_run(loop); closelog(); return 0; } /*Handle user requests.*/ static DBusHandlerResult handle_signal(DBusConnection *connection, DBusMessage *message, void *user_data) { DBusError error; char *msg; dbus_error_init(&error); if(!(dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &msg, DBUS_TYPE_INVALID))) { syslog(LOG_WARNING, "got user message, but got an error decoding it! (%s)", error.message); dbus_error_free(&error); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } syslog(LOG_WARNING, "got user message (%s)", msg); dbus_free(msg); return DBUS_HANDLER_RESULT_HANDLED; } /*Stuff to do on each cycle*/ static gboolean check_n_set(struct data_param *data) { long unsigned freq; long unsigned idealfreq; long unsigned currentfreq; unsigned int cpu; unsigned int tstate; struct libproc_loadavg *load; struct libproc_acpi_cputhrottling* throttle; int err; /*Get the current load average.*/ load = libproc_getloadavg(); if(load == NULL){ syslog(LOG_WARNING, "vsgovernor: unable to get load average!\n"); return FALSE; } currentfreq = get_frequency(file_setSpeedFile); idealfreq = (data->max - data->min)*(load->two) + data->min; //fprintf(stderr, " ideal=%lu\n", idealfreq); freq = find_nearest_freq(data->head, idealfreq); if(load->one > 1.0) load->one = 1.0; /*syslog(LOG_INFO, " ideal=%lu, freq=%lu, current=%lu, load=%f\n", idealfreq, freq, currentfreq, load->two);*/ if(freq != currentfreq) set_frequency(file_setSpeedFile, freq); /*Now for the cpu throttling. For now, this is keyed off of the first load num.*/ for(cpu = 0; cpu < data->numcpus; cpu++) { //syslog(LOG_WARNING, "vsgovernor: cpu %d\n", cpu); /*Get the throttling info for it.*/ if((throttle = libproc_acpi_get_cputhrottling(cpu, &err)) != NULL) { /*got throttle info. What is the best throttle for this load?*/ tstate = locate_throttling(throttle, load->one); //syslog(LOG_WARNING, "vsgovernor: recommended tstate %d\n", tstate); /*Now set that tstate*/ if(tstate != throttle->active) { err = libproc_acpi_set_cpulimit(cpu, 0, tstate); //syslog(LOG_WARNING, "vsgovernor: set cpulimit(%d, %d, %d), returned %d.\n", cpu, 0, tstate, err); //}else{ //syslog(LOG_WARNING, "vsgovernor: setting limit unnecessary.\n"); } /*now we don't need the throttle info, so free the struct.*/ libproc_acpi_freethrottling(throttle); //}/*If we couldn't get throttle information, just keep it where it is.*/else{ //syslog(LOG_WARNING, "vsgovernor: unable to get throttle data\n"); } } libproc_destroy_loadavg(load); load = NULL; return TRUE; } void write_pid(pid_t pid){ FILE *file; file = fopen(DEFPIDFILE, "w"); if(file == NULL){ /*Error. Write an error and exit abnormally.*/ fprintf(stderr, "vsgovernor: error opening pidfile %s for writing (%d: %s)\n", DEFPIDFILE, errno, strerror(errno)); fprintf(stderr, "vsgovernor: child pid is %u\n", pid); exit(2); } fprintf(file, "%u", pid); fclose(file); } void set_files(){ /*For now, we just need a quick and dirty fix.*/ file_cpuMinFreq = (char *)malloc(1024); file_cpuMaxFreq = (char *)malloc(1024); file_scaleMinFreq = (char *)malloc(1024); file_scaleMaxFreq = (char *)malloc(1024); file_governorFile = (char *)malloc(1024); file_setSpeedFile = (char *)malloc(1024); snprintf(file_cpuMinFreq, 1024, "%s/%s/%s/%s", FREQFILEBASE, DEFAULTCPU, FREQSUBDIR, CPUMINFREQFILE); snprintf(file_cpuMaxFreq, 1024, "%s/%s/%s/%s", FREQFILEBASE, DEFAULTCPU, FREQSUBDIR, CPUMAXFREQFILE); snprintf(file_scaleMinFreq, 1024, "%s/%s/%s/%s", FREQFILEBASE, DEFAULTCPU, FREQSUBDIR, MINFREQFILE); snprintf(file_scaleMaxFreq, 1024, "%s/%s/%s/%s", FREQFILEBASE, DEFAULTCPU, FREQSUBDIR, MAXFREQFILE); snprintf(file_governorFile, 1024, "%s/%s/%s/%s", FREQFILEBASE, DEFAULTCPU, FREQSUBDIR, CPUGOVERNORFILE); snprintf(file_setSpeedFile, 1024, "%s/%s/%s/%s", FREQFILEBASE, DEFAULTCPU, FREQSUBDIR, SETSPEEDFILE); } /*Take ownership*/ void set_governor(){ int fd; } /*Scan the frequencies to see what are all available.*/ int scan_frequencies(struct freq **freqs) { /*First, get the frequencies available.*/ long unsigned int current, min, max, readfreq; long unsigned int original_min, original_max; /*First, set min and max frequencies*/ min = get_frequency(file_cpuMinFreq); max = get_frequency(file_cpuMaxFreq); original_min = get_frequency(file_scaleMinFreq); original_max = get_frequency(file_scaleMaxFreq); set_frequency(file_scaleMinFreq, min); set_frequency(file_scaleMaxFreq, max); /*Initialize the list.*/ *freqs = NULL; /*Now, scan through.*/ for(current = min; current <= max; current += FREQSTEP){ /*printf("frequency: %lu\n", current);*/ set_frequency(file_setSpeedFile, current); if(get_frequency(file_setSpeedFile) == current){ /*printf(" recognized!\n");*/ /*This one is valid; it stuck*/ if(*freqs == NULL){ *freqs = (struct freq*)malloc(sizeof(struct freq)); (*freqs)->frequency = current; (*freqs)->weight = 1; (*freqs)->next = NULL; }else{ push_freq(*freqs, current, 1); } } } /*Now reset the limits.*/ set_frequency(file_scaleMinFreq, original_min); set_frequency(file_scaleMaxFreq, original_max); return 0; } void push_freq(struct freq *head, long unsigned int freq, unsigned int weight){ if(head->next == NULL){ head->next = (struct freq*)malloc(sizeof(struct freq)); head->next->frequency = freq; head->next->weight = weight; head->next->next = NULL; }else{ push_freq(head->next, freq, weight); } } struct freq *_find_nearest_freq(struct freq *head, long unsigned int freq); /*Fairly simple, yet elegant; we start at the bottom of the list * comparing the closest value later down the list to the closest * value at this point. The closer wins and gets passed down. */ long unsigned int find_nearest_freq(struct freq *head, long unsigned int freq) { /*This is a wrapper function, so that we can modify the details in the actual function * with impugnity*/ long unsigned int frequency; if(head == NULL) { /*Default to the lowest frequency possible.*/ return 0; } //printf("in find_nearest_freq\n"); frequency= (_find_nearest_freq(head, freq))->frequency; //printf(" frequency is found to be %d.\n", frequency); return frequency; } /*Weight arg is so that the next-down recurse can pass back weight info*/ struct freq *_find_nearest_freq(struct freq *head, long unsigned int freq) { struct freq *nearest; //fprintf(stderr, " *freq=%lu, head weight=%u head freq=%lu\n", freq, (head == NULL)?-1:head->weight, (head == NULL)?-1:head->frequency); if(head->next == NULL){ /*This *is* the nearest, at least for now. *This way, we have at least one valid value.*/ //fprintf(stderr, " head; frequency=%lu\n", head->frequency); /*We have to make sure we pass back the weight.*/ return head; }else{ nearest = _find_nearest_freq(head->next, freq); //fprintf(stderr, " Not head; freq=%lu, nearest=%lu, frequency=%lu\n", freq, nearest->frequency, head->frequency); if((head->weight == 0) || ((nearest->weight != 0) && (abs(freq - (nearest->frequency)/(nearest->weight)) < abs(freq - (head->frequency)/(head->weight))))){ //fprintf(stderr, " Nearest!\n"); return nearest; }else{ //fprintf(stderr, " Head!\n"); return head; } } } long unsigned int get_frequency(const char *filename){ FILE *file; int err = 0; long unsigned int value; char errstring[1024]; if((file = fopen(filename, "r")) == NULL){ err=errno; if(strerror_r(err, errstring, 1024) == 0){ fprintf(stderr, "vsgovernor/get_frequency: unable to open file (%s) for reading (error %d: %s)\n", filename, err, errstring); }else{ fprintf(stderr, "vsgovernor/get_frequency: unable to open file (%s) for reading (error %d)\n", filename, err); } return 0; } /*Alright; the file's open and we have RAM for it. Snag the info.*/ err = fscanf(file, "%lu", &value); if(err != 1){ fprintf(stderr, "vsgovernor/get_frequency: format in file (%s) unrecognized!\n", filename); return 0; } fclose(file); return value; } int set_frequency(const char *filename, long unsigned int value){ FILE *file; int err; if((file = fopen(filename, "w")) == NULL){ fprintf(stderr, "vsgovernor/set_frequency: unable to open file (%s) for writing (error %d: %s)\n", filename, errno, strerror(errno)); return 0; } /*Alright; the file's open and we have RAM for it. Snag the info.*/ err = fprintf(file, "%lu", value); fclose(file); return 0; }