linux/drivers/cpuidle/sysfs.c
<<
>>
Prefs
   1/*
   2 * sysfs.c - sysfs support
   3 *
   4 * (C) 2006-2007 Shaohua Li <shaohua.li@intel.com>
   5 *
   6 * This code is licenced under the GPL.
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/cpuidle.h>
  11#include <linux/sysfs.h>
  12#include <linux/cpu.h>
  13
  14#include "cpuidle.h"
  15
  16static unsigned int sysfs_switch;
  17static int __init cpuidle_sysfs_setup(char *unused)
  18{
  19        sysfs_switch = 1;
  20        return 1;
  21}
  22__setup("cpuidle_sysfs_switch", cpuidle_sysfs_setup);
  23
  24static ssize_t show_available_governors(struct sysdev_class *class,
  25                                        char *buf)
  26{
  27        ssize_t i = 0;
  28        struct cpuidle_governor *tmp;
  29
  30        mutex_lock(&cpuidle_lock);
  31        list_for_each_entry(tmp, &cpuidle_governors, governor_list) {
  32                if (i >= (ssize_t) ((PAGE_SIZE/sizeof(char)) - CPUIDLE_NAME_LEN - 2))
  33                        goto out;
  34                i += scnprintf(&buf[i], CPUIDLE_NAME_LEN, "%s ", tmp->name);
  35        }
  36
  37out:
  38        i+= sprintf(&buf[i], "\n");
  39        mutex_unlock(&cpuidle_lock);
  40        return i;
  41}
  42
  43static ssize_t show_current_driver(struct sysdev_class *class,
  44                                   char *buf)
  45{
  46        ssize_t ret;
  47
  48        spin_lock(&cpuidle_driver_lock);
  49        if (cpuidle_curr_driver)
  50                ret = sprintf(buf, "%s\n", cpuidle_curr_driver->name);
  51        else
  52                ret = sprintf(buf, "none\n");
  53        spin_unlock(&cpuidle_driver_lock);
  54
  55        return ret;
  56}
  57
  58static ssize_t show_current_governor(struct sysdev_class *class,
  59                                     char *buf)
  60{
  61        ssize_t ret;
  62
  63        mutex_lock(&cpuidle_lock);
  64        if (cpuidle_curr_governor)
  65                ret = sprintf(buf, "%s\n", cpuidle_curr_governor->name);
  66        else
  67                ret = sprintf(buf, "none\n");
  68        mutex_unlock(&cpuidle_lock);
  69
  70        return ret;
  71}
  72
  73static ssize_t store_current_governor(struct sysdev_class *class,
  74                                      const char *buf, size_t count)
  75{
  76        char gov_name[CPUIDLE_NAME_LEN];
  77        int ret = -EINVAL;
  78        size_t len = count;
  79        struct cpuidle_governor *gov;
  80
  81        if (!len || len >= sizeof(gov_name))
  82                return -EINVAL;
  83
  84        memcpy(gov_name, buf, len);
  85        gov_name[len] = '\0';
  86        if (gov_name[len - 1] == '\n')
  87                gov_name[--len] = '\0';
  88
  89        mutex_lock(&cpuidle_lock);
  90
  91        list_for_each_entry(gov, &cpuidle_governors, governor_list) {
  92                if (strlen(gov->name) == len && !strcmp(gov->name, gov_name)) {
  93                        ret = cpuidle_switch_governor(gov);
  94                        break;
  95                }
  96        }
  97
  98        mutex_unlock(&cpuidle_lock);
  99
 100        if (ret)
 101                return ret;
 102        else
 103                return count;
 104}
 105
 106static SYSDEV_CLASS_ATTR(current_driver, 0444, show_current_driver, NULL);
 107static SYSDEV_CLASS_ATTR(current_governor_ro, 0444, show_current_governor,
 108                         NULL);
 109
 110static struct attribute *cpuclass_default_attrs[] = {
 111        &attr_current_driver.attr,
 112        &attr_current_governor_ro.attr,
 113        NULL
 114};
 115
 116static SYSDEV_CLASS_ATTR(available_governors, 0444, show_available_governors,
 117                         NULL);
 118static SYSDEV_CLASS_ATTR(current_governor, 0644, show_current_governor,
 119                         store_current_governor);
 120
 121static struct attribute *cpuclass_switch_attrs[] = {
 122        &attr_available_governors.attr,
 123        &attr_current_driver.attr,
 124        &attr_current_governor.attr,
 125        NULL
 126};
 127
 128static struct attribute_group cpuclass_attr_group = {
 129        .attrs = cpuclass_default_attrs,
 130        .name = "cpuidle",
 131};
 132
 133/**
 134 * cpuidle_add_class_sysfs - add CPU global sysfs attributes
 135 */
 136int cpuidle_add_class_sysfs(struct sysdev_class *cls)
 137{
 138        if (sysfs_switch)
 139                cpuclass_attr_group.attrs = cpuclass_switch_attrs;
 140
 141        return sysfs_create_group(&cls->kset.kobj, &cpuclass_attr_group);
 142}
 143
 144/**
 145 * cpuidle_remove_class_sysfs - remove CPU global sysfs attributes
 146 */
 147void cpuidle_remove_class_sysfs(struct sysdev_class *cls)
 148{
 149        sysfs_remove_group(&cls->kset.kobj, &cpuclass_attr_group);
 150}
 151
 152struct cpuidle_attr {
 153        struct attribute attr;
 154        ssize_t (*show)(struct cpuidle_device *, char *);
 155        ssize_t (*store)(struct cpuidle_device *, const char *, size_t count);
 156};
 157
 158#define define_one_ro(_name, show) \
 159        static struct cpuidle_attr attr_##_name = __ATTR(_name, 0444, show, NULL)
 160#define define_one_rw(_name, show, store) \
 161        static struct cpuidle_attr attr_##_name = __ATTR(_name, 0644, show, store)
 162
 163#define kobj_to_cpuidledev(k) container_of(k, struct cpuidle_device, kobj)
 164#define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr)
 165static ssize_t cpuidle_show(struct kobject * kobj, struct attribute * attr ,char * buf)
 166{
 167        int ret = -EIO;
 168        struct cpuidle_device *dev = kobj_to_cpuidledev(kobj);
 169        struct cpuidle_attr * cattr = attr_to_cpuidleattr(attr);
 170
 171        if (cattr->show) {
 172                mutex_lock(&cpuidle_lock);
 173                ret = cattr->show(dev, buf);
 174                mutex_unlock(&cpuidle_lock);
 175        }
 176        return ret;
 177}
 178
 179static ssize_t cpuidle_store(struct kobject * kobj, struct attribute * attr,
 180                     const char * buf, size_t count)
 181{
 182        int ret = -EIO;
 183        struct cpuidle_device *dev = kobj_to_cpuidledev(kobj);
 184        struct cpuidle_attr * cattr = attr_to_cpuidleattr(attr);
 185
 186        if (cattr->store) {
 187                mutex_lock(&cpuidle_lock);
 188                ret = cattr->store(dev, buf, count);
 189                mutex_unlock(&cpuidle_lock);
 190        }
 191        return ret;
 192}
 193
 194static struct sysfs_ops cpuidle_sysfs_ops = {
 195        .show = cpuidle_show,
 196        .store = cpuidle_store,
 197};
 198
 199static void cpuidle_sysfs_release(struct kobject *kobj)
 200{
 201        struct cpuidle_device *dev = kobj_to_cpuidledev(kobj);
 202
 203        complete(&dev->kobj_unregister);
 204}
 205
 206static struct kobj_type ktype_cpuidle = {
 207        .sysfs_ops = &cpuidle_sysfs_ops,
 208        .release = cpuidle_sysfs_release,
 209};
 210
 211struct cpuidle_state_attr {
 212        struct attribute attr;
 213        ssize_t (*show)(struct cpuidle_state *, char *);
 214        ssize_t (*store)(struct cpuidle_state *, const char *, size_t);
 215};
 216
 217#define define_one_state_ro(_name, show) \
 218static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL)
 219
 220#define define_show_state_function(_name) \
 221static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \
 222{ \
 223        return sprintf(buf, "%u\n", state->_name);\
 224}
 225
 226#define define_show_state_ull_function(_name) \
 227static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \
 228{ \
 229        return sprintf(buf, "%llu\n", state->_name);\
 230}
 231
 232#define define_show_state_str_function(_name) \
 233static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \
 234{ \
 235        if (state->_name[0] == '\0')\
 236                return sprintf(buf, "<null>\n");\
 237        return sprintf(buf, "%s\n", state->_name);\
 238}
 239
 240define_show_state_function(exit_latency)
 241define_show_state_function(power_usage)
 242define_show_state_ull_function(usage)
 243define_show_state_ull_function(time)
 244define_show_state_str_function(name)
 245define_show_state_str_function(desc)
 246
 247define_one_state_ro(name, show_state_name);
 248define_one_state_ro(desc, show_state_desc);
 249define_one_state_ro(latency, show_state_exit_latency);
 250define_one_state_ro(power, show_state_power_usage);
 251define_one_state_ro(usage, show_state_usage);
 252define_one_state_ro(time, show_state_time);
 253
 254static struct attribute *cpuidle_state_default_attrs[] = {
 255        &attr_name.attr,
 256        &attr_desc.attr,
 257        &attr_latency.attr,
 258        &attr_power.attr,
 259        &attr_usage.attr,
 260        &attr_time.attr,
 261        NULL
 262};
 263
 264#define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj)
 265#define kobj_to_state(k) (kobj_to_state_obj(k)->state)
 266#define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr)
 267static ssize_t cpuidle_state_show(struct kobject * kobj,
 268        struct attribute * attr ,char * buf)
 269{
 270        int ret = -EIO;
 271        struct cpuidle_state *state = kobj_to_state(kobj);
 272        struct cpuidle_state_attr * cattr = attr_to_stateattr(attr);
 273
 274        if (cattr->show)
 275                ret = cattr->show(state, buf);
 276
 277        return ret;
 278}
 279
 280static struct sysfs_ops cpuidle_state_sysfs_ops = {
 281        .show = cpuidle_state_show,
 282};
 283
 284static void cpuidle_state_sysfs_release(struct kobject *kobj)
 285{
 286        struct cpuidle_state_kobj *state_obj = kobj_to_state_obj(kobj);
 287
 288        complete(&state_obj->kobj_unregister);
 289}
 290
 291static struct kobj_type ktype_state_cpuidle = {
 292        .sysfs_ops = &cpuidle_state_sysfs_ops,
 293        .default_attrs = cpuidle_state_default_attrs,
 294        .release = cpuidle_state_sysfs_release,
 295};
 296
 297static void inline cpuidle_free_state_kobj(struct cpuidle_device *device, int i)
 298{
 299        kobject_put(&device->kobjs[i]->kobj);
 300        wait_for_completion(&device->kobjs[i]->kobj_unregister);
 301        kfree(device->kobjs[i]);
 302        device->kobjs[i] = NULL;
 303}
 304
 305/**
 306 * cpuidle_add_driver_sysfs - adds driver-specific sysfs attributes
 307 * @device: the target device
 308 */
 309int cpuidle_add_state_sysfs(struct cpuidle_device *device)
 310{
 311        int i, ret = -ENOMEM;
 312        struct cpuidle_state_kobj *kobj;
 313
 314        /* state statistics */
 315        for (i = 0; i < device->state_count; i++) {
 316                kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL);
 317                if (!kobj)
 318                        goto error_state;
 319                kobj->state = &device->states[i];
 320                init_completion(&kobj->kobj_unregister);
 321
 322                ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, &device->kobj,
 323                                           "state%d", i);
 324                if (ret) {
 325                        kfree(kobj);
 326                        goto error_state;
 327                }
 328                kobject_uevent(&kobj->kobj, KOBJ_ADD);
 329                device->kobjs[i] = kobj;
 330        }
 331
 332        return 0;
 333
 334error_state:
 335        for (i = i - 1; i >= 0; i--)
 336                cpuidle_free_state_kobj(device, i);
 337        return ret;
 338}
 339
 340/**
 341 * cpuidle_remove_driver_sysfs - removes driver-specific sysfs attributes
 342 * @device: the target device
 343 */
 344void cpuidle_remove_state_sysfs(struct cpuidle_device *device)
 345{
 346        int i;
 347
 348        for (i = 0; i < device->state_count; i++)
 349                cpuidle_free_state_kobj(device, i);
 350}
 351
 352/**
 353 * cpuidle_add_sysfs - creates a sysfs instance for the target device
 354 * @sysdev: the target device
 355 */
 356int cpuidle_add_sysfs(struct sys_device *sysdev)
 357{
 358        int cpu = sysdev->id;
 359        struct cpuidle_device *dev;
 360        int error;
 361
 362        dev = per_cpu(cpuidle_devices, cpu);
 363        error = kobject_init_and_add(&dev->kobj, &ktype_cpuidle, &sysdev->kobj,
 364                                     "cpuidle");
 365        if (!error)
 366                kobject_uevent(&dev->kobj, KOBJ_ADD);
 367        return error;
 368}
 369
 370/**
 371 * cpuidle_remove_sysfs - deletes a sysfs instance on the target device
 372 * @sysdev: the target device
 373 */
 374void cpuidle_remove_sysfs(struct sys_device *sysdev)
 375{
 376        int cpu = sysdev->id;
 377        struct cpuidle_device *dev;
 378
 379        dev = per_cpu(cpuidle_devices, cpu);
 380        kobject_put(&dev->kobj);
 381}
 382