linux/drivers/cpuidle/governor.c
<<
>>
Prefs
   1/*
   2 * governor.c - governor support
   3 *
   4 * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
   5 *               Shaohua Li <shaohua.li@intel.com>
   6 *               Adam Belay <abelay@novell.com>
   7 *
   8 * This code is licenced under the GPL.
   9 */
  10
  11#include <linux/mutex.h>
  12#include <linux/module.h>
  13#include <linux/cpuidle.h>
  14
  15#include "cpuidle.h"
  16
  17LIST_HEAD(cpuidle_governors);
  18struct cpuidle_governor *cpuidle_curr_governor;
  19
  20/**
  21 * __cpuidle_find_governor - finds a governor of the specified name
  22 * @str: the name
  23 *
  24 * Must be called with cpuidle_lock acquired.
  25 */
  26static struct cpuidle_governor * __cpuidle_find_governor(const char *str)
  27{
  28        struct cpuidle_governor *gov;
  29
  30        list_for_each_entry(gov, &cpuidle_governors, governor_list)
  31                if (!strnicmp(str, gov->name, CPUIDLE_NAME_LEN))
  32                        return gov;
  33
  34        return NULL;
  35}
  36
  37/**
  38 * cpuidle_switch_governor - changes the governor
  39 * @gov: the new target governor
  40 *
  41 * NOTE: "gov" can be NULL to specify disabled
  42 * Must be called with cpuidle_lock acquired.
  43 */
  44int cpuidle_switch_governor(struct cpuidle_governor *gov)
  45{
  46        struct cpuidle_device *dev;
  47
  48        if (gov == cpuidle_curr_governor)
  49                return 0;
  50
  51        cpuidle_uninstall_idle_handler();
  52
  53        if (cpuidle_curr_governor) {
  54                list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
  55                        cpuidle_disable_device(dev);
  56                module_put(cpuidle_curr_governor->owner);
  57        }
  58
  59        cpuidle_curr_governor = gov;
  60
  61        if (gov) {
  62                if (!try_module_get(cpuidle_curr_governor->owner))
  63                        return -EINVAL;
  64                list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
  65                        cpuidle_enable_device(dev);
  66                cpuidle_install_idle_handler();
  67                printk(KERN_INFO "cpuidle: using governor %s\n", gov->name);
  68        }
  69
  70        return 0;
  71}
  72
  73/**
  74 * cpuidle_register_governor - registers a governor
  75 * @gov: the governor
  76 */
  77int cpuidle_register_governor(struct cpuidle_governor *gov)
  78{
  79        int ret = -EEXIST;
  80
  81        if (!gov || !gov->select)
  82                return -EINVAL;
  83
  84        if (cpuidle_disabled())
  85                return -ENODEV;
  86
  87        mutex_lock(&cpuidle_lock);
  88        if (__cpuidle_find_governor(gov->name) == NULL) {
  89                ret = 0;
  90                list_add_tail(&gov->governor_list, &cpuidle_governors);
  91                if (!cpuidle_curr_governor ||
  92                    cpuidle_curr_governor->rating < gov->rating)
  93                        cpuidle_switch_governor(gov);
  94        }
  95        mutex_unlock(&cpuidle_lock);
  96
  97        return ret;
  98}
  99
 100/**
 101 * cpuidle_replace_governor - find a replacement governor
 102 * @exclude_rating: the rating that will be skipped while looking for
 103 * new governor.
 104 */
 105static struct cpuidle_governor *cpuidle_replace_governor(int exclude_rating)
 106{
 107        struct cpuidle_governor *gov;
 108        struct cpuidle_governor *ret_gov = NULL;
 109        unsigned int max_rating = 0;
 110
 111        list_for_each_entry(gov, &cpuidle_governors, governor_list) {
 112                if (gov->rating == exclude_rating)
 113                        continue;
 114                if (gov->rating > max_rating) {
 115                        max_rating = gov->rating;
 116                        ret_gov = gov;
 117                }
 118        }
 119
 120        return ret_gov;
 121}
 122
 123/**
 124 * cpuidle_unregister_governor - unregisters a governor
 125 * @gov: the governor
 126 */
 127void cpuidle_unregister_governor(struct cpuidle_governor *gov)
 128{
 129        if (!gov)
 130                return;
 131
 132        mutex_lock(&cpuidle_lock);
 133        if (gov == cpuidle_curr_governor) {
 134                struct cpuidle_governor *new_gov;
 135                new_gov = cpuidle_replace_governor(gov->rating);
 136                cpuidle_switch_governor(new_gov);
 137        }
 138        list_del(&gov->governor_list);
 139        mutex_unlock(&cpuidle_lock);
 140}
 141
 142