diff options
author | Daniel Lezcano <daniel.lezcano@linaro.org> | 2013-06-12 09:08:51 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-07-14 20:09:47 -0400 |
commit | 728ce22b696f9f1404a74d7b2279a65933553a1b (patch) | |
tree | 29b441b10a0e67d937b88f82a1b789ae194f4f20 /drivers/cpuidle/sysfs.c | |
parent | f89ae89e2790341587f7132f0552c941342115e9 (diff) |
cpuidle: Make cpuidle's sysfs directory dynamically allocated
The cpuidle sysfs code is designed to have a single instance of per
CPU cpuidle directory. It is not possible to remove the sysfs entry
and create it again. This is not a problem with the current code but
future changes will add CPU hotplug support to enable/disable the
device, so it will need to remove the sysfs entry like other
subsystems do. That won't be possible without this change, because
the kobj is a static object which can't be reused for
kobj_init_and_add().
Add cpuidle_device_kobj to be allocated dynamically when
adding/removing a sysfs entry which is consistent with the other
cpuidle's sysfs entries.
An added benefit is that the sysfs code is now more self-contained
and the includes needed for sysfs can be moved from cpuidle.h
directly into sysfs.c so as to reduce the total number of headers
dragged along with cpuidle.h.
[rjw: Changelog]
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpuidle/sysfs.c')
-rw-r--r-- | drivers/cpuidle/sysfs.c | 63 |
1 files changed, 48 insertions, 15 deletions
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 7d4448a9fbbd..8739cc05228c 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c | |||
@@ -11,8 +11,10 @@ | |||
11 | #include <linux/sysfs.h> | 11 | #include <linux/sysfs.h> |
12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | #include <linux/cpu.h> | 13 | #include <linux/cpu.h> |
14 | #include <linux/completion.h> | ||
14 | #include <linux/capability.h> | 15 | #include <linux/capability.h> |
15 | #include <linux/device.h> | 16 | #include <linux/device.h> |
17 | #include <linux/kobject.h> | ||
16 | 18 | ||
17 | #include "cpuidle.h" | 19 | #include "cpuidle.h" |
18 | 20 | ||
@@ -167,14 +169,27 @@ struct cpuidle_attr { | |||
167 | #define define_one_rw(_name, show, store) \ | 169 | #define define_one_rw(_name, show, store) \ |
168 | static struct cpuidle_attr attr_##_name = __ATTR(_name, 0644, show, store) | 170 | static struct cpuidle_attr attr_##_name = __ATTR(_name, 0644, show, store) |
169 | 171 | ||
170 | #define kobj_to_cpuidledev(k) container_of(k, struct cpuidle_device, kobj) | ||
171 | #define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr) | 172 | #define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr) |
172 | 173 | ||
174 | struct cpuidle_device_kobj { | ||
175 | struct cpuidle_device *dev; | ||
176 | struct completion kobj_unregister; | ||
177 | struct kobject kobj; | ||
178 | }; | ||
179 | |||
180 | static inline struct cpuidle_device *to_cpuidle_device(struct kobject *kobj) | ||
181 | { | ||
182 | struct cpuidle_device_kobj *kdev = | ||
183 | container_of(kobj, struct cpuidle_device_kobj, kobj); | ||
184 | |||
185 | return kdev->dev; | ||
186 | } | ||
187 | |||
173 | static ssize_t cpuidle_show(struct kobject *kobj, struct attribute *attr, | 188 | static ssize_t cpuidle_show(struct kobject *kobj, struct attribute *attr, |
174 | char *buf) | 189 | char *buf) |
175 | { | 190 | { |
176 | int ret = -EIO; | 191 | int ret = -EIO; |
177 | struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); | 192 | struct cpuidle_device *dev = to_cpuidle_device(kobj); |
178 | struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr); | 193 | struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr); |
179 | 194 | ||
180 | if (cattr->show) { | 195 | if (cattr->show) { |
@@ -189,7 +204,7 @@ static ssize_t cpuidle_store(struct kobject *kobj, struct attribute *attr, | |||
189 | const char *buf, size_t count) | 204 | const char *buf, size_t count) |
190 | { | 205 | { |
191 | int ret = -EIO; | 206 | int ret = -EIO; |
192 | struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); | 207 | struct cpuidle_device *dev = to_cpuidle_device(kobj); |
193 | struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr); | 208 | struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr); |
194 | 209 | ||
195 | if (cattr->store) { | 210 | if (cattr->store) { |
@@ -207,9 +222,10 @@ static const struct sysfs_ops cpuidle_sysfs_ops = { | |||
207 | 222 | ||
208 | static void cpuidle_sysfs_release(struct kobject *kobj) | 223 | static void cpuidle_sysfs_release(struct kobject *kobj) |
209 | { | 224 | { |
210 | struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); | 225 | struct cpuidle_device_kobj *kdev = |
226 | container_of(kobj, struct cpuidle_device_kobj, kobj); | ||
211 | 227 | ||
212 | complete(&dev->kobj_unregister); | 228 | complete(&kdev->kobj_unregister); |
213 | } | 229 | } |
214 | 230 | ||
215 | static struct kobj_type ktype_cpuidle = { | 231 | static struct kobj_type ktype_cpuidle = { |
@@ -377,6 +393,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) | |||
377 | { | 393 | { |
378 | int i, ret = -ENOMEM; | 394 | int i, ret = -ENOMEM; |
379 | struct cpuidle_state_kobj *kobj; | 395 | struct cpuidle_state_kobj *kobj; |
396 | struct cpuidle_device_kobj *kdev = device->kobj_dev; | ||
380 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); | 397 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); |
381 | 398 | ||
382 | /* state statistics */ | 399 | /* state statistics */ |
@@ -389,7 +406,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) | |||
389 | init_completion(&kobj->kobj_unregister); | 406 | init_completion(&kobj->kobj_unregister); |
390 | 407 | ||
391 | ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, | 408 | ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, |
392 | &device->kobj, "state%d", i); | 409 | &kdev->kobj, "state%d", i); |
393 | if (ret) { | 410 | if (ret) { |
394 | kfree(kobj); | 411 | kfree(kobj); |
395 | goto error_state; | 412 | goto error_state; |
@@ -506,6 +523,7 @@ static struct kobj_type ktype_driver_cpuidle = { | |||
506 | static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) | 523 | static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) |
507 | { | 524 | { |
508 | struct cpuidle_driver_kobj *kdrv; | 525 | struct cpuidle_driver_kobj *kdrv; |
526 | struct cpuidle_device_kobj *kdev = dev->kobj_dev; | ||
509 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); | 527 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); |
510 | int ret; | 528 | int ret; |
511 | 529 | ||
@@ -517,7 +535,7 @@ static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) | |||
517 | init_completion(&kdrv->kobj_unregister); | 535 | init_completion(&kdrv->kobj_unregister); |
518 | 536 | ||
519 | ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle, | 537 | ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle, |
520 | &dev->kobj, "driver"); | 538 | &kdev->kobj, "driver"); |
521 | if (ret) { | 539 | if (ret) { |
522 | kfree(kdrv); | 540 | kfree(kdrv); |
523 | return ret; | 541 | return ret; |
@@ -586,16 +604,28 @@ void cpuidle_remove_device_sysfs(struct cpuidle_device *device) | |||
586 | */ | 604 | */ |
587 | int cpuidle_add_sysfs(struct cpuidle_device *dev) | 605 | int cpuidle_add_sysfs(struct cpuidle_device *dev) |
588 | { | 606 | { |
607 | struct cpuidle_device_kobj *kdev; | ||
589 | struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); | 608 | struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); |
590 | int error; | 609 | int error; |
591 | 610 | ||
592 | init_completion(&dev->kobj_unregister); | 611 | kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); |
612 | if (!kdev) | ||
613 | return -ENOMEM; | ||
614 | kdev->dev = dev; | ||
615 | dev->kobj_dev = kdev; | ||
616 | |||
617 | init_completion(&kdev->kobj_unregister); | ||
618 | |||
619 | error = kobject_init_and_add(&kdev->kobj, &ktype_cpuidle, &cpu_dev->kobj, | ||
620 | "cpuidle"); | ||
621 | if (error) { | ||
622 | kfree(kdev); | ||
623 | return error; | ||
624 | } | ||
593 | 625 | ||
594 | error = kobject_init_and_add(&dev->kobj, &ktype_cpuidle, &cpu_dev->kobj, | 626 | kobject_uevent(&kdev->kobj, KOBJ_ADD); |
595 | "cpuidle"); | 627 | |
596 | if (!error) | 628 | return 0; |
597 | kobject_uevent(&dev->kobj, KOBJ_ADD); | ||
598 | return error; | ||
599 | } | 629 | } |
600 | 630 | ||
601 | /** | 631 | /** |
@@ -604,6 +634,9 @@ int cpuidle_add_sysfs(struct cpuidle_device *dev) | |||
604 | */ | 634 | */ |
605 | void cpuidle_remove_sysfs(struct cpuidle_device *dev) | 635 | void cpuidle_remove_sysfs(struct cpuidle_device *dev) |
606 | { | 636 | { |
607 | kobject_put(&dev->kobj); | 637 | struct cpuidle_device_kobj *kdev = dev->kobj_dev; |
608 | wait_for_completion(&dev->kobj_unregister); | 638 | |
639 | kobject_put(&kdev->kobj); | ||
640 | wait_for_completion(&kdev->kobj_unregister); | ||
641 | kfree(kdev); | ||
609 | } | 642 | } |