Messages in this thread Patch in this message | | | Date | Mon, 12 Mar 2012 17:19:36 +0800 | From | ShuoX Liu <> | Subject | [PATCH 1/3] cpuidle: Move cpuidle sysfs entry of each cpu to debugfs. |
| |
On 2012年03月09日 02:01, Greg KH wrote: > On Wed, Mar 07, 2012 at 09:00:51AM +0800, Yanmin Zhang wrote: >> On Tue, 2012-03-06 at 06:39 -0800, Greg KH wrote: >>> On Tue, Mar 06, 2012 at 01:51:18PM +0800, Yanmin Zhang wrote: >>>> On Mon, 2012-03-05 at 21:22 -0800, Greg KH wrote: >>>>> On Tue, Mar 06, 2012 at 09:54:45AM +0800, Yanmin Zhang wrote: >>>>>> On Mon, 2012-03-05 at 14:20 +0200, Valentin, Eduardo wrote: >>>>>>> Hello, >>>>>>> >>>>>>> >>>>>>> On Mon, Mar 5, 2012 at 12:18 PM, Henrique de Moraes Holschuh >>>>>>> <hmh@hmh.eng.br> wrote: >>>>>>>> On Mon, 05 Mar 2012, ShuoX Liu wrote: >>>>>>>>> @@ -45,6 +46,7 @@ total 0 >>>>>>>>> /sys/devices/system/cpu/cpu0/cpuidle/state1: >>>>>>>>> total 0 >>>>>>>>> -r--r--r-- 1 root root 4096 Feb 8 10:42 desc >>>>>>>>> +-rw-r--r-- 1 root root 4096 Feb 8 10:42 disable >>>>>>>>> -r--r--r-- 1 root root 4096 Feb 8 10:42 latency >>>>>>>>> -r--r--r-- 1 root root 4096 Feb 8 10:42 name >>>>>>>>> -r--r--r-- 1 root root 4096 Feb 8 10:42 power >>>>>>>> >>>>>>>> ... >>>>>>>> >>>>>>>>> diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c >>>>>>>>> index 3fe41fe..1eae29a 100644 >>>>>>>>> --- a/drivers/cpuidle/sysfs.c >>>>>>>>> +++ b/drivers/cpuidle/sysfs.c >>>>>>>>> @@ -222,6 +222,9 @@ struct cpuidle_state_attr { >>>>>>>>> #define define_one_state_ro(_name, show) \ >>>>>>>>> static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, >>>>>>>>> show, NULL) >>>>>>>>> >>>>>>>>> +#define define_one_state_rw(_name, show, store) \ >>>>>>>>> +static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0644, >>>>>>>>> show, store) >>>>>>>>> + >>>>>>>>> #define define_show_state_function(_name) \ >>>>>>>>> static ssize_t show_state_##_name(struct cpuidle_state *state, \ >>>>>>>>> struct cpuidle_state_usage *state_usage, char *buf) \ >>>>>>>>> @@ -229,6 +232,19 @@ static ssize_t show_state_##_name(struct >>>>>>>>> cpuidle_state *state, \ >>>>>>>>> return sprintf(buf, "%u\n", state->_name);\ >>>>>>>>> } >>>>>>>>> >>>>>>>>> +#define define_store_state_function(_name) \ >>>>>>>>> +static ssize_t store_state_##_name(struct cpuidle_state *state, \ >>>>>>>>> + const char *buf, size_t size) \ >>>>>>>>> +{ \ >>>>>>>>> + int value; \ >>>>>>>>> + sscanf(buf, "%d",&value); \ >>>>>>>>> + if (value) \ >>>>>>>>> + state->disable = 1; \ >>>>>>>>> + else \ >>>>>>>>> + state->disable = 0; \ >>>>>>>>> + return size; \ >>>>>>>>> +} >>>>>>>> >>>>>>>> Isn't this missing a check for capabilities? Disabling cpuidle states is >>>>>>>> not something random Joe (and IMHO that does mean random capability- >>>>>>>> restricted Joe root) should be doing... >>>>>>>> >>>>>>>> Also, maybe it would be best to use one of the lib helpers to parse that >>>>>>>> value, so that it will be less annoying to userspace (trim blanks, complain >>>>>>>> if there is trailing junk after trimming, etc)? >>>>>>> >>>>>>> I may be jumping the thread in the middle but, if it is for debug >>>>>>> purposes, as states the subject, shouldn't this entry go to debugfs >>>>>>> instead of sysfs? I know cpuidle has all the infrastructure there to >>>>>>> simply add another sysfs entry, but if the intent is to create a debug >>>>>>> capability, then I'd say it fits under debugfs instead. Adding Greg >>>>>>> KH here, as I suppose he may have strong opinion on using sysfs for >>>>>>> debugging. >>>>>> Thanks for the comments. >>>>>> >>>>>> IMHO, all entries under cpuidle directory are for debug purpose. End users >>>>>> shouldn't care about them. If we rewrite codes around all the entries, I strongly >>>>>> agree that we need move them to debugfs. >>>>> >>>>> I totally agree, they all need to move out of sysfs. >>>>> >>>>>> Here, we just add a new entry under same directory. If we create it under debugfs, >>>>>> we need create the similar directory tree, which is a duplicate effort. In addition, >>>>>> users might be confused that why we separate the entries under sysfs and debugfs. >>>>> >>>>> They should all be moved there, that will remove any confusion :) >>>> Greg, >>>> >>>> Sorry. I might mislead you. >>>> >>>> Basically, we could move all the entries of cpuidle from sysfs to debugfs. But such >>>> moving would change KBI. There might be many scripts used by end users to parse the >>>> data. If we change them to debugfs, the scripts wouldn't work and users would >>>> complain. >>>> >>>> What's your opinion about the KBI consistence? >>> >>> These files all are debugging files, right? So they should be moved, >>> especially as the violate the "one value per file" rule of sysfs. >>> >>> Do you know of any tools using these files? I have never heard of them, >>> and I was told we should move these files years ago. So I don't think >>> there should be any api issues. >>> >>> But, if there are, we need to know what they are, and work to preserve >>> them. The only way to find out is to move them :) >> We would move all cpuidle entries to debugfs firstly. >> 1) Create the similar directory tree on debugfs: cpu/cpuXXX, but just have cpuidle subdirectory; >> 2) Move our cpuidle->disable patch to debugfs; >> sysfs cpuXXX has other sub directories. We don't move them this time. >> >> The new directory under debugfs becomes: >> cpu---cpu0---cpuidle >> | >> | >> cpu1---cpuidle >> | >> >> Is it ok? > > Looks fine to me. > > greg k-h
I created a series to do this.
[PATCH 1/3] cpuidle: Move cpuidle sysfs entry of each cpu to debugfs. [PATCH 2/3] cpuidle: Add a debugfs entry to disable specific C state for debug purpose. [PATCH 3/3] cpupower: Update the cpupower tool for new debugfs entries of cpuidle.
Below are patches. --- From: ShuoX Liu <shuox.liu@intel.com>
This patch move cpuidle sysfs entry into debugfs. That is /sys/devices/system/cpu/cpuXX/cpuidle will be changed to $(debugfs_mount_dir)/cpu/cpuXX/cpuidle.
Signed-off-by: ShuoX Liu <shuox.liu@intel.com> Reviewed-by: Yanmin Zhang <yanmin_zhang@intel.com> --- Documentation/cpuidle/debugfs.txt | 63 ++++++++++ Documentation/cpuidle/sysfs.txt | 59 --------- drivers/base/cpu.c | 14 ++ drivers/cpuidle/Makefile | 1 + drivers/cpuidle/cpuidle.c | 23 +--- drivers/cpuidle/cpuidle.h | 29 ++++- drivers/cpuidle/debugfs.c | 176 +++++++++++++++++++++++++++ drivers/cpuidle/sysfs.c | 239 ------------------------------------- include/linux/cpuidle.h | 10 +- 9 files changed, 292 insertions(+), 322 deletions(-) create mode 100644 Documentation/cpuidle/debugfs.txt create mode 100644 drivers/cpuidle/debugfs.c
diff --git a/Documentation/cpuidle/debugfs.txt b/Documentation/cpuidle/debugfs.txt new file mode 100644 index 0000000..7724a69 --- /dev/null +++ b/Documentation/cpuidle/debugfs.txt @@ -0,0 +1,63 @@ + + Supporting multiple CPU idle levels in kernel + + cpuidle debugfs + +Per logical CPU specific cpuidle information are under +/sys/kernel/debug/cpu/cpuX/cpuidle +(when your system mounted debugfs at /sys/kernel/debug) +for each online cpu X + +-------------------------------------------------------------------------------- +# ls -lR /sys/kernel/debug/cpu/cpu0/cpuidle/ +/sys/kernel/debug/cpu/cpu0/cpuidle/: +total 0 +drwxr-xr-x 2 root root 0 Feb 8 10:42 state0 +drwxr-xr-x 2 root root 0 Feb 8 10:42 state1 +drwxr-xr-x 2 root root 0 Feb 8 10:42 state2 +drwxr-xr-x 2 root root 0 Feb 8 10:42 state3 + +/sys/kernel/debug/cpu/cpu0/cpuidle/state0: +total 0 +-r--r--r-- 1 root root 4096 Feb 8 10:42 desc +-r--r--r-- 1 root root 4096 Feb 8 10:42 latency +-r--r--r-- 1 root root 4096 Feb 8 10:42 name +-r--r--r-- 1 root root 4096 Feb 8 10:42 power +-r--r--r-- 1 root root 4096 Feb 8 10:42 time +-r--r--r-- 1 root root 4096 Feb 8 10:42 usage + +/sys/kernel/debug/cpu/cpu0/cpuidle/state1: +total 0 +-r--r--r-- 1 root root 4096 Feb 8 10:42 desc +-r--r--r-- 1 root root 4096 Feb 8 10:42 latency +-r--r--r-- 1 root root 4096 Feb 8 10:42 name +-r--r--r-- 1 root root 4096 Feb 8 10:42 power +-r--r--r-- 1 root root 4096 Feb 8 10:42 time +-r--r--r-- 1 root root 4096 Feb 8 10:42 usage + +/sys/kernel/debug/cpu/cpu0/cpuidle/state2: +total 0 +-r--r--r-- 1 root root 4096 Feb 8 10:42 desc +-r--r--r-- 1 root root 4096 Feb 8 10:42 latency +-r--r--r-- 1 root root 4096 Feb 8 10:42 name +-r--r--r-- 1 root root 4096 Feb 8 10:42 power +-r--r--r-- 1 root root 4096 Feb 8 10:42 time +-r--r--r-- 1 root root 4096 Feb 8 10:42 usage + +/sys/kernel/debug/cpu/cpu0/cpuidle/state3: +total 0 +-r--r--r-- 1 root root 4096 Feb 8 10:42 desc +-r--r--r-- 1 root root 4096 Feb 8 10:42 latency +-r--r--r-- 1 root root 4096 Feb 8 10:42 name +-r--r--r-- 1 root root 4096 Feb 8 10:42 power +-r--r--r-- 1 root root 4096 Feb 8 10:42 time +-r--r--r-- 1 root root 4096 Feb 8 10:42 usage +-------------------------------------------------------------------------------- + + +* desc : Small description about the idle state (string) +* latency : Latency to exit out of this idle state (in microseconds) +* name : Name of the idle state (string) +* power : Power consumed while in this idle state (in milliwatts) +* time : Total time spent in this idle state (in microseconds) +* usage : Number of times this state was entered (count) diff --git a/Documentation/cpuidle/sysfs.txt b/Documentation/cpuidle/sysfs.txt index 50d7b16..588e40b 100644 --- a/Documentation/cpuidle/sysfs.txt +++ b/Documentation/cpuidle/sysfs.txt @@ -18,62 +18,3 @@ following objects are visible instead. * current_governor In this case users can switch the governor at run time by writing to current_governor. - - -Per logical CPU specific cpuidle information are under -/sys/devices/system/cpu/cpuX/cpuidle -for each online cpu X - --------------------------------------------------------------------------------- -# ls -lR /sys/devices/system/cpu/cpu0/cpuidle/ -/sys/devices/system/cpu/cpu0/cpuidle/: -total 0 -drwxr-xr-x 2 root root 0 Feb 8 10:42 state0 -drwxr-xr-x 2 root root 0 Feb 8 10:42 state1 -drwxr-xr-x 2 root root 0 Feb 8 10:42 state2 -drwxr-xr-x 2 root root 0 Feb 8 10:42 state3 - -/sys/devices/system/cpu/cpu0/cpuidle/state0: -total 0 --r--r--r-- 1 root root 4096 Feb 8 10:42 desc --r--r--r-- 1 root root 4096 Feb 8 10:42 latency --r--r--r-- 1 root root 4096 Feb 8 10:42 name --r--r--r-- 1 root root 4096 Feb 8 10:42 power --r--r--r-- 1 root root 4096 Feb 8 10:42 time --r--r--r-- 1 root root 4096 Feb 8 10:42 usage - -/sys/devices/system/cpu/cpu0/cpuidle/state1: -total 0 --r--r--r-- 1 root root 4096 Feb 8 10:42 desc --r--r--r-- 1 root root 4096 Feb 8 10:42 latency --r--r--r-- 1 root root 4096 Feb 8 10:42 name --r--r--r-- 1 root root 4096 Feb 8 10:42 power --r--r--r-- 1 root root 4096 Feb 8 10:42 time --r--r--r-- 1 root root 4096 Feb 8 10:42 usage - -/sys/devices/system/cpu/cpu0/cpuidle/state2: -total 0 --r--r--r-- 1 root root 4096 Feb 8 10:42 desc --r--r--r-- 1 root root 4096 Feb 8 10:42 latency --r--r--r-- 1 root root 4096 Feb 8 10:42 name --r--r--r-- 1 root root 4096 Feb 8 10:42 power --r--r--r-- 1 root root 4096 Feb 8 10:42 time --r--r--r-- 1 root root 4096 Feb 8 10:42 usage - -/sys/devices/system/cpu/cpu0/cpuidle/state3: -total 0 --r--r--r-- 1 root root 4096 Feb 8 10:42 desc --r--r--r-- 1 root root 4096 Feb 8 10:42 latency --r--r--r-- 1 root root 4096 Feb 8 10:42 name --r--r--r-- 1 root root 4096 Feb 8 10:42 power --r--r--r-- 1 root root 4096 Feb 8 10:42 time --r--r--r-- 1 root root 4096 Feb 8 10:42 usage --------------------------------------------------------------------------------- - - -* desc : Small description about the idle state (string) -* latency : Latency to exit out of this idle state (in microseconds) -* name : Name of the idle state (string) -* power : Power consumed while in this idle state (in milliwatts) -* time : Total time spent in this idle state (in microseconds) -* usage : Number of times this state was entered (count) diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 4dabf50..cc4fa6f 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -12,6 +12,7 @@ #include <linux/node.h> #include <linux/gfp.h> #include <linux/percpu.h> +#include <linux/debugfs.h>
#include "base.h"
@@ -22,6 +23,9 @@ struct bus_type cpu_subsys = { EXPORT_SYMBOL_GPL(cpu_subsys);
static DEFINE_PER_CPU(struct device *, cpu_sys_devices); +static struct dentry *cpu_debugfs_root; +DEFINE_PER_CPU(struct dentry *, cpu_debugfs); +EXPORT_PER_CPU_SYMBOL_GPL(cpu_debugfs);
#ifdef CONFIG_HOTPLUG_CPU static ssize_t show_online(struct device *dev, @@ -71,6 +75,8 @@ void unregister_cpu(struct cpu *cpu) { int logical_cpu = cpu->dev.id;
+ debugfs_remove_recursive(per_cpu(cpu_debugfs, logical_cpu)); + unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
device_remove_file(&cpu->dev, &dev_attr_online); @@ -238,6 +244,7 @@ static void cpu_device_release(struct device *dev) int __cpuinit register_cpu(struct cpu *cpu, int num) { int error; + struct dentry **debugfs = &per_cpu(cpu_debugfs, num);
cpu->node_id = cpu_to_node(num); memset(&cpu->dev, 0x00, sizeof(struct device)); @@ -251,6 +258,9 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) per_cpu(cpu_sys_devices, num) = &cpu->dev; if (!error) register_cpu_under_node(num, cpu_to_node(num)); + if (!error && cpu_debugfs_root) + *debugfs = debugfs_create_dir(dev_name(&cpu->dev), + cpu_debugfs_root);
#ifdef CONFIG_KEXEC if (!error) @@ -323,4 +333,8 @@ void __init cpu_dev_init(void) #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) sched_create_sysfs_power_savings_entries(cpu_subsys.dev_root); #endif + + cpu_debugfs_root = debugfs_create_dir("cpu", NULL); + if (cpu_debugfs_root == ERR_PTR(-ENODEV)) + cpu_debugfs_root = NULL; } diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 5634f88..a4640ed 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -3,3 +3,4 @@ #
obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ +obj-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 59f4261..b4946bc 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -228,12 +228,9 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
poll_idle_init(cpuidle_get_driver());
- if ((ret = cpuidle_add_state_sysfs(dev))) - return ret; - if (cpuidle_curr_governor->enable && (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev))) - goto fail_sysfs; + return ret;
for (i = 0; i < dev->state_count; i++) { dev->states_usage[i].usage = 0; @@ -243,15 +240,13 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
smp_wmb();
+ cpuidle_add_state_debugfs(dev); + dev->enabled = 1;
enabled_devices++; - return 0;
-fail_sysfs: - cpuidle_remove_state_sysfs(dev); - - return ret; + return 0; }
EXPORT_SYMBOL_GPL(cpuidle_enable_device); @@ -275,7 +270,7 @@ void cpuidle_disable_device(struct cpuidle_device *dev) if (cpuidle_curr_governor->disable) cpuidle_curr_governor->disable(cpuidle_get_driver(), dev);
- cpuidle_remove_state_sysfs(dev); + cpuidle_remove_state_debugfs(dev); enabled_devices--; }
@@ -290,7 +285,6 @@ EXPORT_SYMBOL_GPL(cpuidle_disable_device); */ static int __cpuidle_register_device(struct cpuidle_device *dev) { - int ret; struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
@@ -303,10 +297,7 @@ static int __cpuidle_register_device(struct cpuidle_device *dev)
per_cpu(cpuidle_devices, dev->cpu) = dev; list_add(&dev->device_list, &cpuidle_detected_devices); - if ((ret = cpuidle_add_sysfs(cpu_dev))) { - module_put(cpuidle_driver->owner); - return ret; - } + cpuidle_add_debugfs(cpu_dev);
dev->registered = 1; return 0; @@ -354,7 +345,7 @@ void cpuidle_unregister_device(struct cpuidle_device *dev)
cpuidle_disable_device(dev);
- cpuidle_remove_sysfs(cpu_dev); + cpuidle_remove_debugfs(cpu_dev); list_del(&dev->device_list); wait_for_completion(&dev->kobj_unregister); per_cpu(cpuidle_devices, dev->cpu) = NULL; diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h index 7db1866..890545c 100644 --- a/drivers/cpuidle/cpuidle.h +++ b/drivers/cpuidle/cpuidle.h @@ -25,9 +25,30 @@ extern int cpuidle_switch_governor(struct cpuidle_governor *gov); /* sysfs */ extern int cpuidle_add_interface(struct device *dev); extern void cpuidle_remove_interface(struct device *dev); -extern int cpuidle_add_state_sysfs(struct cpuidle_device *device); -extern void cpuidle_remove_state_sysfs(struct cpuidle_device *device); -extern int cpuidle_add_sysfs(struct device *dev); -extern void cpuidle_remove_sysfs(struct device *dev); + +/* debugfs */ +#ifdef CONFIG_DEBUG_FS +DECLARE_PER_CPU(struct dentry *, cpu_debugfs); +extern int cpuidle_add_state_debugfs(struct cpuidle_device *device); +extern void cpuidle_remove_state_debugfs(struct cpuidle_device *device); +extern int cpuidle_add_debugfs(struct device *dev); +extern void cpuidle_remove_debugfs(struct device *dev); +#else +static inline int cpuidle_add_state_debugfs(struct cpuidle_device *device) +{ + return 0; +} +static inline +void cpuidle_remove_state_debugfs(struct cpuidle_device *device) +{ +} +static inline int cpuidle_add_debugfs(struct device *cpu_dev) +{ + return 0; +} +static inline void cpuidle_remove_debugfs(struct device *cpu_dev) +{ +} +#endif
#endif /* __DRIVER_CPUIDLE_H */ diff --git a/drivers/cpuidle/debugfs.c b/drivers/cpuidle/debugfs.c new file mode 100644 index 0000000..67ddc44 --- /dev/null +++ b/drivers/cpuidle/debugfs.c @@ -0,0 +1,176 @@ +/* + * debugfs.c - debugfs support + * + * (C) 2006-2007 Shaohua Li <shaohua.li@intel.com> + * (C) 2012 ShuoX Liu <shuox.liu@intel.com> + * + * This code is licenced under the GPL. + */ + +#include <linux/kernel.h> +#include <linux/cpuidle.h> +#include <linux/slab.h> +#include <linux/debugfs.h> + +#include "cpuidle.h" + +static int state_open_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t state_name_read(struct file *f, char __user *buf, + size_t count, loff_t *off) +{ + struct cpuidle_state *state = f->private_data; + char buffer[CPUIDLE_NAME_LEN]; + int len; + + len = snprintf(buffer, sizeof(buffer), "%s\n", state->name); + + return simple_read_from_buffer(buf, count, off, buffer, len); +} + +static const struct file_operations state_name_fops = { + .open = state_open_generic, + .read = state_name_read, +}; + +static ssize_t state_desc_read(struct file *f, char __user *buf, + size_t count, loff_t *off) +{ + struct cpuidle_state *state = f->private_data; + char buffer[CPUIDLE_DESC_LEN]; + int len; + + len = snprintf(buffer, sizeof(buffer), "%s\n", state->desc); + + return simple_read_from_buffer(buf, count, off, buffer, len); +} + +static const struct file_operations state_desc_fops = { + .open = state_open_generic, + .read = state_desc_read, +}; + +static void debugfs_add_state_attrs(struct cpuidle_dev_state *dev_state) +{ + struct dentry *parent = dev_state->debugfs; + struct cpuidle_state *state = dev_state->state; + struct cpuidle_state_usage *state_usage = dev_state->state_usage; + + if (!parent) + return; + + if (!debugfs_create_file("name", S_IRUGO, + parent, state, &state_name_fops)) + goto error; + + if (!debugfs_create_file("desc", S_IRUGO, + parent, state, &state_desc_fops)) + goto error; + + if (!debugfs_create_u32("latency", S_IRUGO, + parent, &state->exit_latency)) + goto error; + + if (!debugfs_create_u32("power", S_IRUGO, + parent, &state->power_usage)) + goto error; + + if (!debugfs_create_u64("usage", S_IRUGO, + parent, &state_usage->usage)) + goto error; + + if (!debugfs_create_u64("time", S_IRUGO, + parent, &state_usage->time)) + goto error; + + return; +error: + debugfs_remove_recursive(parent); +} + +static inline void cpuidle_free_dev_state(struct cpuidle_device *device, int i) +{ + struct cpuidle_dev_state *dev_state = device->dev_states[i]; + + if (!dev_state) + return ; + debugfs_remove_recursive(dev_state->debugfs); + kfree(dev_state); + dev_state = NULL; +} + +int cpuidle_add_state_debugfs(struct cpuidle_device *device) +{ + int i, ret = -ENOMEM; + char buf[16]; + struct cpuidle_dev_state *dev_state; + struct cpuidle_driver *drv = cpuidle_get_driver(); + struct dentry *parent = device->debugfs; + + if (!parent) + return -ENOENT; + /* state statistics */ + for (i = 0; i < device->state_count; i++) { + dev_state = + kzalloc(sizeof(struct cpuidle_dev_state), GFP_KERNEL); + if (!dev_state) + goto error; + dev_state->state = &drv->states[i]; + dev_state->state_usage = &device->states_usage[i]; + + sprintf(buf, "state%d", i); + dev_state->debugfs = debugfs_create_dir(buf, parent); + if (!dev_state->debugfs) { + kfree(dev_state); + dev_state = NULL; + goto error; + } + dev_state->dev = device; + device->dev_states[i] = dev_state; + debugfs_add_state_attrs(dev_state); + } + + return 0; + +error: + for (i = i - 1; i >= 0; i--) + cpuidle_free_dev_state(device, i); + return ret; +} + +void cpuidle_remove_state_debugfs(struct cpuidle_device *device) +{ + int i; + + for (i = 0; i < device->state_count; i++) + cpuidle_free_dev_state(device, i); +} + +int cpuidle_add_debugfs(struct device *cpu_dev) +{ + int cpu = cpu_dev->id; + struct cpuidle_device *device; + struct dentry *parent = per_cpu(cpu_debugfs, cpu); + + if (!parent) + return -ENOENT; + device = per_cpu(cpuidle_devices, cpu); + device->debugfs = debugfs_create_dir("cpuidle", parent); + if (!device->debugfs) + return -ENOMEM; + + return 0; +} + +void cpuidle_remove_debugfs(struct device *cpu_dev) +{ + int cpu = cpu_dev->id; + struct cpuidle_device *dev; + + dev = per_cpu(cpuidle_devices, cpu); + debugfs_remove_recursive(dev->debugfs); +} diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 3fe41fe..dd597a9 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -152,242 +152,3 @@ void cpuidle_remove_interface(struct device *dev) { sysfs_remove_group(&dev->kobj, &cpuidle_attr_group); } - -struct cpuidle_attr { - struct attribute attr; - ssize_t (*show)(struct cpuidle_device *, char *); - ssize_t (*store)(struct cpuidle_device *, const char *, size_t count); -}; - -#define define_one_ro(_name, show) \ - static struct cpuidle_attr attr_##_name = __ATTR(_name, 0444, show, NULL) -#define define_one_rw(_name, show, store) \ - static struct cpuidle_attr attr_##_name = __ATTR(_name, 0644, show, store) - -#define kobj_to_cpuidledev(k) container_of(k, struct cpuidle_device, kobj) -#define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr) -static ssize_t cpuidle_show(struct kobject * kobj, struct attribute * attr ,char * buf) -{ - int ret = -EIO; - struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); - struct cpuidle_attr * cattr = attr_to_cpuidleattr(attr); - - if (cattr->show) { - mutex_lock(&cpuidle_lock); - ret = cattr->show(dev, buf); - mutex_unlock(&cpuidle_lock); - } - return ret; -} - -static ssize_t cpuidle_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count) -{ - int ret = -EIO; - struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); - struct cpuidle_attr * cattr = attr_to_cpuidleattr(attr); - - if (cattr->store) { - mutex_lock(&cpuidle_lock); - ret = cattr->store(dev, buf, count); - mutex_unlock(&cpuidle_lock); - } - return ret; -} - -static const struct sysfs_ops cpuidle_sysfs_ops = { - .show = cpuidle_show, - .store = cpuidle_store, -}; - -static void cpuidle_sysfs_release(struct kobject *kobj) -{ - struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); - - complete(&dev->kobj_unregister); -} - -static struct kobj_type ktype_cpuidle = { - .sysfs_ops = &cpuidle_sysfs_ops, - .release = cpuidle_sysfs_release, -}; - -struct cpuidle_state_attr { - struct attribute attr; - ssize_t (*show)(struct cpuidle_state *, \ - struct cpuidle_state_usage *, char *); - ssize_t (*store)(struct cpuidle_state *, const char *, size_t); -}; - -#define define_one_state_ro(_name, show) \ -static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL) - -#define define_show_state_function(_name) \ -static ssize_t show_state_##_name(struct cpuidle_state *state, \ - struct cpuidle_state_usage *state_usage, char *buf) \ -{ \ - return sprintf(buf, "%u\n", state->_name);\ -} - -#define define_show_state_ull_function(_name) \ -static ssize_t show_state_##_name(struct cpuidle_state *state, \ - struct cpuidle_state_usage *state_usage, char *buf) \ -{ \ - return sprintf(buf, "%llu\n", state_usage->_name);\ -} - -#define define_show_state_str_function(_name) \ -static ssize_t show_state_##_name(struct cpuidle_state *state, \ - struct cpuidle_state_usage *state_usage, char *buf) \ -{ \ - if (state->_name[0] == '\0')\ - return sprintf(buf, "<null>\n");\ - return sprintf(buf, "%s\n", state->_name);\ -} - -define_show_state_function(exit_latency) -define_show_state_function(power_usage) -define_show_state_ull_function(usage) -define_show_state_ull_function(time) -define_show_state_str_function(name) -define_show_state_str_function(desc) - -define_one_state_ro(name, show_state_name); -define_one_state_ro(desc, show_state_desc); -define_one_state_ro(latency, show_state_exit_latency); -define_one_state_ro(power, show_state_power_usage); -define_one_state_ro(usage, show_state_usage); -define_one_state_ro(time, show_state_time); - -static struct attribute *cpuidle_state_default_attrs[] = { - &attr_name.attr, - &attr_desc.attr, - &attr_latency.attr, - &attr_power.attr, - &attr_usage.attr, - &attr_time.attr, - NULL -}; - -#define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj) -#define kobj_to_state(k) (kobj_to_state_obj(k)->state) -#define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage) -#define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr) -static ssize_t cpuidle_state_show(struct kobject * kobj, - struct attribute * attr ,char * buf) -{ - int ret = -EIO; - struct cpuidle_state *state = kobj_to_state(kobj); - struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj); - struct cpuidle_state_attr * cattr = attr_to_stateattr(attr); - - if (cattr->show) - ret = cattr->show(state, state_usage, buf); - - return ret; -} - -static const struct sysfs_ops cpuidle_state_sysfs_ops = { - .show = cpuidle_state_show, -}; - -static void cpuidle_state_sysfs_release(struct kobject *kobj) -{ - struct cpuidle_state_kobj *state_obj = kobj_to_state_obj(kobj); - - complete(&state_obj->kobj_unregister); -} - -static struct kobj_type ktype_state_cpuidle = { - .sysfs_ops = &cpuidle_state_sysfs_ops, - .default_attrs = cpuidle_state_default_attrs, - .release = cpuidle_state_sysfs_release, -}; - -static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i) -{ - kobject_put(&device->kobjs[i]->kobj); - wait_for_completion(&device->kobjs[i]->kobj_unregister); - kfree(device->kobjs[i]); - device->kobjs[i] = NULL; -} - -/** - * cpuidle_add_driver_sysfs - adds driver-specific sysfs attributes - * @device: the target device - */ -int cpuidle_add_state_sysfs(struct cpuidle_device *device) -{ - int i, ret = -ENOMEM; - struct cpuidle_state_kobj *kobj; - struct cpuidle_driver *drv = cpuidle_get_driver(); - - /* state statistics */ - for (i = 0; i < device->state_count; i++) { - kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); - if (!kobj) - goto error_state; - kobj->state = &drv->states[i]; - kobj->state_usage = &device->states_usage[i]; - init_completion(&kobj->kobj_unregister); - - ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, &device->kobj, - "state%d", i); - if (ret) { - kfree(kobj); - goto error_state; - } - kobject_uevent(&kobj->kobj, KOBJ_ADD); - device->kobjs[i] = kobj; - } - - return 0; - -error_state: - for (i = i - 1; i >= 0; i--) - cpuidle_free_state_kobj(device, i); - return ret; -} - -/** - * cpuidle_remove_driver_sysfs - removes driver-specific sysfs attributes - * @device: the target device - */ -void cpuidle_remove_state_sysfs(struct cpuidle_device *device) -{ - int i; - - for (i = 0; i < device->state_count; i++) - cpuidle_free_state_kobj(device, i); -} - -/** - * cpuidle_add_sysfs - creates a sysfs instance for the target device - * @dev: the target device - */ -int cpuidle_add_sysfs(struct device *cpu_dev) -{ - int cpu = cpu_dev->id; - struct cpuidle_device *dev; - int error; - - dev = per_cpu(cpuidle_devices, cpu); - error = kobject_init_and_add(&dev->kobj, &ktype_cpuidle, &cpu_dev->kobj, - "cpuidle"); - if (!error) - kobject_uevent(&dev->kobj, KOBJ_ADD); - return error; -} - -/** - * cpuidle_remove_sysfs - deletes a sysfs instance on the target device - * @dev: the target device - */ -void cpuidle_remove_sysfs(struct device *cpu_dev) -{ - int cpu = cpu_dev->id; - struct cpuidle_device *dev; - - dev = per_cpu(cpuidle_devices, cpu); - kobject_put(&dev->kobj); -} diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 712abcc..f605d28 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -76,11 +76,11 @@ cpuidle_set_statedata(struct cpuidle_state_usage *st_usage, void *data) st_usage->driver_data = data; }
-struct cpuidle_state_kobj { +struct cpuidle_dev_state { struct cpuidle_state *state; struct cpuidle_state_usage *state_usage; - struct completion kobj_unregister; - struct kobject kobj; + struct cpuidle_device *dev; + struct dentry *debugfs; };
struct cpuidle_device { @@ -91,12 +91,14 @@ struct cpuidle_device { int last_residency; int state_count; struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX]; - struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX]; + struct cpuidle_dev_state *dev_states[CPUIDLE_STATE_MAX];
struct list_head device_list; struct kobject kobj; struct completion kobj_unregister; void *governor_data; + + struct dentry *debugfs; };
DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices); -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |