summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/Kconfig3
-rw-r--r--drivers/base/base.h27
-rw-r--r--drivers/base/class.c153
-rw-r--r--drivers/base/core.c196
-rw-r--r--drivers/base/cpu.c10
-rw-r--r--drivers/base/memory.c12
-rw-r--r--drivers/base/node.c15
-rw-r--r--drivers/base/power/trace.c2
-rw-r--r--drivers/base/sys.c64
-rw-r--r--drivers/base/topology.c17
10 files changed, 338 insertions, 161 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index d47482fa1d21..6318f6b57360 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -27,8 +27,9 @@ config PREVENT_FIRMWARE_BUILD
If unsure say Y here.
config FW_LOADER
- tristate "Userspace firmware loading support"
+ tristate "Userspace firmware loading support" if EMBEDDED
depends on HOTPLUG
+ default y
---help---
This option is provided for the case where no in-kernel-tree modules
require userspace firmware loading support, but a module built outside
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 2c9ae43e2219..31dc0cd84afa 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -36,6 +36,33 @@ struct driver_private {
};
#define to_driver(obj) container_of(obj, struct driver_private, kobj)
+
+/**
+ * struct class_private - structure to hold the private to the driver core portions of the class structure.
+ *
+ * @class_subsys - the struct kset that defines this class. This is the main kobject
+ * @class_devices - list of devices associated with this class
+ * @class_interfaces - list of class_interfaces associated with this class
+ * @class_dirs - "glue" directory for virtual devices associated with this class
+ * @class_mutex - mutex to protect the children, devices, and interfaces lists.
+ * @class - pointer back to the struct class that this structure is associated
+ * with.
+ *
+ * This structure is the one that is the actual kobject allowing struct
+ * class to be statically allocated safely. Nothing outside of the driver
+ * core should ever touch these fields.
+ */
+struct class_private {
+ struct kset class_subsys;
+ struct list_head class_devices;
+ struct list_head class_interfaces;
+ struct kset class_dirs;
+ struct mutex class_mutex;
+ struct class *class;
+};
+#define to_class(obj) \
+ container_of(obj, struct class_private, class_subsys.kobj)
+
/* initialisation functions */
extern int devices_init(void);
extern int buses_init(void);
diff --git a/drivers/base/class.c b/drivers/base/class.c
index e085af0ff94f..839d27cecb36 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -18,20 +18,20 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/genhd.h>
+#include <linux/mutex.h>
#include "base.h"
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
-#define to_class(obj) container_of(obj, struct class, subsys.kobj)
static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct class_attribute *class_attr = to_class_attr(attr);
- struct class *dc = to_class(kobj);
+ struct class_private *cp = to_class(kobj);
ssize_t ret = -EIO;
if (class_attr->show)
- ret = class_attr->show(dc, buf);
+ ret = class_attr->show(cp->class, buf);
return ret;
}
@@ -39,17 +39,18 @@ static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct class_attribute *class_attr = to_class_attr(attr);
- struct class *dc = to_class(kobj);
+ struct class_private *cp = to_class(kobj);
ssize_t ret = -EIO;
if (class_attr->store)
- ret = class_attr->store(dc, buf, count);
+ ret = class_attr->store(cp->class, buf, count);
return ret;
}
static void class_release(struct kobject *kobj)
{
- struct class *class = to_class(kobj);
+ struct class_private *cp = to_class(kobj);
+ struct class *class = cp->class;
pr_debug("class '%s': release.\n", class->name);
@@ -70,7 +71,7 @@ static struct kobj_type class_ktype = {
.release = class_release,
};
-/* Hotplug events for classes go to the class_obj subsys */
+/* Hotplug events for classes go to the class class_subsys */
static struct kset *class_kset;
@@ -78,7 +79,8 @@ int class_create_file(struct class *cls, const struct class_attribute *attr)
{
int error;
if (cls)
- error = sysfs_create_file(&cls->subsys.kobj, &attr->attr);
+ error = sysfs_create_file(&cls->p->class_subsys.kobj,
+ &attr->attr);
else
error = -EINVAL;
return error;
@@ -87,21 +89,20 @@ int class_create_file(struct class *cls, const struct class_attribute *attr)
void class_remove_file(struct class *cls, const struct class_attribute *attr)
{
if (cls)
- sysfs_remove_file(&cls->subsys.kobj, &attr->attr);
+ sysfs_remove_file(&cls->p->class_subsys.kobj, &attr->attr);
}
static struct class *class_get(struct class *cls)
{
if (cls)
- return container_of(kset_get(&cls->subsys),
- struct class, subsys);
- return NULL;
+ kset_get(&cls->p->class_subsys);
+ return cls;
}
static void class_put(struct class *cls)
{
if (cls)
- kset_put(&cls->subsys);
+ kset_put(&cls->p->class_subsys);
}
static int add_class_attrs(struct class *cls)
@@ -134,42 +135,57 @@ static void remove_class_attrs(struct class *cls)
}
}
-int class_register(struct class *cls)
+int __class_register(struct class *cls, struct lock_class_key *key)
{
+ struct class_private *cp;
int error;
pr_debug("device class '%s': registering\n", cls->name);
- INIT_LIST_HEAD(&cls->devices);
- INIT_LIST_HEAD(&cls->interfaces);
- kset_init(&cls->class_dirs);
- init_MUTEX(&cls->sem);
- error = kobject_set_name(&cls->subsys.kobj, "%s", cls->name);
- if (error)
+ cp = kzalloc(sizeof(*cp), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&cp->class_devices);
+ INIT_LIST_HEAD(&cp->class_interfaces);
+ kset_init(&cp->class_dirs);
+ __mutex_init(&cp->class_mutex, "struct class mutex", key);
+ error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);
+ if (error) {
+ kfree(cp);
return error;
+ }
+
+ /* set the default /sys/dev directory for devices of this class */
+ if (!cls->dev_kobj)
+ cls->dev_kobj = sysfs_dev_char_kobj;
#if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
/* let the block class directory show up in the root of sysfs */
if (cls != &block_class)
- cls->subsys.kobj.kset = class_kset;
+ cp->class_subsys.kobj.kset = class_kset;
#else
- cls->subsys.kobj.kset = class_kset;
+ cp->class_subsys.kobj.kset = class_kset;
#endif
- cls->subsys.kobj.ktype = &class_ktype;
+ cp->class_subsys.kobj.ktype = &class_ktype;
+ cp->class = cls;
+ cls->p = cp;
- error = kset_register(&cls->subsys);
- if (!error) {
- error = add_class_attrs(class_get(cls));
- class_put(cls);
+ error = kset_register(&cp->class_subsys);
+ if (error) {
+ kfree(cp);
+ return error;
}
+ error = add_class_attrs(class_get(cls));
+ class_put(cls);
return error;
}
+EXPORT_SYMBOL_GPL(__class_register);
void class_unregister(struct class *cls)
{
pr_debug("device class '%s': unregistering\n", cls->name);
remove_class_attrs(cls);
- kset_unregister(&cls->subsys);
+ kset_unregister(&cls->p->class_subsys);
}
static void class_create_release(struct class *cls)
@@ -189,7 +205,8 @@ static void class_create_release(struct class *cls)
* Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy().
*/
-struct class *class_create(struct module *owner, const char *name)
+struct class *__class_create(struct module *owner, const char *name,
+ struct lock_class_key *key)
{
struct class *cls;
int retval;
@@ -204,7 +221,7 @@ struct class *class_create(struct module *owner, const char *name)
cls->owner = owner;
cls->class_release = class_create_release;
- retval = class_register(cls);
+ retval = __class_register(cls, key);
if (retval)
goto error;
@@ -214,6 +231,7 @@ error:
kfree(cls);
return ERR_PTR(retval);
}
+EXPORT_SYMBOL_GPL(__class_create);
/**
* class_destroy - destroys a struct class structure
@@ -252,39 +270,44 @@ char *make_class_name(const char *name, struct kobject *kobj)
/**
* class_for_each_device - device iterator
* @class: the class we're iterating
+ * @start: the device to start with in the list, if any.
* @data: data for the callback
* @fn: function to be called for each device
*
* Iterate over @class's list of devices, and call @fn for each,
- * passing it @data.
+ * passing it @data. If @start is set, the list iteration will start
+ * there, otherwise if it is NULL, the iteration starts at the
+ * beginning of the list.
*
* We check the return of @fn each time. If it returns anything
* other than 0, we break out and return that value.
*
- * Note, we hold class->sem in this function, so it can not be
+ * Note, we hold class->class_mutex in this function, so it can not be
* re-acquired in @fn, otherwise it will self-deadlocking. For
* example, calls to add or remove class members would be verboten.
*/
-int class_for_each_device(struct class *class, void *data,
- int (*fn)(struct device *, void *))
+int class_for_each_device(struct class *class, struct device *start,
+ void *data, int (*fn)(struct device *, void *))
{
struct device *dev;
int error = 0;
if (!class)
return -EINVAL;
- down(&class->sem);
- list_for_each_entry(dev, &class->devices, node) {
+ mutex_lock(&class->p->class_mutex);
+ list_for_each_entry(dev, &class->p->class_devices, node) {
+ if (start) {
+ if (start == dev)
+ start = NULL;
+ continue;
+ }
dev = get_device(dev);
- if (dev) {
- error = fn(dev, data);
- put_device(dev);
- } else
- error = -ENODEV;
+ error = fn(dev, data);
+ put_device(dev);
if (error)
break;
}
- up(&class->sem);
+ mutex_unlock(&class->p->class_mutex);
return error;
}
@@ -293,6 +316,7 @@ EXPORT_SYMBOL_GPL(class_for_each_device);
/**
* class_find_device - device iterator for locating a particular device
* @class: the class we're iterating
+ * @start: Device to begin with
* @data: data for the match function
* @match: function to check device
*
@@ -306,12 +330,13 @@ EXPORT_SYMBOL_GPL(class_for_each_device);
*
* Note, you will need to drop the reference with put_device() after use.
*
- * We hold class->sem in this function, so it can not be
+ * We hold class->class_mutex in this function, so it can not be
* re-acquired in @match, otherwise it will self-deadlocking. For
* example, calls to add or remove class members would be verboten.
*/
-struct device *class_find_device(struct class *class, void *data,
- int (*match)(struct device *, void *))
+struct device *class_find_device(struct class *class, struct device *start,
+ void *data,
+ int (*match)(struct device *, void *))
{
struct device *dev;
int found = 0;
@@ -319,19 +344,21 @@ struct device *class_find_device(struct class *class, void *data,
if (!class)
return NULL;
- down(&class->sem);
- list_for_each_entry(dev, &class->devices, node) {
+ mutex_lock(&class->p->class_mutex);
+ list_for_each_entry(dev, &class->p->class_devices, node) {
+ if (start) {
+ if (start == dev)
+ start = NULL;
+ continue;
+ }
dev = get_device(dev);
- if (dev) {
- if (match(dev, data)) {
- found = 1;
- break;
- } else
- put_device(dev);
- } else
+ if (match(dev, data)) {
+ found = 1;
break;
+ } else
+ put_device(dev);
}
- up(&class->sem);
+ mutex_unlock(&class->p->class_mutex);
return found ? dev : NULL;
}
@@ -349,13 +376,13 @@ int class_interface_register(struct class_interface *class_intf)
if (!parent)
return -EINVAL;
- down(&parent->sem);
- list_add_tail(&class_intf->node, &parent->interfaces);
+ mutex_lock(&parent->p->class_mutex);
+ list_add_tail(&class_intf->node, &parent->p->class_interfaces);
if (class_intf->add_dev) {
- list_for_each_entry(dev, &parent->devices, node)
+ list_for_each_entry(dev, &parent->p->class_devices, node)
class_intf->add_dev(dev, class_intf);
}
- up(&parent->sem);
+ mutex_unlock(&parent->p->class_mutex);
return 0;
}
@@ -368,13 +395,13 @@ void class_interface_unregister(struct class_interface *class_intf)
if (!parent)
return;
- down(&parent->sem);
+ mutex_lock(&parent->p->class_mutex);
list_del_init(&class_intf->node);
if (class_intf->remove_dev) {
- list_for_each_entry(dev, &parent->devices, node)
+ list_for_each_entry(dev, &parent->p->class_devices, node)
class_intf->remove_dev(dev, class_intf);
}
- up(&parent->sem);
+ mutex_unlock(&parent->p->class_mutex);
class_put(parent);
}
@@ -389,9 +416,7 @@ int __init classes_init(void)
EXPORT_SYMBOL_GPL(class_create_file);
EXPORT_SYMBOL_GPL(class_remove_file);
-EXPORT_SYMBOL_GPL(class_register);
EXPORT_SYMBOL_GPL(class_unregister);
-EXPORT_SYMBOL_GPL(class_create);
EXPORT_SYMBOL_GPL(class_destroy);
EXPORT_SYMBOL_GPL(class_interface_register);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index ee0a51a3a41d..7d5c63c81a59 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -21,12 +21,16 @@
#include <linux/genhd.h>
#include <linux/kallsyms.h>
#include <linux/semaphore.h>
+#include <linux/mutex.h>
#include "base.h"
#include "power/power.h"
int (*platform_notify)(struct device *dev) = NULL;
int (*platform_notify_remove)(struct device *dev) = NULL;
+static struct kobject *dev_kobj;
+struct kobject *sysfs_dev_char_kobj;
+struct kobject *sysfs_dev_block_kobj;
#ifdef CONFIG_BLOCK
static inline int device_is_not_partition(struct device *dev)
@@ -548,7 +552,7 @@ static struct kobject *get_device_parent(struct device *dev,
{
/* class devices without a parent live in /sys/class/<classname>/ */
if (dev->class && (!parent || parent->class != dev->class))
- return &dev->class->subsys.kobj;
+ return &dev->class->p->class_subsys.kobj;
/* all other devices keep their parent */
else if (parent)
return &parent->kobj;
@@ -594,13 +598,13 @@ static struct kobject *get_device_parent(struct device *dev,
parent_kobj = &parent->kobj;
/* find our class-directory at the parent and reference it */
- spin_lock(&dev->class->class_dirs.list_lock);
- list_for_each_entry(k, &dev->class->class_dirs.list, entry)
+ spin_lock(&dev->class->p->class_dirs.list_lock);
+ list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
if (k->parent == parent_kobj) {
kobj = kobject_get(k);
break;
}
- spin_unlock(&dev->class->class_dirs.list_lock);
+ spin_unlock(&dev->class->p->class_dirs.list_lock);
if (kobj)
return kobj;
@@ -608,7 +612,7 @@ static struct kobject *get_device_parent(struct device *dev,
k = kobject_create();
if (!k)
return NULL;
- k->kset = &dev->class->class_dirs;
+ k->kset = &dev->class->p->class_dirs;
retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
if (retval < 0) {
kobject_put(k);
@@ -627,7 +631,7 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
{
/* see if we live in a "glue" directory */
if (!glue_dir || !dev->class ||
- glue_dir->kset != &dev->class->class_dirs)
+ glue_dir->kset != &dev->class->p->class_dirs)
return;
kobject_put(glue_dir);
@@ -654,17 +658,18 @@ static int device_add_class_symlinks(struct device *dev)
if (!dev->class)
return 0;
- error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj,
+ error = sysfs_create_link(&dev->kobj,
+ &dev->class->p->class_subsys.kobj,
"subsystem");
if (error)
goto out;
#ifdef CONFIG_SYSFS_DEPRECATED
/* stacked class devices need a symlink in the class directory */
- if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
device_is_not_partition(dev)) {
- error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
- dev->bus_id);
+ error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
+ &dev->kobj, dev->bus_id);
if (error)
goto out_subsys;
}
@@ -701,13 +706,14 @@ out_device:
if (dev->parent && device_is_not_partition(dev))
sysfs_remove_link(&dev->kobj, "device");
out_busid:
- if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
device_is_not_partition(dev))
- sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+ dev->bus_id);
#else
/* link in the class directory pointing to the device */
- error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
- dev->bus_id);
+ error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
+ &dev->kobj, dev->bus_id);
if (error)
goto out_subsys;
@@ -720,7 +726,7 @@ out_busid:
return 0;
out_busid:
- sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id);
#endif
out_subsys:
@@ -746,14 +752,15 @@ static void device_remove_class_symlinks(struct device *dev)
sysfs_remove_link(&dev->kobj, "device");
}
- if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
device_is_not_partition(dev))
- sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+ dev->bus_id);
#else
if (dev->parent && device_is_not_partition(dev))
sysfs_remove_link(&dev->kobj, "device");
- sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id);
#endif
sysfs_remove_link(&dev->kobj, "subsystem");
@@ -776,6 +783,54 @@ int dev_set_name(struct device *dev, const char *fmt, ...)
EXPORT_SYMBOL_GPL(dev_set_name);
/**
+ * device_to_dev_kobj - select a /sys/dev/ directory for the device
+ * @dev: device
+ *
+ * By default we select char/ for new entries. Setting class->dev_obj
+ * to NULL prevents an entry from being created. class->dev_kobj must
+ * be set (or cleared) before any devices are registered to the class
+ * otherwise device_create_sys_dev_entry() and
+ * device_remove_sys_dev_entry() will disagree about the the presence
+ * of the link.
+ */
+static struct kobject *device_to_dev_kobj(struct device *dev)
+{
+ struct kobject *kobj;
+
+ if (dev->class)
+ kobj = dev->class->dev_kobj;
+ else
+ kobj = sysfs_dev_char_kobj;
+
+ return kobj;
+}
+
+static int device_create_sys_dev_entry(struct device *dev)
+{
+ struct kobject *kobj = device_to_dev_kobj(dev);
+ int error = 0;
+ char devt_str[15];
+
+ if (kobj) {
+ format_dev_t(devt_str, dev->devt);
+ error = sysfs_create_link(kobj, &dev->kobj, devt_str);
+ }
+
+ return error;
+}
+
+static void device_remove_sys_dev_entry(struct device *dev)
+{
+ struct kobject *kobj = device_to_dev_kobj(dev);
+ char devt_str[15];
+
+ if (kobj) {
+ format_dev_t(devt_str, dev->devt);
+ sysfs_remove_link(kobj, devt_str);
+ }
+}
+
+/**
* device_add - add device to device hierarchy.
* @dev: device.
*
@@ -829,6 +884,10 @@ int device_add(struct device *dev)
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
+
+ error = device_create_sys_dev_entry(dev);
+ if (error)
+ goto devtattrError;
}
error = device_add_class_symlinks(dev);
@@ -849,15 +908,16 @@ int device_add(struct device *dev)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) {
- down(&dev->class->sem);
+ mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
- list_add_tail(&dev->node, &dev->class->devices);
+ list_add_tail(&dev->node, &dev->class->p->class_devices);
/* notify any interfaces that the device is here */
- list_for_each_entry(class_intf, &dev->class->interfaces, node)
+ list_for_each_entry(class_intf,
+ &dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
- up(&dev->class->sem);
+ mutex_unlock(&dev->class->p->class_mutex);
}
Done:
put_device(dev);
@@ -873,6 +933,9 @@ int device_add(struct device *dev)
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
+ device_remove_sys_dev_entry(dev);
+ devtattrError:
+ if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
@@ -948,19 +1011,22 @@ void device_del(struct device *dev)
device_pm_remove(dev);
if (parent)
klist_del(&dev->knode_parent);
- if (MAJOR(dev->devt))
+ if (MAJOR(dev->devt)) {
+ device_remove_sys_dev_entry(dev);
device_remove_file(dev, &devt_attr);
+ }
if (dev->class) {
device_remove_class_symlinks(dev);
- down(&dev->class->sem);
+ mutex_lock(&dev->class->p->class_mutex);
/* notify any interfaces that the device is now gone */
- list_for_each_entry(class_intf, &dev->class->interfaces, node)
+ list_for_each_entry(class_intf,
+ &dev->class->p->class_interfaces, node)
if (class_intf->remove_dev)
class_intf->remove_dev(dev, class_intf);
/* remove the device from the class list */
list_del_init(&dev->node);
- up(&dev->class->sem);
+ mutex_unlock(&dev->class->p->class_mutex);
}
device_remove_file(dev, &uevent_attr);
device_remove_attrs(dev);
@@ -1074,7 +1140,25 @@ int __init devices_init(void)
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
+ dev_kobj = kobject_create_and_add("dev", NULL);
+ if (!dev_kobj)
+ goto dev_kobj_err;
+ sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
+ if (!sysfs_dev_block_kobj)
+ goto block_kobj_err;
+ sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
+ if (!sysfs_dev_char_kobj)
+ goto char_kobj_err;
+
return 0;
+
+ char_kobj_err:
+ kobject_put(sysfs_dev_block_kobj);
+ block_kobj_err:
+ kobject_put(dev_kobj);
+ dev_kobj_err:
+ kset_unregister(devices_kset);
+ return -ENOMEM;
}
EXPORT_SYMBOL_GPL(device_for_each_child);
@@ -1158,48 +1242,11 @@ error:
EXPORT_SYMBOL_GPL(device_create_vargs);
/**
- * device_create_drvdata - creates a device and registers it with sysfs
- * @class: pointer to the struct class that this device should be registered to
- * @parent: pointer to the parent struct device of this new device, if any
- * @devt: the dev_t for the char device to be added
- * @drvdata: the data to be added to the device for callbacks
- * @fmt: string for the device's name
- *
- * This function can be used by char device classes. A struct device
- * will be created in sysfs, registered to the specified class.
- *
- * A "dev" file will be created, showing the dev_t for the device, if
- * the dev_t is not 0,0.
- * If a pointer to a parent struct device is passed in, the newly created
- * struct device will be a child of that device in sysfs.
- * The pointer to the struct device will be returned from the call.
- * Any further sysfs files that might be required can be created using this
- * pointer.
- *
- * Note: the struct class passed to this function must have previously
- * been created with a call to class_create().
- */
-struct device *device_create_drvdata(struct class *class,
- struct device *parent,
- dev_t devt,
- void *drvdata,
- const char *fmt, ...)
-{
- va_list vargs;
- struct device *dev;
-
- va_start(vargs, fmt);
- dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
- va_end(vargs);
- return dev;
-}
-EXPORT_SYMBOL_GPL(device_create_drvdata);
-
-/**
* device_create - creates a device and registers it with sysfs
* @class: pointer to the struct class that this device should be registered to
* @parent: pointer to the parent struct device of this new device, if any
* @devt: the dev_t for the char device to be added
+ * @drvdata: the data to be added to the device for callbacks
* @fmt: string for the device's name
*
* This function can be used by char device classes. A struct device
@@ -1217,13 +1264,13 @@ EXPORT_SYMBOL_GPL(device_create_drvdata);
* been created with a call to class_create().
*/
struct device *device_create(struct class *class, struct device *parent,
- dev_t devt, const char *fmt, ...)
+ dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
- dev = device_create_vargs(class, parent, devt, NULL, fmt, vargs);
+ dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
@@ -1248,7 +1295,7 @@ void device_destroy(struct class *class, dev_t devt)
{
struct device *dev;
- dev = class_find_device(class, &devt, __match_devt);
+ dev = class_find_device(class, NULL, &devt, __match_devt);
if (dev) {
put_device(dev);
device_unregister(dev);
@@ -1298,8 +1345,9 @@ int device_rename(struct device *dev, char *new_name)
if (old_class_name) {
new_class_name = make_class_name(dev->class->name, &dev->kobj);
if (new_class_name) {
- error = sysfs_create_link(&dev->parent->kobj,
- &dev->kobj, new_class_name);
+ error = sysfs_create_link_nowarn(&dev->parent->kobj,
+ &dev->kobj,
+ new_class_name);
if (error)
goto out;
sysfs_remove_link(&dev->parent->kobj, old_class_name);
@@ -1307,11 +1355,12 @@ int device_rename(struct device *dev, char *new_name)
}
#else
if (dev->class) {
- error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
- dev->bus_id);
+ error = sysfs_create_link_nowarn(&dev->class->p->class_subsys.kobj,
+ &dev->kobj, dev->bus_id);
if (error)
goto out;
- sysfs_remove_link(&dev->class->subsys.kobj, old_device_name);
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+ old_device_name);
}
#endif
@@ -1447,4 +1496,7 @@ void device_shutdown(void)
dev->driver->shutdown(dev);
}
}
+ kobject_put(sysfs_dev_char_kobj);
+ kobject_put(sysfs_dev_block_kobj);
+ kobject_put(dev_kobj);
}
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 5000402ae092..64f5d54f7edc 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -21,15 +21,16 @@ EXPORT_SYMBOL(cpu_sysdev_class);
static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices);
#ifdef CONFIG_HOTPLUG_CPU
-static ssize_t show_online(struct sys_device *dev, char *buf)
+static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr,
+ char *buf)
{
struct cpu *cpu = container_of(dev, struct cpu, sysdev);
return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id));
}
-static ssize_t __ref store_online(struct sys_device *dev, const char *buf,
- size_t count)
+static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr,
+ const char *buf, size_t count)
{
struct cpu *cpu = container_of(dev, struct cpu, sysdev);
ssize_t ret;
@@ -80,7 +81,8 @@ static inline void register_cpu_control(struct cpu *cpu)
#ifdef CONFIG_KEXEC
#include <linux/kexec.h>
-static ssize_t show_crash_notes(struct sys_device *dev, char *buf)
+static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr,
+ char *buf)
{
struct cpu *cpu = container_of(dev, struct cpu, sysdev);
ssize_t rc;
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 937e8258981d..4d4e0e7b6e92 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -92,7 +92,8 @@ unregister_memory(struct memory_block *memory, struct mem_section *section)
* uses.
*/
-static ssize_t show_mem_phys_index(struct sys_device *dev, char *buf)
+static ssize_t show_mem_phys_index(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
{
struct memory_block *mem =
container_of(dev, struct memory_block, sysdev);
@@ -102,7 +103,8 @@ static ssize_t show_mem_phys_index(struct sys_device *dev, char *buf)
/*
* online, offline, going offline, etc.
*/
-static ssize_t show_mem_state(struct sys_device *dev, char *buf)
+static ssize_t show_mem_state(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
{
struct memory_block *mem =
container_of(dev, struct memory_block, sysdev);
@@ -217,7 +219,8 @@ out:
}
static ssize_t
-store_mem_state(struct sys_device *dev, const char *buf, size_t count)
+store_mem_state(struct sys_device *dev,
+ struct sysdev_attribute *attr, const char *buf, size_t count)
{
struct memory_block *mem;
unsigned int phys_section_nr;
@@ -248,7 +251,8 @@ out:
* s.t. if I offline all of these sections I can then
* remove the physical device?
*/
-static ssize_t show_phys_device(struct sys_device *dev, char *buf)
+static ssize_t show_phys_device(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
{
struct memory_block *mem =
container_of(dev, struct memory_block, sysdev);
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 0f867a083338..5116b78c6325 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -36,11 +36,13 @@ static ssize_t node_read_cpumap(struct sys_device *dev, int type, char *buf)
return len;
}
-static inline ssize_t node_read_cpumask(struct sys_device *dev, char *buf)
+static inline ssize_t node_read_cpumask(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
{
return node_read_cpumap(dev, 0, buf);
}
-static inline ssize_t node_read_cpulist(struct sys_device *dev, char *buf)
+static inline ssize_t node_read_cpulist(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
{
return node_read_cpumap(dev, 1, buf);
}
@@ -49,7 +51,8 @@ static SYSDEV_ATTR(cpumap, S_IRUGO, node_read_cpumask, NULL);
static SYSDEV_ATTR(cpulist, S_IRUGO, node_read_cpulist, NULL);
#define K(x) ((x) << (PAGE_SHIFT - 10))
-static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
+static ssize_t node_read_meminfo(struct sys_device * dev,
+ struct sysdev_attribute *attr, char * buf)
{
int n;
int nid = dev->id;
@@ -112,7 +115,8 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
#undef K
static SYSDEV_ATTR(meminfo, S_IRUGO, node_read_meminfo, NULL);
-static ssize_t node_read_numastat(struct sys_device * dev, char * buf)
+static ssize_t node_read_numastat(struct sys_device * dev,
+ struct sysdev_attribute *attr, char * buf)
{
return sprintf(buf,
"numa_hit %lu\n"
@@ -130,7 +134,8 @@ static ssize_t node_read_numastat(struct sys_device * dev, char * buf)
}
static SYSDEV_ATTR(numastat, S_IRUGO, node_read_numastat, NULL);
-static ssize_t node_read_distance(struct sys_device * dev, char * buf)
+static ssize_t node_read_distance(struct sys_device * dev,
+ struct sysdev_attribute *attr, char * buf)
{
int nid = dev->id;
int len = 0;
diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c
index 9b1b20b59e0a..2aa6e8fc4def 100644
--- a/drivers/base/power/trace.c
+++ b/drivers/base/power/trace.c
@@ -194,7 +194,7 @@ static int show_dev_hash(unsigned int value)
struct device * dev = to_device(entry);
unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH);
if (hash == value) {
- printk(" hash matches device %s\n", dev->bus_id);
+ dev_info(dev, "hash matches\n");
match++;
}
entry = entry->prev;
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
index 358bb0be3c08..40fc14f03540 100644
--- a/drivers/base/sys.c
+++ b/drivers/base/sys.c
@@ -36,7 +36,7 @@ sysdev_show(struct kobject * kobj, struct attribute * attr, char * buffer)
struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr);
if (sysdev_attr->show)
- return sysdev_attr->show(sysdev, buffer);
+ return sysdev_attr->show(sysdev, sysdev_attr, buffer);
return -EIO;
}
@@ -49,7 +49,7 @@ sysdev_store(struct kobject * kobj, struct attribute * attr,
struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr);
if (sysdev_attr->store)
- return sysdev_attr->store(sysdev, buffer, count);
+ return sysdev_attr->store(sysdev, sysdev_attr, buffer, count);
return -EIO;
}
@@ -130,8 +130,8 @@ static struct kset *system_kset;
int sysdev_class_register(struct sysdev_class * cls)
{
- pr_debug("Registering sysdev class '%s'\n",
- kobject_name(&cls->kset.kobj));
+ pr_debug("Registering sysdev class '%s'\n", cls->name);
+
INIT_LIST_HEAD(&cls->drivers);
memset(&cls->kset.kobj, 0x00, sizeof(struct kobject));
cls->kset.kobj.parent = &system_kset->kobj;
@@ -241,7 +241,8 @@ int sysdev_register(struct sys_device * sysdev)
if (!cls)
return -EINVAL;
- pr_debug("Registering sys device '%s'\n", kobject_name(&sysdev->kobj));
+ pr_debug("Registering sys device of class '%s'\n",
+ kobject_name(&cls->kset.kobj));
/* initialize the kobject to 0, in case it had previously been used */
memset(&sysdev->kobj, 0x00, sizeof(struct kobject));
@@ -257,6 +258,9 @@ int sysdev_register(struct sys_device * sysdev)
if (!error) {
struct sysdev_driver * drv;
+ pr_debug("Registering sys device '%s'\n",
+ kobject_name(&sysdev->kobj));
+
mutex_lock(&sysdev_drivers_lock);
/* Generic notification is implicit, because it's that
* code that should have called us.
@@ -269,6 +273,7 @@ int sysdev_register(struct sys_device * sysdev)
}
mutex_unlock(&sysdev_drivers_lock);
}
+
kobject_uevent(&sysdev->kobj, KOBJ_ADD);
return error;
}
@@ -474,3 +479,52 @@ int __init system_bus_init(void)
EXPORT_SYMBOL_GPL(sysdev_register);
EXPORT_SYMBOL_GPL(sysdev_unregister);
+
+#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr)
+
+ssize_t sysdev_store_ulong(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ char *end;
+ unsigned long new = simple_strtoul(buf, &end, 0);
+ if (end == buf)
+ return -EINVAL;
+ *(unsigned long *)(ea->var) = new;
+ return end - buf;
+}
+EXPORT_SYMBOL_GPL(sysdev_store_ulong);
+
+ssize_t sysdev_show_ulong(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ char *buf)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ return snprintf(buf, PAGE_SIZE, "%lx\n", *(unsigned long *)(ea->var));
+}
+EXPORT_SYMBOL_GPL(sysdev_show_ulong);
+
+ssize_t sysdev_store_int(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ char *end;
+ long new = simple_strtol(buf, &end, 0);
+ if (end == buf || new > INT_MAX || new < INT_MIN)
+ return -EINVAL;
+ *(int *)(ea->var) = new;
+ return end - buf;
+}
+EXPORT_SYMBOL_GPL(sysdev_store_int);
+
+ssize_t sysdev_show_int(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ char *buf)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ return snprintf(buf, PAGE_SIZE, "%d\n", *(int *)(ea->var));
+}
+EXPORT_SYMBOL_GPL(sysdev_show_int);
+
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index 3f6d9b0a6abe..199cd97e32e6 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -34,7 +34,8 @@
static SYSDEV_ATTR(_name, 0444, show_##_name, NULL)
#define define_id_show_func(name) \
-static ssize_t show_##name(struct sys_device *dev, char *buf) \
+static ssize_t show_##name(struct sys_device *dev, \
+ struct sysdev_attribute *attr, char *buf) \
{ \
unsigned int cpu = dev->id; \
return sprintf(buf, "%d\n", topology_##name(cpu)); \
@@ -59,14 +60,17 @@ static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf)
#ifdef arch_provides_topology_pointers
#define define_siblings_show_map(name) \
-static ssize_t show_##name(struct sys_device *dev, char *buf) \
+static ssize_t show_##name(struct sys_device *dev, \
+ struct sysdev_attribute *attr, char *buf) \
{ \
unsigned int cpu = dev->id; \
return show_cpumap(0, &(topology_##name(cpu)), buf); \
}
#define define_siblings_show_list(name) \
-static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \
+static ssize_t show_##name##_list(struct sys_device *dev, \
+ struct sysdev_attribute *attr, \
+ char *buf) \
{ \
unsigned int cpu = dev->id; \
return show_cpumap(1, &(topology_##name(cpu)), buf); \
@@ -74,7 +78,8 @@ static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \
#else
#define define_siblings_show_map(name) \
-static ssize_t show_##name(struct sys_device *dev, char *buf) \
+static ssize_t show_##name(struct sys_device *dev, \
+ struct sysdev_attribute *attr, char *buf) \
{ \
unsigned int cpu = dev->id; \
cpumask_t mask = topology_##name(cpu); \
@@ -82,7 +87,9 @@ static ssize_t show_##name(struct sys_device *dev, char *buf) \
}
#define define_siblings_show_list(name) \
-static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \
+static ssize_t show_##name##_list(struct sys_device *dev, \
+ struct sysdev_attribute *attr, \
+ char *buf) \
{ \
unsigned int cpu = dev->id; \
cpumask_t mask = topology_##name(cpu); \
OpenPOWER on IntegriCloud