Hi,
CPU frequency scaling is the last remaining important power management
technology that should be controllable from the desktop IMO, but that's
not possible yet. Or there's at least no common interface/ software
everybody agrees on.
Richard already proposed something similar in the past. His idea was to
control the different frequencies a CPU supports from higher level
applications on the desktop. His proposal was rejected, though.
So I'm doing a second apprach and I've got a different way of solving the
problem. It's implemented as a HAL addon which summarizes all cpufreq
capable CPUs into six DBus methods on the
org.freedesktop.Hal.Device.SystemPowerManagement interface.
Addon-cpufreq supports kernel governors and also implements a userspace
controlling mechanism. That makes it the "all you need for CPUFreq"
application. Furthermore, to not make things unnecessarily complicated for
desktop applications, it is supposed to abstract all the different
settings you can make for the different governors. One thing which isn't
available yet, is the possibility to control the performance of
dynamically scaled CPUs. There's a method to set the performance (from 1
to 100) which can be easily used from other applications with showing a
nice progress bar or the like. That makes it unique in contrast to former
solutions.
The biggest advantage is that you get CPUFreq out of the box on every
system supporting HAL without the need to install another daemon. Another
one is that desktop applications like gnome-power-manager or kpowersave
can use one common interface after all.
It claims the following DBus interfaces on the
/org/freedesktop/Hal/devices/computer device:
Set a specific CPU frequency governor
-------------------------------------
Interface: org.freedesktop.Hal.Device.SystemPowerManagement
Member : "SetCPUFreqGovernor"
Type : Method call with return
Param:
string : The governor to be set. This can be any arbitary string. Also
governors which are not known can be set
Return type:
int : 0 on success, 1 on error
Set the performance independently from the current governor
-----------------------------------------------------------
Interface: org.freedesktop.Hal.Device.SystemPowerManagement
Member : "SetCPUFreqPerformance"
Type : Method call with return
Param:
int : Value between 1 and 100. The higher the value the more
performance you get. 50 is default and should be sufficient
in most cases
Return type:
int : 0 on success, 1 on error or if performance settings are not
supported for the current governor
Set if niced processes should be consider when calculating CPU load
--------------------------------------------------------------------
Interface: org.freedesktop.Hal.Device.SystemPowerManagement
Member : "SetCPUFreqConsiderNice"
Type : Method call with return
Param:
boolean: True if niced processes should be considered, false otherwise
Return type:
int : 0 on success, 1 on error or if this setting is not supported
with the current governor
Get the current active governor
-------------------------------
Interface: org.freedesktop.Hal.Device.SystemPowerManagement
Member : "GetCPUFreqGovernor"
Type : Method call with return
Return type:
string : The current active governor
Get the current performance setting
-----------------------------------
Interface: org.freedesktop.Hal.Device.SystemPowerManagement
Member : "GetCPUFreqPerformance"
Type : Method call with return
Return type:
int : The current performance setting from 1 to 100
Get the current consider nice setting
-------------------------------------
Interface: org.freedesktop.Hal.Device.SystemPowerManagement
Member : "GetCPUFreqConsiderNice"
Type : Method call with return
Return type:
boolean: True if niced processes are considered, false otherwise
The addon basically consists of five parts:
1. One interface which contains all methods which are common for all
governors (see DBus interfaces above)
2. Some helper functions
3. The userspace governor implementation with the CPULoad calculation
mechanism
4. The ondemand governor implementation
5. DBus/HAL integration
Current addons only consist of one source file. However, I don't think
that makes sense for addon-cpufreq. If this would be a independent
project, I would have separated these parts into more different files. But
for now, I've just put the userspace part into two files (header, source)
and the remaining parts into another two files (header, source). It would
of course be possible to only have one source file and thus get rid of the
header files if this is desired.
I've appended a somewhat patch to accomplish all this. Please comment...
If you guys don't agree that this could be added to HAL as an addon, maybe
the above mentioned interfaces can be implemented by installing some
scripts like hal-system-power-cpufreq-set-governor. Then I'm trying to get
org.freedesktop.CPUFreq for a separate daemon and can call the DBus
methods inside them. All other people can then of course do whatever they
like in those scripts.
Regards,
Holger
diff -uNr -x CVS hal/fdi/policy/10osvendor/10-power-mgmt-policy.fdi hal.new/fdi/policy/10osvendor/10-power-mgmt-policy.fdi
--- hal/fdi/policy/10osvendor/10-power-mgmt-policy.fdi 2006-06-07 01:56:09.000000000 +0200
+++ hal.new/fdi/policy/10osvendor/10-power-mgmt-policy.fdi 2006-07-05 16:06:23.000000000 +0200
@@ -52,5 +52,12 @@
<append key="org.freedesktop.Hal.Device.SystemPowerManagement.method_execpaths" type="strlist">hal-system-power-set-power-save</append>
</match>
</device>
+
+ <device>
+ <match key="info.udi" string="/org/freedesktop/Hal/devices/computer">
+ <append key="info.addons" type="strlist">hald-addon-cpufreq</append>
+ </match>
+ </device>
+
</deviceinfo>
diff -uNr -x CVS hal/hald/linux2/addons/addon-cpufreq.c hal.new/hald/linux2/addons/addon-cpufreq.c
--- hal/hald/linux2/addons/addon-cpufreq.c 1970-01-01 01:00:00.000000000 +0100
+++ hal.new/hald/linux2/addons/addon-cpufreq.c 2006-07-05 16:03:12.000000000 +0200
@@ -0,0 +1,900 @@
+/***************************************************************************
+ * *
+ * addon-cpufreq.c *
+ * *
+ * Copyright (C) 2006 SUSE Linux Products GmbH *
+ * *
+ * Author(s): Holger Macht <[EMAIL PROTECTED]> *
+ * *
+ * 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 you *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA *
+ * *
+ ***************************************************************************/
+
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <getopt.h>
+
+#include "addon-cpufreq.h"
+#include "addon-cpufreq-userspace.h"
+#include "libhal/libhal.h"
+
+#define MAX_LINE_SIZE 255
+#define PROC_CPUINFO_FILE "/proc/cpuinfo"
+#define DBUS_INTERFACE "org.freedesktop.Hal.Device.SystemPowerManagement"
+
+
+const char SYSFS_GOVERNOR_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_governor";
+
+const char ONDEMAND_UP_THRESHOLD_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/ondemand/up_threshold";
+
+const char SYSFS_AFFECTED_CPUS_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/affected_cpus";
+
+const char SYSFS_CPU_ONLINE_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/online";
+
+const char ONDEMAND_IGNORE_NICE_LOAD_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/ondemand/ignore_nice_load";
+
+/** list holding all cpufreq objects (userspace, ondemand, etc.) */
+static GSList *cpufreq_objs = NULL;
+
+/******************** helper functions **********************/
+static gboolean read_line_int(const char *filename, int *val)
+{
+ char line[MAX_LINE_SIZE + 1];
+
+ if (!read_line(filename, line, MAX_LINE_SIZE)) {
+ ERROR("Could not read from %s", filename);
+ return FALSE;
+ }
+
+ /* strip trailing '\n' */
+ line[strlen(line) - 1] = '\0';
+ *val = atoi(line);
+
+ return TRUE;
+}
+
+/** reads one line from filename with the given length */
+gboolean read_line(const char *filename, char *line, unsigned len)
+{
+ FILE *fp = fopen(filename, "r");
+ if (!fp) {
+ ERROR("Could not open '%s': %s", filename, strerror(errno));
+ return FALSE;
+ }
+ if ((!fgets(line, len, fp))) {
+ ERROR("Could not read from '%s': %s", filename, strerror(errno));
+ fclose(fp);
+ return FALSE;
+ }
+ fclose(fp);
+ return TRUE;
+}
+
+/** writes one line with the given format to filename */
+gboolean write_line(const char *filename, const char *fmt, ...)
+{
+ va_list ap;
+ FILE *fp;
+
+ fp = fopen(filename, "w+");
+ if (!fp) {
+ ERROR("Could not open file for writing: %s; %s", filename,
+ strerror(errno));
+ return FALSE;
+ }
+
+ va_start(ap, fmt);
+
+ if (vfprintf(fp, fmt, ap) < 0) {
+ ERROR("Could not write to file: %s", filename);
+ fclose(fp);
+ return FALSE;
+ }
+
+ va_end(ap);
+ fclose(fp);
+ return TRUE;
+}
+
+/** @brief returns the number of CPUs
+ *
+ * @return number of CPUs or < 0 on error
+ * @retval > 0 number of CPUs
+ * @retval -1 general error
+ * @retval -2 no CPUs found
+ */
+int get_cpu_count(void)
+{
+ FILE *fp;
+ char line[256];
+ int count = 0;
+
+ fp = fopen(PROC_CPUINFO_FILE, "r");
+ if (fp == NULL) {
+ ERROR("Could not open %s: %s", PROC_CPUINFO_FILE, strerror(errno));
+ return -1;
+ }
+
+ /* search how many lines start with processor in /proc/cpuinfo */
+ while (fgets(line, MAX_LINE_SIZE, fp) != NULL) {
+ if (!memcmp(line, "processor", 9))
+ count++;
+ }
+ fclose(fp);
+
+ if (count > 0)
+ return count;
+ else {
+ ERROR("No processor found");
+ return -2;
+ }
+}
+
+/** reads one line from filename, splits its integers by delim and stores
+ * all items in the given list */
+gboolean read_line_split(GString *filename, gchar delim, GSList **list)
+{
+ gchar line[MAX_LINE_SIZE];
+ gchar **l;
+ int i;
+
+ if(!read_line(filename->str, line, MAX_LINE_SIZE)){
+ ERROR("Could not read splitted line from %s", filename->str);
+ return FALSE;
+ }
+
+ /* strip trailing '\n' */
+ line[strlen(line)-1] = '\0';
+
+ l = g_strsplit(line, &delim, MAX_LINE_SIZE);
+
+ for (i = 0; l[i] != NULL; i++) {
+ int value = atoi(l[i]);
+
+ if (g_strcasecmp(l[i], "") != 0) {
+ *list = g_slist_append(*list, GINT_TO_POINTER(value));
+ }
+ }
+ g_strfreev(l);
+
+ return TRUE;
+}
+
+/** gets a two dimensional list of integers and sorts out duplicates */
+static void cpu_list_unique(gpointer data, gpointer whole_list)
+{
+ GSList **list = (GSList**)whole_list;
+ GSList *current= (GSList*)data;
+ GSList *it = NULL;
+
+ for (it = *list; it != NULL; it = g_slist_next(it)) {
+ gboolean equal = TRUE;
+ if (current == it->data)
+ continue;
+
+ GSList *list_it = NULL;
+ GSList *current_it = NULL;
+ for (list_it = it->data, current_it = current;
+ list_it != NULL && current_it != NULL;
+ list_it = g_slist_next(list_it), current_it = g_slist_next(current_it)) {
+
+ dbg("comparing %d with %d", (int)current_it->data, (int)list_it->data);
+ if ((int)current_it->data != (int)list_it->data)
+ equal = FALSE;
+ }
+
+ dbg("equal? %s, %d", equal ? "yes" : "no", equal);
+ if (equal) {
+ dbg("remove: %d", g_slist_length(*list));
+ *list = g_slist_remove(*list, current);
+ dbg("remove_2: %d", g_slist_length(*list));
+ return;
+ }
+ }
+}
+
+/** @brief gets the CPUs and their dependencies */
+static gboolean get_cpu_dependencies(GSList **cpu_list, int num_cpus)
+{
+ int i;
+
+ for (i = 0; i < num_cpus; i++) {
+ GSList *int_cpus = NULL;
+ GSList *affected_cpus = NULL;
+ GSList *it = NULL;
+ GString *affected_cpus_file = g_string_new("");
+
+ g_string_printf(affected_cpus_file, SYSFS_AFFECTED_CPUS_FILE, i);
+
+ if (!read_line_split(affected_cpus_file, ' ', &affected_cpus))
+ return FALSE;
+ g_string_free(affected_cpus_file, TRUE);
+
+ if (affected_cpus == NULL)
+ return FALSE;
+
+ for (it = affected_cpus; it != NULL; it = g_slist_next(it)) {
+ int_cpus = g_slist_append(int_cpus,
+ GINT_TO_POINTER(affected_cpus->data));
+ }
+ g_slist_free(affected_cpus);
+
+ if (!g_slist_length(int_cpus)) {
+ ERROR("failed to get affected_cpus for cpu %d", i);
+ continue;
+ }
+
+ *cpu_list = g_slist_append(*cpu_list, int_cpus);
+ }
+
+ dbg("Number of CPUs before uniquing cpu_list: %d", g_slist_length(*cpu_list));
+ g_slist_foreach(*cpu_list, (GFunc)cpu_list_unique, cpu_list);
+ dbg("Number of CPUs after uniquing cpu_list: %d", g_slist_length(*cpu_list));
+
+ if (g_slist_length(*cpu_list) == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+/** check if given CPU starting from 0 is online */
+gboolean cpu_online(int cpu_id)
+{
+ gboolean online;
+ char online_str[2];
+
+ GString *online_file = g_string_new("");
+ g_string_printf(online_file, SYSFS_CPU_ONLINE_FILE, cpu_id);
+
+ if (access(online_file->str, F_OK) < 0) {
+ online = TRUE;
+ goto Out;
+ }
+
+ if (!read_line(online_file->str, online_str, 2)) {
+ ERROR("Unable to open file: %s", online_file->str);
+ online = FALSE;
+ goto Out;
+ }
+
+ online = atoi(online_str);
+
+ if (!online)
+ online = FALSE;
+Out:
+ g_string_free(online_file, TRUE);
+ return online;
+}
+
+/** writes the new_governor string into the sysfs interface */
+gboolean write_governor(char *new_governor, int cpu_id)
+{
+ gboolean ret = TRUE;
+ GString *governor_file = g_string_new("");
+ char governor[MAX_LINE_SIZE + 1];
+
+ if (!cpu_online(cpu_id))
+ goto Out;
+
+ g_string_printf(governor_file, SYSFS_GOVERNOR_FILE, cpu_id);
+ dbg("Trying ot write governor %s", new_governor);
+ if (!write_line(governor_file->str, "%s", new_governor)) {
+ ret = FALSE;
+ goto Out;
+ }
+
+ /* check if governor has been set */
+ usleep(1000);
+ read_line(governor_file->str, governor, MAX_LINE_SIZE);
+ if (strstr(governor, new_governor))
+ ret = TRUE;
+ else
+ ret = FALSE;
+Out:
+ g_string_free(governor_file, TRUE);
+ return ret;
+}
+/******************** helper functions end ********************/
+
+/********************* ondemand interface *********************/
+#define ONDEMAND_STRING "ondemand"
+
+struct ondemand_interface {
+ int base_cpu;
+};
+
+static gboolean ondemand_set_performance(void *data, int performance)
+{
+ struct ondemand_interface *iface = data;
+ GString *up_threshold_file = g_string_new("");
+
+ g_string_printf(up_threshold_file, ONDEMAND_UP_THRESHOLD_FILE, iface->base_cpu);
+
+ if(!write_line(up_threshold_file->str, "%u", performance)){
+ ERROR("Could not set up_threshold to %u kHz; %s", performance,
+ strerror(errno));
+ return FALSE;
+ }
+
+ dbg("Up threshold set to %d for ondemand", performance);
+ g_string_free(up_threshold_file, TRUE);
+
+ return TRUE;
+}
+
+static int ondemand_get_performance(void)
+{
+ GString *governor_file = g_string_new("");
+ int performance = -1;
+
+ g_string_printf(governor_file, ONDEMAND_UP_THRESHOLD_FILE, 0);
+
+ if (!read_line_int(governor_file->str, &performance)) {
+ ERROR("Could not read up_threshold");
+ return -1;
+ }
+
+ g_string_free(governor_file, TRUE);
+
+ return performance;
+}
+
+static gboolean ondemand_set_consider_nice(void *data, gboolean consider)
+{
+ struct ondemand_interface *iface = data;
+ GString *consider_file = g_string_new("");
+
+ g_string_printf(consider_file, ONDEMAND_IGNORE_NICE_LOAD_FILE, iface->base_cpu);
+
+ if(!write_line(consider_file->str, "%u", consider)){
+ ERROR("Could not set ignore_nice_load to: %u kHz; %s", consider,
+ strerror(errno));
+ return FALSE;
+ }
+
+ dbg("Set consider nice to %d for ondemand", consider);
+ g_string_free(consider_file, TRUE);
+
+ return TRUE;
+}
+
+static gboolean ondemand_get_consider_nice(void)
+{
+ GString *governor_file = g_string_new("");
+ gboolean consider = -1;
+
+ /* only read the setting of cpu0 */
+ g_string_printf(governor_file, ONDEMAND_IGNORE_NICE_LOAD_FILE, 0);
+
+ if (!read_line_int(governor_file->str, &consider)) {
+ ERROR("Could not read ignore_nice_load file");
+ return -1;
+ }
+ g_string_free(governor_file, TRUE);
+
+ return consider;
+}
+
+static gboolean ondemand_init(struct ondemand_interface *iface, GSList *cores)
+{
+ if (iface == NULL)
+ return FALSE;
+
+ if (!write_governor(ONDEMAND_STRING, (int)cores->data)) {
+ ERROR("Could not set ondemand governor.");
+ return FALSE;
+ }
+
+ iface->base_cpu = (int)cores->data;
+
+ return TRUE;
+}
+
+static void ondemand_free(void *data)
+{
+ return;
+}
+
+/********************* ondemand end *********************/
+
+/********************* main interface *********************/
+static gboolean set_performance(int performance)
+{
+ float steps;
+ float up_threshold;
+
+ if (performance < 1)
+ performance = 1;
+ if (performance > 100)
+ performance = 100;
+
+ if (performance >= 50) {
+ steps = UP_THRESHOLD_BASE - UP_THRESHOLD_MIN + 1;
+ up_threshold = (UP_THRESHOLD_BASE) - (((float)performance - 50.0) *
+ (steps / 51.0));
+ performance = (int)up_threshold;
+ } else if (performance < 50) {
+ steps = UP_THRESHOLD_MAX - UP_THRESHOLD_BASE;
+ up_threshold = (UP_THRESHOLD_MAX + 1) - ((float)performance *
+ (steps / 49.0));
+ performance = (int)up_threshold;
+ }
+
+ GSList *it = NULL;
+ for (it = cpufreq_objs; it != NULL; it = g_slist_next(it)) {
+ struct cpufreq_obj *obj = it->data;
+ obj->set_performance(obj->iface, performance);
+ }
+ return TRUE;
+}
+
+static int get_performance(void)
+{
+ struct cpufreq_obj *obj;
+ float steps;
+ float performance;
+ int up_threshold;
+
+ if (cpufreq_objs == NULL)
+ return -1;
+ obj = cpufreq_objs->data;
+
+ up_threshold = obj->get_performance();
+
+ if (up_threshold < UP_THRESHOLD_BASE) {
+ steps = UP_THRESHOLD_BASE - UP_THRESHOLD_MIN + 1;
+ performance = (((UP_THRESHOLD_BASE) - up_threshold) /
+ (steps / 51.0)) + 50.0;
+ } else if (up_threshold >= UP_THRESHOLD_BASE) {
+ steps = UP_THRESHOLD_MAX - UP_THRESHOLD_BASE;
+ performance = ((UP_THRESHOLD_MAX + 1) - up_threshold) /
+ (steps / 49.0);
+ }
+
+ return (int)performance;
+}
+
+static gboolean set_consider_nice(gboolean consider)
+{
+ GSList *it = NULL;
+ for (it = cpufreq_objs; it != NULL; it = g_slist_next(it)) {
+ struct cpufreq_obj *obj= it->data;
+ obj->set_consider_nice(obj->iface, consider);
+ }
+ return TRUE;
+}
+
+static gboolean get_consider_nice(void)
+{
+ struct cpufreq_obj *obj;
+
+ if (cpufreq_objs == NULL)
+ return -1;
+ obj = cpufreq_objs->data;
+ return obj->get_consider_nice();
+}
+
+static gboolean set_governors(const char *governor)
+{
+ GSList *cpus = NULL;
+ GSList *it = NULL;
+ int num_cpus;
+ static int g_source_id = -1;
+
+ /** clear all previous cpufreq_objs */
+ if (g_slist_length(cpufreq_objs) > 0) {
+ GSList *it = NULL;
+ for (it = cpufreq_objs; it != NULL; it = g_slist_next(it)) {
+ struct cpufreq_obj *obj = it->data;
+ obj->free(obj->iface);
+ free(obj->iface);
+ free(obj);
+ }
+ g_slist_free(cpufreq_objs);
+ cpufreq_objs = NULL;
+ g_source_remove(g_source_id);
+ g_source_id = -1;
+ }
+
+ num_cpus = get_cpu_count();
+ if (num_cpus < 0) {
+ ERROR("No CPUs found in system");
+ return FALSE;
+ }
+
+ if (!get_cpu_dependencies(&cpus, num_cpus)) {
+ ERROR("Could not figure out cpu core dependencies");
+ return FALSE;
+ }
+
+ if (!strcmp(governor, USERSPACE_STRING)) {
+ struct cpufreq_obj *cpufreq_obj;
+ struct userspace_interface *iface;
+
+ for (it = cpus; it != NULL; it = g_slist_next(it)) {
+ cpufreq_obj = malloc(sizeof(struct cpufreq_obj));
+ iface = malloc(sizeof(struct userspace_interface));
+
+ if (userspace_init(iface, it->data)) {
+ cpufreq_obj->iface = iface;
+ cpufreq_obj->set_performance = userspace_set_performance;
+ cpufreq_obj->get_performance = userspace_get_performance;
+ cpufreq_obj->set_consider_nice = userspace_set_consider_nice;
+ cpufreq_obj->get_consider_nice = userspace_get_consider_nice;
+ cpufreq_obj->free = userspace_free;
+ cpufreq_objs = g_slist_append(cpufreq_objs, cpufreq_obj);
+ dbg("added userspace interface");
+ } else
+ return FALSE;
+ }
+ g_source_id = g_timeout_add(USERSPACE_POLL_INTERVAL,
+ (GSourceFunc)userspace_adjust_speeds,
+ cpufreq_objs);
+
+ } else if (!strcmp(governor, ONDEMAND_STRING)) {
+ struct cpufreq_obj *cpufreq_obj;
+ struct ondemand_interface *iface;
+
+ for (it = cpus; it != NULL; it = g_slist_next(it)) {
+ cpufreq_obj = malloc(sizeof(struct cpufreq_obj));
+ iface = malloc(sizeof(struct ondemand_interface));
+
+ if (ondemand_init(iface, it->data)) {
+ cpufreq_obj->iface = iface;
+ cpufreq_obj->set_performance = ondemand_set_performance;
+ cpufreq_obj->get_performance = ondemand_get_performance;
+ cpufreq_obj->set_consider_nice = ondemand_set_consider_nice;
+ cpufreq_obj->get_consider_nice = ondemand_get_consider_nice;
+ cpufreq_obj->free = ondemand_free;
+ cpufreq_objs = g_slist_append(cpufreq_objs, cpufreq_obj);
+ dbg("added ondemand interface");
+ } else
+ return FALSE;
+ }
+ } else {
+ for (it = cpus; it != NULL; it = g_slist_next(it)) {
+ if (!write_governor((char*)governor,
+ (int)((GSList*)it->data)->data)) {
+ ERROR("Could not set %s governor.", governor);
+ return FALSE;
+ }
+ }
+ }
+
+ set_performance(DEFAULT_PERFORMANCE);
+
+ return TRUE;
+}
+
+static gboolean get_governors(char *governor)
+{
+ GString *governor_file = g_string_new("");
+ int cpu_id = 0;
+
+ g_string_printf(governor_file, SYSFS_GOVERNOR_FILE, cpu_id);
+
+ if (!read_line(governor_file->str, governor, MAX_LINE_SIZE)) {
+ ERROR("Could not read current governor");
+ return FALSE;
+ }
+ g_string_free(governor_file, TRUE);
+
+ /* strip trailing '\n' */
+ governor[strlen(governor)-1] = '\0';
+
+ return TRUE;
+}
+/********************* main interface end *********************/
+
+/********************* DBus stuff *********************/
+static gboolean dbus_send_reply_int(DBusConnection *connection, DBusMessage *message,
+ int result)
+{
+ DBusMessage *reply;
+
+ if ((reply = dbus_message_new_method_return(message)) == NULL) {
+ ERROR("Could not allocate memory for the DBus reply");
+ return FALSE;
+ }
+
+ dbus_message_append_args(reply, DBUS_TYPE_INT32, &result, DBUS_TYPE_INVALID);
+
+ if (!dbus_connection_send(connection, reply, NULL)) {
+ ERROR("Could not sent reply");
+ return FALSE;
+ }
+ dbus_connection_flush(connection);
+ dbus_message_unref(reply);
+
+ return TRUE;
+}
+
+static gboolean dbus_send_reply_string(DBusConnection *connection, DBusMessage *message,
+ char *result)
+{
+ DBusMessage *reply;
+
+ if ((reply = dbus_message_new_method_return(message)) == NULL) {
+ ERROR("Could not allocate memory for the DBus reply");
+ return FALSE;
+ }
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &result, DBUS_TYPE_INVALID);
+
+ if (!dbus_connection_send(connection, reply, NULL)) {
+ ERROR("Could not sent reply");
+ return FALSE;
+ }
+ dbus_connection_flush(connection);
+ dbus_message_unref(reply);
+
+ return TRUE;
+}
+
+static gboolean dbus_get_argument_int(DBusMessage *message, DBusError *dbus_error,
+ int *arg)
+{
+ dbus_message_get_args(message, dbus_error, DBUS_TYPE_INT32, arg,
+ DBUS_TYPE_INVALID);
+ if (dbus_error_is_set(dbus_error)) {
+ ERROR("Could not get argument of DBus message: %s",
+ dbus_error->message);
+ dbus_error_free(dbus_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean dbus_get_argument_bool(DBusMessage *message, DBusError *dbus_error,
+ gboolean *arg)
+{
+ dbus_message_get_args(message, dbus_error, DBUS_TYPE_BOOLEAN, arg,
+ DBUS_TYPE_INVALID);
+ if (dbus_error_is_set(dbus_error)) {
+ ERROR("Could not get argument of DBus message: %s",
+ dbus_error->message);
+ dbus_error_free(dbus_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean dbus_get_argument_string(DBusMessage *message, DBusError *dbus_error,
+ char **arg)
+{
+ dbus_message_get_args(message, dbus_error, DBUS_TYPE_STRING, arg,
+ DBUS_TYPE_INVALID);
+ if (dbus_error_is_set(dbus_error)) {
+ ERROR("Could not get argument of DBus message: %s",
+ dbus_error->message);
+ dbus_error_free(dbus_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static DBusHandlerResult dbus_filter_function(DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ DBusError dbus_error;
+
+ const char *member = dbus_message_get_member(message);
+ const char *path = dbus_message_get_path(message);
+ dbg("Received DBus message with member %s", member);
+ dbg("Received DBus message with path %s", path);
+
+ dbus_error_init(&dbus_error);
+ if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "SetCPUFreqGovernor")) {
+ char *arg;
+
+ if (!dbus_get_argument_string(message, &dbus_error, &arg))
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ dbg("Received argument: %s", arg);
+
+ int err_id_u16 = !set_governors(arg);
+ dbg("Sent reply %d\n", err_id_u16);
+ dbus_send_reply_int(connection, message, err_id_u16);
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "SetCPUFreqPerformance")) {
+ int arg;
+
+ if (!dbus_get_argument_int(message, &dbus_error, &arg))
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ dbg("Received argument: %d", arg);
+
+ int err_id_u16 = !set_performance(arg);
+ dbus_send_reply_int(connection, message, err_id_u16);
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "SetCPUFreqConsiderNice")) {
+ gboolean arg;
+
+ if (!dbus_get_argument_bool(message, &dbus_error, &arg))
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ dbg("Received argument: %d", arg);
+
+ int err_id_u16 = !set_consider_nice(arg);
+ dbus_send_reply_int(connection, message, err_id_u16);
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "GetCPUFreqGovernor")) {
+ char governor[MAX_LINE_SIZE + 1];
+
+ if (!get_governors(governor))
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ dbus_send_reply_string(connection, message, governor);
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "GetCPUFreqPerformance")) {
+ dbus_send_reply_int(connection, message, get_performance());
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "GetCPUFreqConsiderNice")) {
+ dbus_send_reply_int(connection, message, get_consider_nice());
+ } else if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL,
+ "Disconnected")) {
+ dbg("DBus daemon disconnected. Trying to reconnect...");
+ dbus_connection_close(connection);
+ dbus_connection_unref(connection);
+ g_timeout_add(5000, (GSourceFunc)dbus_init, NULL);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void dbus_add_method(LibHalContext *halctx, const char *method,
+ const char *signature)
+{
+ libhal_device_property_strlist_append(halctx,
+ "/org/freedesktop/Hal/devices/computer",
+ "org.freedesktop.Hal.Device.SystemPowerManagement.method_names",
+ method,
+ NULL);
+ libhal_device_property_strlist_append(halctx,
+ "/org/freedesktop/Hal/devices/computer",
+ "org.freedesktop.Hal.Device.SystemPowerManagement.method_signatures",
+ signature,
+ NULL);
+}
+
+/** returns FALSE on success because it's used as a callback */
+gboolean dbus_init(void)
+{
+ DBusError dbus_error;
+ DBusConnection *dbus_connection;
+ char *udi = getenv("UDI");
+ LibHalContext *halctx = NULL;
+ GString *governor_file = g_string_new("");
+
+ dbus_error_init(&dbus_error);
+
+ if ((halctx = libhal_ctx_init_direct(&dbus_error)) == NULL) {
+ ERROR("Cannot connect to hald");
+ goto Error;
+ }
+
+ if ((dbus_connection = libhal_ctx_get_dbus_connection(halctx)) == NULL) {
+ ERROR("Cannot get DBus connection");
+ goto Error;
+ }
+
+ if (!libhal_device_claim_interface(halctx, udi,
+ "org.freedesktop.Hal.Device.SystemPowerManagement",
+ " <method name=\"SetCPUFreqGovernor\">"
+ " <arg name=\"governor_string\" direction=\"in\" type=\"s\"/>"
+ " <arg name=\"return_code\" direction=\"out\" type=\"i\"/>"
+ " </method>"
+ " <method name=\"SetCPUFreqPerformance\">"
+ " <arg name=\"value\" direction=\"in\" type=\"i\"/>"
+ " <arg name=\"return_code\" direction=\"out\" type=\"i\"/>"
+ " </method>"
+ " <method name=\"SetCPUFreqConsiderNice\">"
+ " <arg name=\"value\" direction=\"in\" type=\"b\"/>"
+ " <arg name=\"return_code\" direction=\"out\" type=\"i\"/>"
+ " </method>"
+ " <method name=\"GetCPUFreqGovernor\">"
+ " <arg name=\"return_code\" direction=\"out\" type=\"s\"/>"
+ " </method>"
+ " <method name=\"GetCPUFreqPerformance\">"
+ " <arg name=\"return_code\" direction=\"out\" type=\"i\"/>"
+ " </method>"
+ " <method name=\"GetCPUFreqConsiderNice\">"
+ " <arg name=\"return_code\" direction=\"out\" type=\"b\"/>"
+ " </method>",
+ &dbus_error)) {
+
+ ERROR("Cannot claim interface");
+ goto Error;
+ }
+
+ g_string_printf(governor_file, SYSFS_GOVERNOR_FILE, 0);
+ if (access(governor_file->str, F_OK) == 0) {
+ dbus_add_method(halctx, "SetCPUFreqGovernor", "s");
+ dbus_add_method(halctx, "SetCPUFreqPerformance", "i");
+ dbus_add_method(halctx, "SetCPUFreqConsiderNice", "b");
+ dbus_add_method(halctx, "GetCPUFreqGovernor", "");
+ dbus_add_method(halctx, "GetCPUFreqPerformance", "");
+ dbus_add_method(halctx, "GetCPUFreqConsiderNice", "");
+ }
+ g_string_free(governor_file, TRUE);
+
+ dbus_connection_setup_with_g_main(dbus_connection, NULL);
+ dbus_connection_add_filter(dbus_connection, dbus_filter_function, NULL, NULL);
+ dbus_bus_add_match(dbus_connection,
+ "type='method_call',"
+ "interface='" DBUS_INTERFACE "',", NULL);
+ dbus_connection_set_exit_on_disconnect(dbus_connection, 0);
+ return FALSE;
+
+Error:
+ dbus_error_free(&dbus_error);
+ return TRUE;
+}
+/********************* DBus end *********************/
+
+static void exit_handler(int i)
+{
+ GSList *it = NULL;
+ for (it = cpufreq_objs; it != NULL; it = g_slist_next(it)) {
+ struct cpufreq_obj *obj = it->data;
+ obj->free(obj->iface);
+ free(obj->iface);
+ free(obj);
+ }
+ g_slist_free(cpufreq_objs);
+
+ dbg("exit");
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ struct sigaction signal_action;
+ GMainLoop *gmain;
+
+ memset(&signal_action, 0, sizeof(signal_action));
+ sigaddset(&signal_action.sa_mask, SIGTERM);
+ signal_action.sa_flags = SA_RESTART || SA_NOCLDSTOP;
+ signal_action.sa_handler = exit_handler;
+ sigaction(SIGINT, &signal_action, 0);
+ sigaction(SIGQUIT, &signal_action, 0);
+ sigaction(SIGTERM, &signal_action, 0);
+
+ if (dbus_init())
+ ERROR("Could not establish DBus connection");
+
+ if ((getenv("HALD_VERBOSE")) != NULL)
+ is_verbose = TRUE;
+
+ gmain = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(gmain);
+
+ return 0;
+}
diff -uNr -x CVS hal/hald/linux2/addons/addon-cpufreq.h hal.new/hald/linux2/addons/addon-cpufreq.h
--- hal/hald/linux2/addons/addon-cpufreq.h 1970-01-01 01:00:00.000000000 +0100
+++ hal.new/hald/linux2/addons/addon-cpufreq.h 2006-07-05 16:03:15.000000000 +0200
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * *
+ * addon-cpufreq.h *
+ * *
+ * Copyright (C) 2006 SUSE Linux Products GmbH *
+ * *
+ * Author(s): Holger Macht <[EMAIL PROTECTED]> *
+ * *
+ * 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 you *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA *
+ * *
+ ***************************************************************************/
+
+#ifndef ADDON_CPUFREQ_H
+#define ADDON_CPUFREQ_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "../probing/shared.h"
+
+/** UP_THRESHOLD defines at which CPU load (in percent) we switch up */
+#define UP_THRESHOLD_MAX 99
+#define UP_THRESHOLD_MIN 11
+/** this is the kernel default up_threshold */
+#define UP_THRESHOLD_BASE 80
+#define DEFAULT_PERFORMANCE 50
+
+#define ERROR(string, args...) dbg(string, ## args)
+
+struct cpufreq_obj {
+ void *iface;
+ gboolean (*set_performance) (void *data, int);
+ gboolean (*set_consider_nice) (void *data, gboolean);
+ int (*get_performance) (void);
+ gboolean (*get_consider_nice) (void);
+ void (*free) (void *data);
+};
+
+gboolean write_line (const char *filename,
+ const char *fmt, ...);
+
+gboolean read_line (const char *filename,
+ char *line,
+ unsigned len);
+
+gboolean read_line_split (GString *filename,
+ gchar delim,
+ GSList **list);
+
+int get_cpu_count (void);
+
+gboolean cpu_online (int cpu_id);
+
+gboolean write_governor (char *new_governor,
+ int cpu_id);
+
+gboolean dbus_init (void);
+
+#endif /* ADDON_CPUFREQ_H */
diff -uNr -x CVS hal/hald/linux2/addons/addon-cpufreq-userspace.c hal.new/hald/linux2/addons/addon-cpufreq-userspace.c
--- hal/hald/linux2/addons/addon-cpufreq-userspace.c 1970-01-01 01:00:00.000000000 +0100
+++ hal.new/hald/linux2/addons/addon-cpufreq-userspace.c 2006-07-05 16:03:21.000000000 +0200
@@ -0,0 +1,528 @@
+/***************************************************************************
+ * *
+ * addon-cpufreq-userspace.c *
+ * *
+ * Copyright (C) 2006 SUSE Linux Products GmbH *
+ * *
+ * Author(s): Holger Macht <[EMAIL PROTECTED]> *
+ * Speed adjustments based on code by *
+ * Thomas Renninger <[EMAIL PROTECTED]> *
+ * *
+ * 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 you *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA *
+ * *
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "addon-cpufreq.h"
+#include "addon-cpufreq-userspace.h"
+
+/** at which load difference (in percent) we should immediately switch to
+ * the maximum possible frequency */
+#define JUMP_CPUFREQ_LIMIT_MIN 20
+/** the load difference at which we jump up to the maximum freq
+ * immediately is calculated by the UP_THRESHOLD multiplied with this
+ * relation value */
+#define THRESHOLD_JUMP_LIMIT_RELATION 0.625
+/** how many frequency steps we should consider */
+#define HYSTERESIS 5
+#define DEFAULT_CONSIDER_NICE FALSE
+#define PROC_STAT_FILE "/proc/stat"
+
+const char SYSFS_SCALING_SETSPEED_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_setspeed";
+
+const char SYSFS_SCALING_AVAILABLE_FREQS_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_available_frequencies";
+
+/** shortcut for g_array_index */
+#define g_a_i(a,i) g_array_index(a, unsigned, i)
+
+struct userspace_config {
+ int up_threshold;
+ int cpu_high_limit;
+ int consider_nice;
+ int performance;
+};
+
+static struct userspace_config config = { UP_THRESHOLD_MAX,
+ JUMP_CPUFREQ_LIMIT_MIN,
+ DEFAULT_CONSIDER_NICE,
+ DEFAULT_PERFORMANCE };
+
+/********************* CPU load calculation *********************/
+struct cpuload_data {
+ int num_cpus;
+ int *load;
+ unsigned long *last_total_time;
+ unsigned long *last_working_time;
+};
+static struct cpuload_data cpuload = { -1,
+ NULL,
+ NULL,
+ NULL };
+
+/** frees data needed for CPU load calculation */
+void free_cpu_load_data()
+{
+ if (cpuload.num_cpus != -1) {
+ free(cpuload.last_working_time);
+ free(cpuload.last_total_time);
+ free(cpuload.load);
+ cpuload.num_cpus = -1;
+ cpuload.load = NULL;
+ cpuload.last_total_time = NULL;
+ cpuload.last_working_time = NULL;
+ }
+}
+
+/** calculates current cpu load and stores it in cpuload_data object */
+static int calc_cpu_load(const int consider_nice)
+{
+ unsigned long total_elapsed, working_elapsed;
+ char what[32];
+ unsigned long user_time, nice_time, system_time, idle_time;
+ unsigned long total_time, iowait_time;
+ unsigned scan_ret;
+ char line[256];
+ char cpu_string[7];
+ FILE *fp;
+ int new_num_cpus;
+
+ new_num_cpus = get_cpu_count();
+ if (new_num_cpus == -1 || new_num_cpus != cpuload.num_cpus) {
+ free_cpu_load_data();
+ cpuload.num_cpus = new_num_cpus;
+ if (cpuload.num_cpus <= 0) {
+ errno = ENODEV;
+ return -20;
+ }
+
+ cpuload.last_total_time = (unsigned long *)calloc(cpuload.num_cpus + 1,
+ sizeof(unsigned long));
+ cpuload.last_working_time = (unsigned long *)calloc(cpuload.num_cpus + 1,
+ sizeof(unsigned long));
+ cpuload.load = (int *)calloc(cpuload.num_cpus + 1, sizeof(int));
+ }
+
+ if ((fp = fopen(PROC_STAT_FILE, "r")) == NULL) {
+ dbg("Could not open %s: %s", PROC_STAT_FILE, strerror(errno));
+ return -1;
+ }
+
+ /* start with the first line, "overall" cpu load */
+ /* if cpuload.num_cpus == 1, we do not need to evaluate "overall" and "per-cpu" load */
+ sprintf(cpu_string, "cpu ");
+ int i;
+ for (i = 0; i <= cpuload.num_cpus - (cpuload.num_cpus == 1); i++) {
+
+ if (fgets(line,255,fp) == NULL) {
+ ERROR("%s too short (%s)", PROC_STAT_FILE, cpu_string);
+ fclose(fp);
+ return -1;
+ }
+ if (memcmp(line, cpu_string, strlen(cpu_string))) {
+ ERROR("no '%s' string in %s line %d", cpu_string, PROC_STAT_FILE, i);
+ fclose(fp);
+ return -1;
+ }
+ /* initialized, since it is simply not there in 2.4 */
+ iowait_time = 0;
+ scan_ret = sscanf(line, "%s %lu %lu %lu %lu %lu", what, &user_time, &nice_time,
+ &system_time, &idle_time, &iowait_time);
+ if (scan_ret < 5) {
+ ERROR("only %d values in %s. Please report.", scan_ret, PROC_STAT_FILE);
+ fclose(fp);
+ return -1;
+ }
+
+ unsigned long working_time;
+ if (consider_nice) {
+ working_time = user_time + system_time + nice_time;
+ idle_time += iowait_time;
+ } else {
+ working_time = user_time + system_time;
+ idle_time += (nice_time + iowait_time);
+ }
+ total_time = working_time + idle_time;
+ total_elapsed = total_time - cpuload.last_total_time[i];
+ working_elapsed = working_time - cpuload.last_working_time[i];
+ cpuload.last_working_time[i] = working_time;
+ cpuload.last_total_time[i] = total_time;
+
+ if (!total_elapsed) {
+ /* not once per CPU, only once per check. */
+ if (!i)
+ dbg("%s not updated yet, poll slower.", PROC_STAT_FILE);
+ } else
+ cpuload.load[i] = working_elapsed * 100 / total_elapsed;
+
+ sprintf(cpu_string, "cpu%d ", i);
+ }
+ /* shortcut for UP systems */
+ if (cpuload.num_cpus == 1)
+ cpuload.load[1] = cpuload.load[0];
+
+ fclose(fp);
+
+ return 0;
+}
+
+/** returns current cpuload which has been caluclated before */
+static int get_cpu_load(const int cpu_id)
+{
+ if (cpu_id < -1) {
+ errno = EINVAL;
+ return -10;
+ }
+
+ if (cpuload.load == NULL) {
+ ERROR("cpuload.load uninitialized");
+ errno = EFAULT;
+ return -40;
+ }
+
+ if (cpu_id >= cpuload.num_cpus) {
+ errno = ENODEV;
+ return -30;
+ }
+
+ return cpuload.load[cpu_id + 1];
+}
+/********************* CPU load end *********************/
+
+/********************* userspace interface *********************/
+static gboolean write_speed(unsigned kHz, int cpu_id)
+{
+ GString *speed_file = g_string_new("");
+ gboolean ret = TRUE;
+
+ if (!cpu_online(cpu_id)) {
+ ret = FALSE;
+ goto Out;
+ }
+
+ g_string_printf(speed_file, SYSFS_SCALING_SETSPEED_FILE, cpu_id);
+
+ if(!write_line(speed_file->str, "%u", kHz)){
+ ERROR("Could not set speed to: %u kHz; %s", kHz, strerror(errno));
+ ret = FALSE;
+ goto Out;
+ }
+
+ dbg("Speed set to: %uKHz for CPU %d", kHz, cpu_id);
+Out:
+ g_string_free(speed_file, TRUE);
+ return ret;
+}
+
+static void reinit_speed(struct userspace_interface *iface, int current_speed)
+{
+ if (!cpu_online(iface->base_cpu))
+ return;
+
+ write_speed(g_a_i(iface->speeds_kHz, current_speed), iface->base_cpu);
+ dbg("forced speed to %d kHz", g_a_i(iface->speeds_kHz, current_speed));
+}
+
+/** @brief set a speed with traversing all intermediary speeds */
+static int set_speed(struct userspace_interface *iface, int target_speed)
+{
+ int delta;
+ int current_speed = iface->current_speed;
+
+ if (current_speed == target_speed)
+ return -1;
+
+ if (current_speed > target_speed)
+ delta = -1;
+ else
+ delta = 1;
+
+ do {
+ current_speed += delta;
+ write_speed(g_a_i(iface->speeds_kHz, current_speed), iface->base_cpu);
+ } while (current_speed != target_speed);
+
+ return current_speed;
+}
+
+/** @brief set speed to the next higher supported value
+ *
+ * @return integer with result of increase speed
+ * @retval 0 if maximum is already reached
+ * @retval 1 if new speed could be set
+ * @retval -1 if mode is not userspace
+ */
+static int increase_speed(struct userspace_interface *iface)
+{
+ int new_speed = iface->current_speed;
+ int current_speed = iface->current_speed;
+
+ if (current_speed != 0)
+ new_speed--;
+ else
+ return current_speed;
+ if (current_speed != new_speed) {
+ dbg("current: %u new: %u", g_a_i(iface->speeds_kHz, current_speed),
+ g_a_i(iface->speeds_kHz, new_speed));
+ set_speed(iface, new_speed);
+ }
+ return new_speed;
+}
+
+/** @brief set speed to the next lower supported value
+ *
+ * @return integer with result of increase speed
+ * @retval 0 if maximum is already reached
+ * @retval 1 if new speed could be set
+ * @retval -1 if mode is not userspace
+ */
+static int decrease_speed(struct userspace_interface *iface)
+{
+ int new_speed = iface->current_speed;
+ int current_speed = iface->current_speed;
+
+
+ if (g_a_i(iface->speeds_kHz, new_speed + 1) != 0)
+ new_speed++;
+ else
+ return current_speed;
+ if (current_speed != new_speed) {
+ dbg("current: %u new: %u", g_a_i(iface->speeds_kHz, current_speed),
+ g_a_i(iface->speeds_kHz, new_speed));
+ set_speed(iface, new_speed);
+ }
+ return new_speed;
+}
+
+/** increases and decreases speeds */
+static gboolean adjust_speed(struct userspace_interface *iface)
+{
+ GSList *cpus = (GSList*)iface->cpus;
+ GSList *it = NULL;
+ int ret = 0;
+ int cpu_load = 0;
+
+ for (it = cpus; it != NULL; it = g_slist_next(it)) {
+ dbg("checking cpu %d: cpu_core: %d", (int)it->data, (int)it->data);
+ if (get_cpu_load((int)it->data) > cpu_load)
+ cpu_load = get_cpu_load((int)it->data);
+ }
+
+ dbg("cpu_max: %d cpu_high_limit: %d consider_nice: %d",
+ config.up_threshold, config.cpu_high_limit,
+ config.consider_nice);
+ dbg("Current: %u; current speed: %u MHz",
+ iface->current_speed, g_a_i(iface->speeds_kHz, iface->current_speed));
+ dbg("CPU load: %d, Previous CPU load %d, cpu_load diff: %d, last_step: %d, demotion: %u",
+ cpu_load, iface->prev_cpu_load, cpu_load - iface->prev_cpu_load, iface->last_step,
+ g_a_i(iface->demotion, iface->current_speed));
+
+ /* directly increase speed to maximum if cpu load jumped */
+ if (config.cpu_high_limit &&
+ (cpu_load - iface->prev_cpu_load) > config.cpu_high_limit) {
+ if (iface->current_speed != 0) {
+ set_speed(iface, 0);
+ iface->current_speed = 0;
+ dbg("jumped to max (%d kHz)",
+ g_a_i(iface->speeds_kHz, iface->current_speed));
+ ret = 1;
+ }
+ } else if (cpu_load > config.up_threshold && iface->current_speed > 0) {
+ iface->current_speed = increase_speed(iface);
+ dbg("increased to %d kHz", g_a_i(iface->speeds_kHz, iface->current_speed));
+ ret = 1;
+ } else if (cpu_load < (int)g_a_i(iface->demotion, iface->current_speed) &&
+ iface->current_speed < iface->last_step) {
+ iface->current_speed = decrease_speed(iface);
+ dbg("decreased to %d kHz", g_a_i(iface->speeds_kHz, iface->current_speed));
+ ret = -1;
+ } else {
+ ret = 0;
+ dbg("Speed not changed");
+ }
+
+ iface->prev_cpu_load = cpu_load;
+ return TRUE;
+}
+
+/** @brief create the hysteresis array */
+static void create_hysteresis_array(struct userspace_interface *iface)
+{
+ g_array_free(iface->demotion, TRUE);
+ iface->demotion = g_array_new(TRUE, TRUE, sizeof(unsigned));
+
+ int i;
+ if (iface->last_step > 0) {
+ for (i = 0; i < iface->last_step; i++) {
+ int demotion = (config.up_threshold - HYSTERESIS) *
+ g_a_i(iface->speeds_kHz, i + 1) /
+ g_a_i(iface->speeds_kHz, i);
+ g_array_append_val(iface->demotion, demotion);
+ dbg("Speed: %2u, kHz: %9u, demotion: %3u %%", i,
+ g_a_i(iface->speeds_kHz, i), g_a_i(iface->demotion, i));
+ }
+ }
+}
+
+static gboolean read_frequencies(struct userspace_interface *iface)
+{
+ int num_speeds = 0;
+ GSList *it = NULL;
+ GSList *available_freqs = NULL;
+ GString *available_frequencies_file = g_string_new("");
+
+ if (!cpu_online(iface->base_cpu))
+ return FALSE;
+
+ g_string_printf(available_frequencies_file,
+ SYSFS_SCALING_AVAILABLE_FREQS_FILE,
+ iface->base_cpu);
+ if (!read_line_split(available_frequencies_file, ' ', &available_freqs))
+ return FALSE;
+
+ g_string_free(available_frequencies_file, TRUE);
+
+ if (available_freqs == NULL) {
+ iface->last_step = 0;
+ return FALSE;
+ }
+
+ for (num_speeds = 0, it = available_freqs; it != NULL;
+ num_speeds++, it = g_slist_next(it)) {
+
+ unsigned index = (unsigned)it->data;
+ g_array_append_val(iface->speeds_kHz, index);
+ }
+ g_slist_free(available_freqs);
+
+ iface->last_step = num_speeds - 1;
+ dbg("Number of speeds: %d, last_step: %d", num_speeds, iface->last_step);
+
+ reinit_speed(iface, 0);
+
+ dbg("Available speeds:");
+ for (num_speeds = 0; g_a_i(iface->speeds_kHz, num_speeds); num_speeds++) {
+ dbg(" %2u: %9uKHz", num_speeds, g_a_i(iface->speeds_kHz, num_speeds));
+ }
+
+ return TRUE;
+}
+
+/** calculates current cpu load and traverses all existing interfaces */
+gboolean userspace_adjust_speeds(GSList *cpufreq_objs)
+{
+ GSList *it = NULL;
+
+ dbg("Adjusting speeds...");
+
+ if ((calc_cpu_load(DEFAULT_CONSIDER_NICE) < 0)) {
+ dbg("calc_cpu_load failed. Cannot adjust speeds");
+ return TRUE;
+ }
+
+ for (it = cpufreq_objs; it != NULL; it = g_slist_next(it)) {
+ struct cpufreq_obj *obj = it->data;
+ adjust_speed(obj->iface);
+ }
+
+ return TRUE;
+}
+
+/** inits one userspace interface with the given cores list. iface has to
+ * be allocated before passing it to that fucntion */
+gboolean userspace_init(struct userspace_interface *iface, GSList *cpus)
+{
+ if (iface == NULL)
+ return FALSE;
+
+ iface->demotion = g_array_new(TRUE, TRUE, sizeof(unsigned));
+ iface->speeds_kHz = g_array_new(TRUE, TRUE, sizeof(unsigned));
+ iface->last_step = -1;
+ iface->current_speed = 0;
+ iface->cpus = cpus;
+ iface->prev_cpu_load = 50;
+ iface->base_cpu = (int)cpus->data;
+
+ if ((getenv("HALD_VERBOSE")) != NULL)
+ is_verbose = TRUE;
+
+ if (!write_governor(USERSPACE_STRING, (int)cpus->data)) {
+ ERROR("Could not set userspace governor.");
+ return FALSE;
+ }
+
+ if (!read_frequencies(iface)) {
+ ERROR("Could not read available frequencies");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/** frees the userspace data */
+void userspace_free(void *data)
+{
+ struct userspace_interface *iface = data;
+ free_cpu_load_data();
+ g_array_free(iface->speeds_kHz, TRUE);
+ g_array_free(iface->demotion, TRUE);
+}
+
+/** sets the performance of the userspace governor. num has to be between
+ * 1 and 100 */
+gboolean userspace_set_performance(void *data, int up_threshold)
+{
+ struct userspace_interface *iface = data;
+
+ config.up_threshold = up_threshold;
+
+ config.cpu_high_limit = (int)(up_threshold * THRESHOLD_JUMP_LIMIT_RELATION);
+ if (config.cpu_high_limit < JUMP_CPUFREQ_LIMIT_MIN)
+ config.cpu_high_limit = JUMP_CPUFREQ_LIMIT_MIN;
+
+ dbg("cpu_max set to %d, cpu_high_limit set to %d",
+ config.up_threshold, config.cpu_high_limit);
+
+ create_hysteresis_array(iface);
+
+ return TRUE;
+}
+
+/** return the current performance setting */
+int userspace_get_performance(void)
+{
+ return config.up_threshold;
+}
+
+/** sets whether niced processes should be considered when calculating CPU
+ * load */
+gboolean userspace_set_consider_nice(void *data, gboolean consider)
+{
+ dbg("consider nice set to %d for userspace", consider);
+ config.consider_nice = consider;
+ return TRUE;
+}
+
+/** return the current consider nice setting */
+gboolean userspace_get_consider_nice(void)
+{
+ return config.consider_nice;
+}
+/********************* userspace end *********************/
diff -uNr -x CVS hal/hald/linux2/addons/addon-cpufreq-userspace.h hal.new/hald/linux2/addons/addon-cpufreq-userspace.h
--- hal/hald/linux2/addons/addon-cpufreq-userspace.h 1970-01-01 01:00:00.000000000 +0100
+++ hal.new/hald/linux2/addons/addon-cpufreq-userspace.h 2006-07-05 16:03:25.000000000 +0200
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * *
+ * addon-cpufreq-userspace.h *
+ * *
+ * Copyright (C) 2006 SUSE Linux Products GmbH *
+ * *
+ * Author(s): Holger Macht <[EMAIL PROTECTED]> *
+ * *
+ * 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 you *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA *
+ * *
+ ***************************************************************************/
+
+#ifndef ADDON_CPUFREQ_USERSPACE_H
+#define ADDON_CPUFREQ_USERSPACE_H
+
+#define USERSPACE_STRING "userspace"
+#define USERSPACE_POLL_INTERVAL 333
+
+struct userspace_interface {
+ int base_cpu;
+ int last_step;
+ int current_speed;
+ int g_source_id;
+ int prev_cpu_load;
+ GSList *cpus;
+ GArray *speeds_kHz;
+ GArray *demotion;
+};
+
+gboolean userspace_adjust_speeds (GSList *cpufreq_objs);
+
+gboolean userspace_init (struct userspace_interface *iface,
+ GSList *cpus);
+
+gboolean userspace_set_performance (void *data,
+ int performance);
+
+int userspace_get_performance (void);
+
+gboolean userspace_set_consider_nice (void *data,
+ gboolean consider);
+
+gboolean userspace_get_consider_nice (void);
+
+void userspace_free (void *data);
+
+void free_cpu_load_data (void);
+
+#endif /* ADDON_CPUFREQ_USERSPACE_H */
diff -uNr -x CVS hal/hald/linux2/addons/Makefile.am hal.new/hald/linux2/addons/Makefile.am
--- hal/hald/linux2/addons/Makefile.am 2006-06-09 04:19:32.000000000 +0200
+++ hal.new/hald/linux2/addons/Makefile.am 2006-07-05 16:03:05.000000000 +0200
@@ -15,7 +15,8 @@
hald-addon-acpi-buttons-toshiba \
hald-addon-storage \
hald-addon-keyboard \
- hald-addon-pmu
+ hald-addon-pmu \
+ hald-addon-cpufreq
if HAVE_LIBUSB
libexec_PROGRAMS += hald-addon-usb-csr
@@ -25,6 +26,10 @@
endif
endif
+hald_addon_cpufreq_SOURCES = addon-cpufreq.c addon-cpufreq.h addon-cpufreq-userspace.h \
+ addon-cpufreq-userspace.c
+hald_addon_cpufreq_LDADD = $(top_builddir)/libhal/libhal.la @PACKAGE_LIBS@
+
hald_addon_hid_ups_SOURCES = addon-hid-ups.c
hald_addon_hid_ups_LDADD = $(top_builddir)/libhal/libhal.la
_______________________________________________
powersave-devel mailing list
[email protected]
http://forge.novell.com/mailman/listinfo/powersave-devel