brintos

brintos / linux-shallow public Read only

0
0
Text · 19.2 KiB · d6f5da6 Raw
748 lines · c
1/*2 * sysfs.c - sysfs support3 *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/slab.h>13#include <linux/cpu.h>14#include <linux/completion.h>15#include <linux/capability.h>16#include <linux/device.h>17#include <linux/kobject.h>18 19#include "cpuidle.h"20 21static ssize_t show_available_governors(struct device *dev,22					struct device_attribute *attr,23					char *buf)24{25	ssize_t i = 0;26	struct cpuidle_governor *tmp;27 28	mutex_lock(&cpuidle_lock);29	list_for_each_entry(tmp, &cpuidle_governors, governor_list) {30		if (i >= (ssize_t) (PAGE_SIZE - (CPUIDLE_NAME_LEN + 2)))31			goto out;32 33		i += scnprintf(&buf[i], CPUIDLE_NAME_LEN + 1, "%s ", tmp->name);34	}35 36out:37	i+= sprintf(&buf[i], "\n");38	mutex_unlock(&cpuidle_lock);39	return i;40}41 42static ssize_t show_current_driver(struct device *dev,43				   struct device_attribute *attr,44				   char *buf)45{46	ssize_t ret;47	struct cpuidle_driver *drv;48 49	spin_lock(&cpuidle_driver_lock);50	drv = cpuidle_get_driver();51	if (drv)52		ret = sprintf(buf, "%s\n", drv->name);53	else54		ret = sprintf(buf, "none\n");55	spin_unlock(&cpuidle_driver_lock);56 57	return ret;58}59 60static ssize_t show_current_governor(struct device *dev,61				     struct device_attribute *attr,62				     char *buf)63{64	ssize_t ret;65 66	mutex_lock(&cpuidle_lock);67	if (cpuidle_curr_governor)68		ret = sprintf(buf, "%s\n", cpuidle_curr_governor->name);69	else70		ret = sprintf(buf, "none\n");71	mutex_unlock(&cpuidle_lock);72 73	return ret;74}75 76static ssize_t store_current_governor(struct device *dev,77				      struct device_attribute *attr,78				      const char *buf, size_t count)79{80	char gov_name[CPUIDLE_NAME_LEN + 1];81	int ret;82	struct cpuidle_governor *gov;83 84	ret = sscanf(buf, "%" __stringify(CPUIDLE_NAME_LEN) "s", gov_name);85	if (ret != 1)86		return -EINVAL;87 88	mutex_lock(&cpuidle_lock);89	ret = -EINVAL;90	list_for_each_entry(gov, &cpuidle_governors, governor_list) {91		if (!strncmp(gov->name, gov_name, CPUIDLE_NAME_LEN)) {92			ret = cpuidle_switch_governor(gov);93			break;94		}95	}96	mutex_unlock(&cpuidle_lock);97 98	return ret ? ret : count;99}100 101static DEVICE_ATTR(available_governors, 0444, show_available_governors, NULL);102static DEVICE_ATTR(current_driver, 0444, show_current_driver, NULL);103static DEVICE_ATTR(current_governor, 0644, show_current_governor,104				   store_current_governor);105static DEVICE_ATTR(current_governor_ro, 0444, show_current_governor, NULL);106 107static struct attribute *cpuidle_attrs[] = {108	&dev_attr_available_governors.attr,109	&dev_attr_current_driver.attr,110	&dev_attr_current_governor.attr,111	&dev_attr_current_governor_ro.attr,112	NULL113};114 115static struct attribute_group cpuidle_attr_group = {116	.attrs = cpuidle_attrs,117	.name = "cpuidle",118};119 120/**121 * cpuidle_add_interface - add CPU global sysfs attributes122 */123int cpuidle_add_interface(void)124{125	struct device *dev_root = bus_get_dev_root(&cpu_subsys);126	int retval;127 128	if (!dev_root)129		return -EINVAL;130 131	retval = sysfs_create_group(&dev_root->kobj, &cpuidle_attr_group);132	put_device(dev_root);133	return retval;134}135 136/**137 * cpuidle_remove_interface - remove CPU global sysfs attributes138 * @dev: the target device139 */140void cpuidle_remove_interface(struct device *dev)141{142	sysfs_remove_group(&dev->kobj, &cpuidle_attr_group);143}144 145struct cpuidle_attr {146	struct attribute attr;147	ssize_t (*show)(struct cpuidle_device *, char *);148	ssize_t (*store)(struct cpuidle_device *, const char *, size_t count);149};150 151#define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr)152 153struct cpuidle_device_kobj {154	struct cpuidle_device *dev;155	struct completion kobj_unregister;156	struct kobject kobj;157};158 159static inline struct cpuidle_device *to_cpuidle_device(struct kobject *kobj)160{161	struct cpuidle_device_kobj *kdev =162		container_of(kobj, struct cpuidle_device_kobj, kobj);163 164	return kdev->dev;165}166 167static ssize_t cpuidle_show(struct kobject *kobj, struct attribute *attr,168			    char *buf)169{170	int ret = -EIO;171	struct cpuidle_device *dev = to_cpuidle_device(kobj);172	struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr);173 174	if (cattr->show) {175		mutex_lock(&cpuidle_lock);176		ret = cattr->show(dev, buf);177		mutex_unlock(&cpuidle_lock);178	}179	return ret;180}181 182static ssize_t cpuidle_store(struct kobject *kobj, struct attribute *attr,183			     const char *buf, size_t count)184{185	int ret = -EIO;186	struct cpuidle_device *dev = to_cpuidle_device(kobj);187	struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr);188 189	if (cattr->store) {190		mutex_lock(&cpuidle_lock);191		ret = cattr->store(dev, buf, count);192		mutex_unlock(&cpuidle_lock);193	}194	return ret;195}196 197static const struct sysfs_ops cpuidle_sysfs_ops = {198	.show = cpuidle_show,199	.store = cpuidle_store,200};201 202static void cpuidle_sysfs_release(struct kobject *kobj)203{204	struct cpuidle_device_kobj *kdev =205		container_of(kobj, struct cpuidle_device_kobj, kobj);206 207	complete(&kdev->kobj_unregister);208}209 210static const struct kobj_type ktype_cpuidle = {211	.sysfs_ops = &cpuidle_sysfs_ops,212	.release = cpuidle_sysfs_release,213};214 215struct cpuidle_state_attr {216	struct attribute attr;217	ssize_t (*show)(struct cpuidle_state *, \218					struct cpuidle_state_usage *, char *);219	ssize_t (*store)(struct cpuidle_state *, \220			struct cpuidle_state_usage *, const char *, size_t);221};222 223#define define_one_state_ro(_name, show) \224static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL)225 226#define define_one_state_rw(_name, show, store) \227static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0644, show, store)228 229#define define_show_state_function(_name) \230static ssize_t show_state_##_name(struct cpuidle_state *state, \231			 struct cpuidle_state_usage *state_usage, char *buf) \232{ \233	return sprintf(buf, "%u\n", state->_name);\234}235 236#define define_show_state_ull_function(_name) \237static ssize_t show_state_##_name(struct cpuidle_state *state, \238				  struct cpuidle_state_usage *state_usage, \239				  char *buf)				\240{ \241	return sprintf(buf, "%llu\n", state_usage->_name);\242}243 244#define define_show_state_str_function(_name) \245static ssize_t show_state_##_name(struct cpuidle_state *state, \246				  struct cpuidle_state_usage *state_usage, \247				  char *buf)				\248{ \249	if (state->_name[0] == '\0')\250		return sprintf(buf, "<null>\n");\251	return sprintf(buf, "%s\n", state->_name);\252}253 254#define define_show_state_time_function(_name) \255static ssize_t show_state_##_name(struct cpuidle_state *state, \256				  struct cpuidle_state_usage *state_usage, \257				  char *buf) \258{ \259	return sprintf(buf, "%llu\n", ktime_to_us(state->_name##_ns)); \260}261 262define_show_state_time_function(exit_latency)263define_show_state_time_function(target_residency)264define_show_state_function(power_usage)265define_show_state_ull_function(usage)266define_show_state_ull_function(rejected)267define_show_state_str_function(name)268define_show_state_str_function(desc)269define_show_state_ull_function(above)270define_show_state_ull_function(below)271 272static ssize_t show_state_time(struct cpuidle_state *state,273			       struct cpuidle_state_usage *state_usage,274			       char *buf)275{276	return sprintf(buf, "%llu\n", ktime_to_us(state_usage->time_ns));277}278 279static ssize_t show_state_disable(struct cpuidle_state *state,280				  struct cpuidle_state_usage *state_usage,281				  char *buf)282{283	return sprintf(buf, "%llu\n",284		       state_usage->disable & CPUIDLE_STATE_DISABLED_BY_USER);285}286 287static ssize_t store_state_disable(struct cpuidle_state *state,288				   struct cpuidle_state_usage *state_usage,289				   const char *buf, size_t size)290{291	unsigned int value;292	int err;293 294	if (!capable(CAP_SYS_ADMIN))295		return -EPERM;296 297	err = kstrtouint(buf, 0, &value);298	if (err)299		return err;300 301	if (value)302		state_usage->disable |= CPUIDLE_STATE_DISABLED_BY_USER;303	else304		state_usage->disable &= ~CPUIDLE_STATE_DISABLED_BY_USER;305 306	return size;307}308 309static ssize_t show_state_default_status(struct cpuidle_state *state,310					  struct cpuidle_state_usage *state_usage,311					  char *buf)312{313	return sprintf(buf, "%s\n",314		       state->flags & CPUIDLE_FLAG_OFF ? "disabled" : "enabled");315}316 317define_one_state_ro(name, show_state_name);318define_one_state_ro(desc, show_state_desc);319define_one_state_ro(latency, show_state_exit_latency);320define_one_state_ro(residency, show_state_target_residency);321define_one_state_ro(power, show_state_power_usage);322define_one_state_ro(usage, show_state_usage);323define_one_state_ro(rejected, show_state_rejected);324define_one_state_ro(time, show_state_time);325define_one_state_rw(disable, show_state_disable, store_state_disable);326define_one_state_ro(above, show_state_above);327define_one_state_ro(below, show_state_below);328define_one_state_ro(default_status, show_state_default_status);329 330static struct attribute *cpuidle_state_default_attrs[] = {331	&attr_name.attr,332	&attr_desc.attr,333	&attr_latency.attr,334	&attr_residency.attr,335	&attr_power.attr,336	&attr_usage.attr,337	&attr_rejected.attr,338	&attr_time.attr,339	&attr_disable.attr,340	&attr_above.attr,341	&attr_below.attr,342	&attr_default_status.attr,343	NULL344};345ATTRIBUTE_GROUPS(cpuidle_state_default);346 347struct cpuidle_state_kobj {348	struct cpuidle_state *state;349	struct cpuidle_state_usage *state_usage;350	struct completion kobj_unregister;351	struct kobject kobj;352	struct cpuidle_device *device;353};354 355#ifdef CONFIG_SUSPEND356#define define_show_state_s2idle_ull_function(_name) \357static ssize_t show_state_s2idle_##_name(struct cpuidle_state *state, \358					 struct cpuidle_state_usage *state_usage, \359					 char *buf)				\360{ \361	return sprintf(buf, "%llu\n", state_usage->s2idle_##_name);\362}363 364define_show_state_s2idle_ull_function(usage);365define_show_state_s2idle_ull_function(time);366 367#define define_one_state_s2idle_ro(_name, show) \368static struct cpuidle_state_attr attr_s2idle_##_name = \369	__ATTR(_name, 0444, show, NULL)370 371define_one_state_s2idle_ro(usage, show_state_s2idle_usage);372define_one_state_s2idle_ro(time, show_state_s2idle_time);373 374static struct attribute *cpuidle_state_s2idle_attrs[] = {375	&attr_s2idle_usage.attr,376	&attr_s2idle_time.attr,377	NULL378};379 380static const struct attribute_group cpuidle_state_s2idle_group = {381	.name	= "s2idle",382	.attrs	= cpuidle_state_s2idle_attrs,383};384 385static void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj)386{387	int ret;388 389	if (!kobj->state->enter_s2idle)390		return;391 392	ret = sysfs_create_group(&kobj->kobj, &cpuidle_state_s2idle_group);393	if (ret)394		pr_debug("%s: sysfs attribute group not created\n", __func__);395}396 397static void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj)398{399	if (kobj->state->enter_s2idle)400		sysfs_remove_group(&kobj->kobj, &cpuidle_state_s2idle_group);401}402#else403static inline void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }404static inline void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }405#endif /* CONFIG_SUSPEND */406 407#define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj)408#define kobj_to_state(k) (kobj_to_state_obj(k)->state)409#define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage)410#define kobj_to_device(k) (kobj_to_state_obj(k)->device)411#define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr)412 413static ssize_t cpuidle_state_show(struct kobject *kobj, struct attribute *attr,414				  char *buf)415{416	int ret = -EIO;417	struct cpuidle_state *state = kobj_to_state(kobj);418	struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj);419	struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);420 421	if (cattr->show)422		ret = cattr->show(state, state_usage, buf);423 424	return ret;425}426 427static ssize_t cpuidle_state_store(struct kobject *kobj, struct attribute *attr,428				   const char *buf, size_t size)429{430	int ret = -EIO;431	struct cpuidle_state *state = kobj_to_state(kobj);432	struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj);433	struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);434	struct cpuidle_device *dev = kobj_to_device(kobj);435 436	if (cattr->store)437		ret = cattr->store(state, state_usage, buf, size);438 439	/* reset poll time cache */440	dev->poll_limit_ns = 0;441 442	return ret;443}444 445static const struct sysfs_ops cpuidle_state_sysfs_ops = {446	.show = cpuidle_state_show,447	.store = cpuidle_state_store,448};449 450static void cpuidle_state_sysfs_release(struct kobject *kobj)451{452	struct cpuidle_state_kobj *state_obj = kobj_to_state_obj(kobj);453 454	complete(&state_obj->kobj_unregister);455}456 457static const struct kobj_type ktype_state_cpuidle = {458	.sysfs_ops = &cpuidle_state_sysfs_ops,459	.default_groups = cpuidle_state_default_groups,460	.release = cpuidle_state_sysfs_release,461};462 463static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i)464{465	cpuidle_remove_s2idle_attr_group(device->kobjs[i]);466	kobject_put(&device->kobjs[i]->kobj);467	wait_for_completion(&device->kobjs[i]->kobj_unregister);468	kfree(device->kobjs[i]);469	device->kobjs[i] = NULL;470}471 472/**473 * cpuidle_add_state_sysfs - adds cpuidle states sysfs attributes474 * @device: the target device475 */476static int cpuidle_add_state_sysfs(struct cpuidle_device *device)477{478	int i, ret = -ENOMEM;479	struct cpuidle_state_kobj *kobj;480	struct cpuidle_device_kobj *kdev = device->kobj_dev;481	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device);482 483	/* state statistics */484	for (i = 0; i < drv->state_count; i++) {485		kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL);486		if (!kobj) {487			ret = -ENOMEM;488			goto error_state;489		}490		kobj->state = &drv->states[i];491		kobj->state_usage = &device->states_usage[i];492		kobj->device = device;493		init_completion(&kobj->kobj_unregister);494 495		ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle,496					   &kdev->kobj, "state%d", i);497		if (ret) {498			kobject_put(&kobj->kobj);499			kfree(kobj);500			goto error_state;501		}502		cpuidle_add_s2idle_attr_group(kobj);503		kobject_uevent(&kobj->kobj, KOBJ_ADD);504		device->kobjs[i] = kobj;505	}506 507	return 0;508 509error_state:510	for (i = i - 1; i >= 0; i--)511		cpuidle_free_state_kobj(device, i);512	return ret;513}514 515/**516 * cpuidle_remove_state_sysfs - removes the cpuidle states sysfs attributes517 * @device: the target device518 */519static void cpuidle_remove_state_sysfs(struct cpuidle_device *device)520{521	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device);522	int i;523 524	for (i = 0; i < drv->state_count; i++)525		cpuidle_free_state_kobj(device, i);526}527 528#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS529#define kobj_to_driver_kobj(k) container_of(k, struct cpuidle_driver_kobj, kobj)530#define attr_to_driver_attr(a) container_of(a, struct cpuidle_driver_attr, attr)531 532#define define_one_driver_ro(_name, show)                       \533	static struct cpuidle_driver_attr attr_driver_##_name = \534		__ATTR(_name, 0444, show, NULL)535 536struct cpuidle_driver_kobj {537	struct cpuidle_driver *drv;538	struct completion kobj_unregister;539	struct kobject kobj;540};541 542struct cpuidle_driver_attr {543	struct attribute attr;544	ssize_t (*show)(struct cpuidle_driver *, char *);545	ssize_t (*store)(struct cpuidle_driver *, const char *, size_t);546};547 548static ssize_t show_driver_name(struct cpuidle_driver *drv, char *buf)549{550	ssize_t ret;551 552	spin_lock(&cpuidle_driver_lock);553	ret = sprintf(buf, "%s\n", drv ? drv->name : "none");554	spin_unlock(&cpuidle_driver_lock);555 556	return ret;557}558 559static void cpuidle_driver_sysfs_release(struct kobject *kobj)560{561	struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj);562	complete(&driver_kobj->kobj_unregister);563}564 565static ssize_t cpuidle_driver_show(struct kobject *kobj, struct attribute *attr,566				   char *buf)567{568	int ret = -EIO;569	struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj);570	struct cpuidle_driver_attr *dattr = attr_to_driver_attr(attr);571 572	if (dattr->show)573		ret = dattr->show(driver_kobj->drv, buf);574 575	return ret;576}577 578static ssize_t cpuidle_driver_store(struct kobject *kobj, struct attribute *attr,579				    const char *buf, size_t size)580{581	int ret = -EIO;582	struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj);583	struct cpuidle_driver_attr *dattr = attr_to_driver_attr(attr);584 585	if (dattr->store)586		ret = dattr->store(driver_kobj->drv, buf, size);587 588	return ret;589}590 591define_one_driver_ro(name, show_driver_name);592 593static const struct sysfs_ops cpuidle_driver_sysfs_ops = {594	.show = cpuidle_driver_show,595	.store = cpuidle_driver_store,596};597 598static struct attribute *cpuidle_driver_default_attrs[] = {599	&attr_driver_name.attr,600	NULL601};602ATTRIBUTE_GROUPS(cpuidle_driver_default);603 604static const struct kobj_type ktype_driver_cpuidle = {605	.sysfs_ops = &cpuidle_driver_sysfs_ops,606	.default_groups = cpuidle_driver_default_groups,607	.release = cpuidle_driver_sysfs_release,608};609 610/**611 * cpuidle_add_driver_sysfs - adds the driver name sysfs attribute612 * @dev: the target device613 */614static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev)615{616	struct cpuidle_driver_kobj *kdrv;617	struct cpuidle_device_kobj *kdev = dev->kobj_dev;618	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);619	int ret;620 621	kdrv = kzalloc(sizeof(*kdrv), GFP_KERNEL);622	if (!kdrv)623		return -ENOMEM;624 625	kdrv->drv = drv;626	init_completion(&kdrv->kobj_unregister);627 628	ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle,629				   &kdev->kobj, "driver");630	if (ret) {631		kobject_put(&kdrv->kobj);632		kfree(kdrv);633		return ret;634	}635 636	kobject_uevent(&kdrv->kobj, KOBJ_ADD);637	dev->kobj_driver = kdrv;638 639	return ret;640}641 642/**643 * cpuidle_remove_driver_sysfs - removes the driver name sysfs attribute644 * @dev: the target device645 */646static void cpuidle_remove_driver_sysfs(struct cpuidle_device *dev)647{648	struct cpuidle_driver_kobj *kdrv = dev->kobj_driver;649	kobject_put(&kdrv->kobj);650	wait_for_completion(&kdrv->kobj_unregister);651	kfree(kdrv);652}653#else654static inline int cpuidle_add_driver_sysfs(struct cpuidle_device *dev)655{656	return 0;657}658 659static inline void cpuidle_remove_driver_sysfs(struct cpuidle_device *dev)660{661	;662}663#endif664 665/**666 * cpuidle_add_device_sysfs - adds device specific sysfs attributes667 * @device: the target device668 */669int cpuidle_add_device_sysfs(struct cpuidle_device *device)670{671	int ret;672 673	ret = cpuidle_add_state_sysfs(device);674	if (ret)675		return ret;676 677	ret = cpuidle_add_driver_sysfs(device);678	if (ret)679		cpuidle_remove_state_sysfs(device);680	return ret;681}682 683/**684 * cpuidle_remove_device_sysfs : removes device specific sysfs attributes685 * @device : the target device686 */687void cpuidle_remove_device_sysfs(struct cpuidle_device *device)688{689	cpuidle_remove_driver_sysfs(device);690	cpuidle_remove_state_sysfs(device);691}692 693/**694 * cpuidle_add_sysfs - creates a sysfs instance for the target device695 * @dev: the target device696 */697int cpuidle_add_sysfs(struct cpuidle_device *dev)698{699	struct cpuidle_device_kobj *kdev;700	struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu);701	int error;702 703	/*704	 * Return if cpu_device is not setup for this CPU.705	 *706	 * This could happen if the arch did not set up cpu_device707	 * since this CPU is not in cpu_present mask and the708	 * driver did not send a correct CPU mask during registration.709	 * Without this check we would end up passing bogus710	 * value for &cpu_dev->kobj in kobject_init_and_add()711	 */712	if (!cpu_dev)713		return -ENODEV;714 715	kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);716	if (!kdev)717		return -ENOMEM;718	kdev->dev = dev;719 720	init_completion(&kdev->kobj_unregister);721 722	error = kobject_init_and_add(&kdev->kobj, &ktype_cpuidle, &cpu_dev->kobj,723				   "cpuidle");724	if (error) {725		kobject_put(&kdev->kobj);726		kfree(kdev);727		return error;728	}729 730	dev->kobj_dev = kdev;731	kobject_uevent(&kdev->kobj, KOBJ_ADD);732 733	return 0;734}735 736/**737 * cpuidle_remove_sysfs - deletes a sysfs instance on the target device738 * @dev: the target device739 */740void cpuidle_remove_sysfs(struct cpuidle_device *dev)741{742	struct cpuidle_device_kobj *kdev = dev->kobj_dev;743 744	kobject_put(&kdev->kobj);745	wait_for_completion(&kdev->kobj_unregister);746	kfree(kdev);747}748