summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/bus.c12
-rw-r--r--drivers/base/class.c41
-rw-r--r--drivers/base/core.c2
-rw-r--r--drivers/base/dd.c2
-rw-r--r--drivers/base/node.c24
-rw-r--r--drivers/base/power/resume.c8
-rw-r--r--drivers/base/power/runtime.c8
-rw-r--r--drivers/base/power/suspend.c12
-rw-r--r--drivers/base/power/sysfs.c8
-rw-r--r--drivers/base/sys.c111
10 files changed, 170 insertions, 58 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 96fe2f956754..17e96698410e 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -156,7 +156,9 @@ static ssize_t driver_unbind(struct device_driver *drv,
device_release_driver(dev);
err = count;
}
- return err;
+ if (err)
+ return err;
+ return count;
}
static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind);
@@ -180,7 +182,9 @@ static ssize_t driver_bind(struct device_driver *drv,
up(&dev->sem);
put_device(dev);
}
- return err;
+ if (err)
+ return err;
+ return count;
}
static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);
@@ -356,7 +360,7 @@ int bus_add_device(struct device * dev)
if (bus) {
pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
device_attach(dev);
- klist_add_tail(&bus->klist_devices, &dev->knode_bus);
+ klist_add_tail(&dev->knode_bus, &bus->klist_devices);
error = device_add_attrs(bus, dev);
if (!error) {
sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id);
@@ -444,7 +448,7 @@ int bus_add_driver(struct device_driver * drv)
}
driver_attach(drv);
- klist_add_tail(&bus->klist_drivers, &drv->knode_bus);
+ klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
module_add_driver(drv->owner, drv);
driver_add_attrs(bus, drv);
diff --git a/drivers/base/class.c b/drivers/base/class.c
index 479c12570881..d164c32a97ad 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -299,6 +299,9 @@ static void class_dev_release(struct kobject * kobj)
pr_debug("device class '%s': release.\n", cd->class_id);
+ kfree(cd->devt_attr);
+ cd->devt_attr = NULL;
+
if (cls->release)
cls->release(cd);
else {
@@ -447,10 +450,29 @@ void class_device_initialize(struct class_device *class_dev)
INIT_LIST_HEAD(&class_dev->node);
}
+static char *make_class_name(struct class_device *class_dev)
+{
+ char *name;
+ int size;
+
+ size = strlen(class_dev->class->name) +
+ strlen(kobject_name(&class_dev->kobj)) + 2;
+
+ name = kmalloc(size, GFP_KERNEL);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ strcpy(name, class_dev->class->name);
+ strcat(name, ":");
+ strcat(name, kobject_name(&class_dev->kobj));
+ return name;
+}
+
int class_device_add(struct class_device *class_dev)
{
struct class * parent = NULL;
struct class_interface * class_intf;
+ char *class_name = NULL;
int error;
class_dev = class_device_get(class_dev);
@@ -495,9 +517,13 @@ int class_device_add(struct class_device *class_dev)
}
class_device_add_attrs(class_dev);
- if (class_dev->dev)
+ if (class_dev->dev) {
+ class_name = make_class_name(class_dev);
sysfs_create_link(&class_dev->kobj,
&class_dev->dev->kobj, "device");
+ sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
+ class_name);
+ }
/* notify any interfaces this device is now here */
if (parent) {
@@ -514,6 +540,7 @@ int class_device_add(struct class_device *class_dev)
if (error && parent)
class_put(parent);
class_device_put(class_dev);
+ kfree(class_name);
return error;
}
@@ -579,6 +606,7 @@ void class_device_del(struct class_device *class_dev)
{
struct class * parent = class_dev->class;
struct class_interface * class_intf;
+ char *class_name = NULL;
if (parent) {
down(&parent->sem);
@@ -589,13 +617,13 @@ void class_device_del(struct class_device *class_dev)
up(&parent->sem);
}
- if (class_dev->dev)
+ if (class_dev->dev) {
+ class_name = make_class_name(class_dev);
sysfs_remove_link(&class_dev->kobj, "device");
- if (class_dev->devt_attr) {
- class_device_remove_file(class_dev, class_dev->devt_attr);
- kfree(class_dev->devt_attr);
- class_dev->devt_attr = NULL;
+ sysfs_remove_link(&class_dev->dev->kobj, class_name);
}
+ if (class_dev->devt_attr)
+ class_device_remove_file(class_dev, class_dev->devt_attr);
class_device_remove_attrs(class_dev);
kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE);
@@ -603,6 +631,7 @@ void class_device_del(struct class_device *class_dev)
if (parent)
class_put(parent);
+ kfree(class_name);
}
void class_device_unregister(struct class_device *class_dev)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index efe03a024a5b..c8a33df00761 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -249,7 +249,7 @@ int device_add(struct device *dev)
if ((error = bus_add_device(dev)))
goto BusError;
if (parent)
- klist_add_tail(&parent->klist_children, &dev->knode_parent);
+ klist_add_tail(&dev->knode_parent, &parent->klist_children);
/* notify platform of device entry */
if (platform_notify)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 16323f9cbff0..d5bbce38282f 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -42,7 +42,7 @@ void device_bind_driver(struct device * dev)
{
pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id, dev->driver->name);
- klist_add_tail(&dev->driver->klist_devices, &dev->knode_driver);
+ klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
sysfs_create_link(&dev->driver->kobj, &dev->kobj,
kobject_name(&dev->kobj));
sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver");
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 904b27caf697..16c513aa4d48 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -39,13 +39,25 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
int n;
int nid = dev->id;
struct sysinfo i;
+ struct page_state ps;
unsigned long inactive;
unsigned long active;
unsigned long free;
si_meminfo_node(&i, nid);
+ get_page_state_node(&ps, nid);
__get_zone_counts(&active, &inactive, &free, NODE_DATA(nid));
+ /* Check for negative values in these approximate counters */
+ if ((long)ps.nr_dirty < 0)
+ ps.nr_dirty = 0;
+ if ((long)ps.nr_writeback < 0)
+ ps.nr_writeback = 0;
+ if ((long)ps.nr_mapped < 0)
+ ps.nr_mapped = 0;
+ if ((long)ps.nr_slab < 0)
+ ps.nr_slab = 0;
+
n = sprintf(buf, "\n"
"Node %d MemTotal: %8lu kB\n"
"Node %d MemFree: %8lu kB\n"
@@ -55,7 +67,11 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
"Node %d HighTotal: %8lu kB\n"
"Node %d HighFree: %8lu kB\n"
"Node %d LowTotal: %8lu kB\n"
- "Node %d LowFree: %8lu kB\n",
+ "Node %d LowFree: %8lu kB\n"
+ "Node %d Dirty: %8lu kB\n"
+ "Node %d Writeback: %8lu kB\n"
+ "Node %d Mapped: %8lu kB\n"
+ "Node %d Slab: %8lu kB\n",
nid, K(i.totalram),
nid, K(i.freeram),
nid, K(i.totalram - i.freeram),
@@ -64,7 +80,11 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
nid, K(i.totalhigh),
nid, K(i.freehigh),
nid, K(i.totalram - i.totalhigh),
- nid, K(i.freeram - i.freehigh));
+ nid, K(i.freeram - i.freehigh),
+ nid, K(ps.nr_dirty),
+ nid, K(ps.nr_writeback),
+ nid, K(ps.nr_mapped),
+ nid, K(ps.nr_slab));
n += hugetlb_report_node_meminfo(nid, buf + n);
return n;
}
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index bdd96b03b885..0a7aa07b9a2a 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -26,11 +26,11 @@ int resume_device(struct device * dev)
down(&dev->sem);
if (dev->power.pm_parent
- && dev->power.pm_parent->power.power_state) {
+ && dev->power.pm_parent->power.power_state.event) {
dev_err(dev, "PM: resume from %d, parent %s still %d\n",
- dev->power.power_state,
+ dev->power.power_state.event,
dev->power.pm_parent->bus_id,
- dev->power.pm_parent->power.power_state);
+ dev->power.pm_parent->power.power_state.event);
}
if (dev->bus && dev->bus->resume) {
dev_dbg(dev,"resuming\n");
@@ -54,7 +54,7 @@ void dpm_resume(void)
list_add_tail(entry, &dpm_active);
up(&dpm_list_sem);
- if (!dev->power.prev_state)
+ if (!dev->power.prev_state.event)
resume_device(dev);
down(&dpm_list_sem);
put_device(dev);
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 325962d80191..e8f0519f5dfa 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -13,10 +13,10 @@
static void runtime_resume(struct device * dev)
{
dev_dbg(dev, "resuming\n");
- if (!dev->power.power_state)
+ if (!dev->power.power_state.event)
return;
if (!resume_device(dev))
- dev->power.power_state = 0;
+ dev->power.power_state = PMSG_ON;
}
@@ -49,10 +49,10 @@ int dpm_runtime_suspend(struct device * dev, pm_message_t state)
int error = 0;
down(&dpm_sem);
- if (dev->power.power_state == state)
+ if (dev->power.power_state.event == state.event)
goto Done;
- if (dev->power.power_state)
+ if (dev->power.power_state.event)
runtime_resume(dev);
if (!(error = suspend_device(dev, state)))
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
index 2ccee3763acf..50501764d050 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -40,22 +40,22 @@ int suspend_device(struct device * dev, pm_message_t state)
int error = 0;
down(&dev->sem);
- if (dev->power.power_state) {
+ if (dev->power.power_state.event) {
dev_dbg(dev, "PM: suspend %d-->%d\n",
- dev->power.power_state, state);
+ dev->power.power_state.event, state.event);
}
if (dev->power.pm_parent
- && dev->power.pm_parent->power.power_state) {
+ && dev->power.pm_parent->power.power_state.event) {
dev_err(dev,
"PM: suspend %d->%d, parent %s already %d\n",
- dev->power.power_state, state,
+ dev->power.power_state.event, state.event,
dev->power.pm_parent->bus_id,
- dev->power.pm_parent->power.power_state);
+ dev->power.pm_parent->power.power_state.event);
}
dev->power.prev_state = dev->power.power_state;
- if (dev->bus && dev->bus->suspend && !dev->power.power_state) {
+ if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
dev_dbg(dev, "suspending\n");
error = dev->bus->suspend(dev, state);
}
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index f82b3df9545f..8d04fb435c17 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -26,19 +26,19 @@
static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf)
{
- return sprintf(buf, "%u\n", dev->power.power_state);
+ return sprintf(buf, "%u\n", dev->power.power_state.event);
}
static ssize_t state_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t n)
{
- u32 state;
+ pm_message_t state;
char * rest;
int error = 0;
- state = simple_strtoul(buf, &rest, 10);
+ state.event = simple_strtoul(buf, &rest, 10);
if (*rest)
return -EINVAL;
- if (state)
+ if (state.event)
error = dpm_runtime_suspend(dev, state);
else
dpm_runtime_resume(dev);
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
index f37a13de804a..3431eb6004c3 100644
--- a/drivers/base/sys.c
+++ b/drivers/base/sys.c
@@ -22,7 +22,6 @@
#include <linux/string.h>
#include <linux/pm.h>
-
extern struct subsystem devices_subsys;
#define to_sysdev(k) container_of(k, struct sys_device, kobj)
@@ -289,6 +288,27 @@ void sysdev_shutdown(void)
up(&sysdev_drivers_lock);
}
+static void __sysdev_resume(struct sys_device *dev)
+{
+ struct sysdev_class *cls = dev->cls;
+ struct sysdev_driver *drv;
+
+ /* First, call the class-specific one */
+ if (cls->resume)
+ cls->resume(dev);
+
+ /* Call auxillary drivers next. */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->resume)
+ drv->resume(dev);
+ }
+
+ /* Call global drivers. */
+ list_for_each_entry(drv, &sysdev_drivers, entry) {
+ if (drv->resume)
+ drv->resume(dev);
+ }
+}
/**
* sysdev_suspend - Suspend all system devices.
@@ -306,38 +326,93 @@ void sysdev_shutdown(void)
int sysdev_suspend(pm_message_t state)
{
struct sysdev_class * cls;
+ struct sys_device *sysdev, *err_dev;
+ struct sysdev_driver *drv, *err_drv;
+ int ret;
pr_debug("Suspending System Devices\n");
list_for_each_entry_reverse(cls, &system_subsys.kset.list,
kset.kobj.entry) {
- struct sys_device * sysdev;
pr_debug("Suspending type '%s':\n",
kobject_name(&cls->kset.kobj));
list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
- struct sysdev_driver * drv;
pr_debug(" %s\n", kobject_name(&sysdev->kobj));
/* Call global drivers first. */
list_for_each_entry(drv, &sysdev_drivers, entry) {
- if (drv->suspend)
- drv->suspend(sysdev, state);
+ if (drv->suspend) {
+ ret = drv->suspend(sysdev, state);
+ if (ret)
+ goto gbl_driver;
+ }
}
/* Call auxillary drivers next. */
list_for_each_entry(drv, &cls->drivers, entry) {
- if (drv->suspend)
- drv->suspend(sysdev, state);
+ if (drv->suspend) {
+ ret = drv->suspend(sysdev, state);
+ if (ret)
+ goto aux_driver;
+ }
}
/* Now call the generic one */
- if (cls->suspend)
- cls->suspend(sysdev, state);
+ if (cls->suspend) {
+ ret = cls->suspend(sysdev, state);
+ if (ret)
+ goto cls_driver;
+ }
}
}
return 0;
+ /* resume current sysdev */
+cls_driver:
+ drv = NULL;
+ printk(KERN_ERR "Class suspend failed for %s\n",
+ kobject_name(&sysdev->kobj));
+
+aux_driver:
+ if (drv)
+ printk(KERN_ERR "Class driver suspend failed for %s\n",
+ kobject_name(&sysdev->kobj));
+ list_for_each_entry(err_drv, &cls->drivers, entry) {
+ if (err_drv == drv)
+ break;
+ if (err_drv->resume)
+ err_drv->resume(sysdev);
+ }
+ drv = NULL;
+
+gbl_driver:
+ if (drv)
+ printk(KERN_ERR "sysdev driver suspend failed for %s\n",
+ kobject_name(&sysdev->kobj));
+ list_for_each_entry(err_drv, &sysdev_drivers, entry) {
+ if (err_drv == drv)
+ break;
+ if (err_drv->resume)
+ err_drv->resume(sysdev);
+ }
+ /* resume other sysdevs in current class */
+ list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
+ if (err_dev == sysdev)
+ break;
+ pr_debug(" %s\n", kobject_name(&err_dev->kobj));
+ __sysdev_resume(err_dev);
+ }
+
+ /* resume other classes */
+ list_for_each_entry_continue(cls, &system_subsys.kset.list,
+ kset.kobj.entry) {
+ list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
+ pr_debug(" %s\n", kobject_name(&err_dev->kobj));
+ __sysdev_resume(err_dev);
+ }
+ }
+ return ret;
}
@@ -363,25 +438,9 @@ int sysdev_resume(void)
kobject_name(&cls->kset.kobj));
list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
- struct sysdev_driver * drv;
pr_debug(" %s\n", kobject_name(&sysdev->kobj));
- /* First, call the class-specific one */
- if (cls->resume)
- cls->resume(sysdev);
-
- /* Call auxillary drivers next. */
- list_for_each_entry(drv, &cls->drivers, entry) {
- if (drv->resume)
- drv->resume(sysdev);
- }
-
- /* Call global drivers. */
- list_for_each_entry(drv, &sysdev_drivers, entry) {
- if (drv->resume)
- drv->resume(sysdev);
- }
-
+ __sysdev_resume(sysdev);
}
}
return 0;