747 lines · 19.2 KB · C
RawDownload
/* * sysfs.c - sysfs support * * (C) 2006-2007 Shaohua Li <shaohua.li@intel.com> * * This code is licenced under the GPL. */ #include <linux/kernel.h>#include <linux/cpuidle.h>#include <linux/sysfs.h>#include <linux/slab.h>#include <linux/cpu.h>#include <linux/completion.h>#include <linux/capability.h>#include <linux/device.h>#include <linux/kobject.h> #include "cpuidle.h" static ssize_t show_available_governors(struct device *dev,					struct device_attribute *attr,					char *buf){	ssize_t i = 0;	struct cpuidle_governor *tmp; 	mutex_lock(&cpuidle_lock);	list_for_each_entry(tmp, &cpuidle_governors, governor_list) {		if (i >= (ssize_t)(PAGE_SIZE - (CPUIDLE_NAME_LEN + 2)))			goto out; 		i += sysfs_emit_at(buf, i, "%.*s ", CPUIDLE_NAME_LEN, tmp->name);	} out:	i += sysfs_emit_at(buf, i, "\n");	mutex_unlock(&cpuidle_lock);	return i;} static ssize_t show_current_driver(struct device *dev,				   struct device_attribute *attr,				   char *buf){	ssize_t ret;	struct cpuidle_driver *drv; 	spin_lock(&cpuidle_driver_lock);	drv = cpuidle_get_driver();	if (drv)		ret = sysfs_emit(buf, "%s\n", drv->name);	else		ret = sysfs_emit(buf, "none\n");	spin_unlock(&cpuidle_driver_lock); 	return ret;} static ssize_t show_current_governor(struct device *dev,				     struct device_attribute *attr,				     char *buf){	ssize_t ret; 	mutex_lock(&cpuidle_lock);	if (cpuidle_curr_governor)		ret = sysfs_emit(buf, "%s\n", cpuidle_curr_governor->name);	else		ret = sysfs_emit(buf, "none\n");	mutex_unlock(&cpuidle_lock); 	return ret;} static ssize_t store_current_governor(struct device *dev,				      struct device_attribute *attr,				      const char *buf, size_t count){	char gov_name[CPUIDLE_NAME_LEN + 1];	int ret;	struct cpuidle_governor *gov; 	ret = sscanf(buf, "%" __stringify(CPUIDLE_NAME_LEN) "s", gov_name);	if (ret != 1)		return -EINVAL; 	mutex_lock(&cpuidle_lock);	ret = -EINVAL;	list_for_each_entry(gov, &cpuidle_governors, governor_list) {		if (!strncmp(gov->name, gov_name, CPUIDLE_NAME_LEN)) {			ret = cpuidle_switch_governor(gov);			break;		}	}	mutex_unlock(&cpuidle_lock); 	return ret ? ret : count;} static DEVICE_ATTR(available_governors, 0444, show_available_governors, NULL);static DEVICE_ATTR(current_driver, 0444, show_current_driver, NULL);static DEVICE_ATTR(current_governor, 0644, show_current_governor,				   store_current_governor);static DEVICE_ATTR(current_governor_ro, 0444, show_current_governor, NULL); static struct attribute *cpuidle_attrs[] = {	&dev_attr_available_governors.attr,	&dev_attr_current_driver.attr,	&dev_attr_current_governor.attr,	&dev_attr_current_governor_ro.attr,	NULL}; static struct attribute_group cpuidle_attr_group = {	.attrs = cpuidle_attrs,	.name = "cpuidle",}; /** * cpuidle_add_interface - add CPU global sysfs attributes */int cpuidle_add_interface(void){	struct device *dev_root = bus_get_dev_root(&cpu_subsys);	int retval; 	if (!dev_root)		return -EINVAL; 	retval = sysfs_create_group(&dev_root->kobj, &cpuidle_attr_group);	put_device(dev_root);	return retval;} /** * cpuidle_remove_interface - remove CPU global sysfs attributes * @dev: the target device */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 attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr) struct cpuidle_device_kobj {	struct cpuidle_device *dev;	struct completion kobj_unregister;	struct kobject kobj;}; static inline struct cpuidle_device *to_cpuidle_device(struct kobject *kobj){	struct cpuidle_device_kobj *kdev =		container_of(kobj, struct cpuidle_device_kobj, kobj); 	return kdev->dev;} static ssize_t cpuidle_show(struct kobject *kobj, struct attribute *attr,			    char *buf){	int ret = -EIO;	struct cpuidle_device *dev = to_cpuidle_device(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 = to_cpuidle_device(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_kobj *kdev =		container_of(kobj, struct cpuidle_device_kobj, kobj); 	complete(&kdev->kobj_unregister);} static const 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 *, \			struct cpuidle_state_usage *, 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_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) \{ \	return sysfs_emit(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 sysfs_emit(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 sysfs_emit(buf, "<null>\n");\	return sysfs_emit(buf, "%s\n", state->_name);\} #define define_show_state_time_function(_name) \static ssize_t show_state_##_name(struct cpuidle_state *state, \				  struct cpuidle_state_usage *state_usage, \				  char *buf) \{ \	return sysfs_emit(buf, "%llu\n", ktime_to_us(state->_name##_ns)); \} define_show_state_time_function(exit_latency)define_show_state_time_function(target_residency)define_show_state_function(power_usage)define_show_state_ull_function(usage)define_show_state_ull_function(rejected)define_show_state_str_function(name)define_show_state_str_function(desc)define_show_state_ull_function(above)define_show_state_ull_function(below) static ssize_t show_state_time(struct cpuidle_state *state,			       struct cpuidle_state_usage *state_usage,			       char *buf){	return sysfs_emit(buf, "%llu\n", ktime_to_us(state_usage->time_ns));} static ssize_t show_state_disable(struct cpuidle_state *state,				  struct cpuidle_state_usage *state_usage,				  char *buf){	return sysfs_emit(buf, "%llu\n",		       state_usage->disable & CPUIDLE_STATE_DISABLED_BY_USER);} static ssize_t store_state_disable(struct cpuidle_state *state,				   struct cpuidle_state_usage *state_usage,				   const char *buf, size_t size){	unsigned int value;	int err; 	if (!capable(CAP_SYS_ADMIN))		return -EPERM; 	err = kstrtouint(buf, 0, &value);	if (err)		return err; 	if (value)		state_usage->disable |= CPUIDLE_STATE_DISABLED_BY_USER;	else		state_usage->disable &= ~CPUIDLE_STATE_DISABLED_BY_USER; 	return size;} static ssize_t show_state_default_status(struct cpuidle_state *state,					  struct cpuidle_state_usage *state_usage,					  char *buf){	return sysfs_emit(buf, "%s\n",		       state->flags & CPUIDLE_FLAG_OFF ? "disabled" : "enabled");} 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(residency, show_state_target_residency);define_one_state_ro(power, show_state_power_usage);define_one_state_ro(usage, show_state_usage);define_one_state_ro(rejected, show_state_rejected);define_one_state_ro(time, show_state_time);define_one_state_rw(disable, show_state_disable, store_state_disable);define_one_state_ro(above, show_state_above);define_one_state_ro(below, show_state_below);define_one_state_ro(default_status, show_state_default_status); static struct attribute *cpuidle_state_default_attrs[] = {	&attr_name.attr,	&attr_desc.attr,	&attr_latency.attr,	&attr_residency.attr,	&attr_power.attr,	&attr_usage.attr,	&attr_rejected.attr,	&attr_time.attr,	&attr_disable.attr,	&attr_above.attr,	&attr_below.attr,	&attr_default_status.attr,	NULL};ATTRIBUTE_GROUPS(cpuidle_state_default); struct cpuidle_state_kobj {	struct cpuidle_state *state;	struct cpuidle_state_usage *state_usage;	struct completion kobj_unregister;	struct kobject kobj;	struct cpuidle_device *device;}; #ifdef CONFIG_SUSPEND#define define_show_state_s2idle_ull_function(_name) \static ssize_t show_state_s2idle_##_name(struct cpuidle_state *state, \					 struct cpuidle_state_usage *state_usage, \					 char *buf)				\{ \	return sysfs_emit(buf, "%llu\n", state_usage->s2idle_##_name);\} define_show_state_s2idle_ull_function(usage);define_show_state_s2idle_ull_function(time); #define define_one_state_s2idle_ro(_name, show) \static struct cpuidle_state_attr attr_s2idle_##_name = \	__ATTR(_name, 0444, show, NULL) define_one_state_s2idle_ro(usage, show_state_s2idle_usage);define_one_state_s2idle_ro(time, show_state_s2idle_time); static struct attribute *cpuidle_state_s2idle_attrs[] = {	&attr_s2idle_usage.attr,	&attr_s2idle_time.attr,	NULL}; static const struct attribute_group cpuidle_state_s2idle_group = {	.name	= "s2idle",	.attrs	= cpuidle_state_s2idle_attrs,}; static void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj){	int ret; 	if (!kobj->state->enter_s2idle)		return; 	ret = sysfs_create_group(&kobj->kobj, &cpuidle_state_s2idle_group);	if (ret)		pr_debug("%s: sysfs attribute group not created\n", __func__);} static void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj){	if (kobj->state->enter_s2idle)		sysfs_remove_group(&kobj->kobj, &cpuidle_state_s2idle_group);}#elsestatic inline void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }static inline void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }#endif /* CONFIG_SUSPEND */ #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 kobj_to_device(k) (kobj_to_state_obj(k)->device)#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 ssize_t cpuidle_state_store(struct kobject *kobj, struct attribute *attr,				   const char *buf, size_t size){	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);	struct cpuidle_device *dev = kobj_to_device(kobj); 	if (cattr->store)		ret = cattr->store(state, state_usage, buf, size); 	/* reset poll time cache */	dev->poll_limit_ns = 0; 	return ret;} static const struct sysfs_ops cpuidle_state_sysfs_ops = {	.show = cpuidle_state_show,	.store = cpuidle_state_store,}; 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 const struct kobj_type ktype_state_cpuidle = {	.sysfs_ops = &cpuidle_state_sysfs_ops,	.default_groups = cpuidle_state_default_groups,	.release = cpuidle_state_sysfs_release,}; static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i){	cpuidle_remove_s2idle_attr_group(device->kobjs[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_state_sysfs - adds cpuidle states sysfs attributes * @device: the target device */static int cpuidle_add_state_sysfs(struct cpuidle_device *device){	int i, ret = -ENOMEM;	struct cpuidle_state_kobj *kobj;	struct cpuidle_device_kobj *kdev = device->kobj_dev;	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); 	/* state statistics */	for (i = 0; i < drv->state_count; i++) {		kobj = kzalloc_obj(struct cpuidle_state_kobj);		if (!kobj) {			ret = -ENOMEM;			goto error_state;		}		kobj->state = &drv->states[i];		kobj->state_usage = &device->states_usage[i];		kobj->device = device;		init_completion(&kobj->kobj_unregister); 		ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle,					   &kdev->kobj, "state%d", i);		if (ret) {			kobject_put(&kobj->kobj);			kfree(kobj);			goto error_state;

Lines 1–500 of 747.