diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/bus.c | 8 | ||||
-rw-r--r-- | drivers/base/class.c | 39 | ||||
-rw-r--r-- | drivers/base/core.c | 2 | ||||
-rw-r--r-- | drivers/base/dd.c | 2 | ||||
-rw-r--r-- | drivers/base/firmware_class.c | 79 | ||||
-rw-r--r-- | drivers/base/node.c | 24 | ||||
-rw-r--r-- | drivers/base/power/resume.c | 8 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 8 | ||||
-rw-r--r-- | drivers/base/power/suspend.c | 12 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 8 | ||||
-rw-r--r-- | drivers/base/sys.c | 110 |
11 files changed, 215 insertions, 85 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index ab53832d57e5..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); @@ -358,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); @@ -446,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 0154a1623b21..d164c32a97ad 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -299,10 +299,8 @@ static void class_dev_release(struct kobject * kobj) pr_debug("device class '%s': release.\n", cd->class_id); - if (cd->devt_attr) { - kfree(cd->devt_attr); - cd->devt_attr = NULL; - } + kfree(cd->devt_attr); + cd->devt_attr = NULL; if (cls->release) cls->release(cd); @@ -452,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); @@ -500,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) { @@ -519,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; } @@ -584,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); @@ -594,8 +617,11 @@ 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"); + 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); @@ -605,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/firmware_class.c b/drivers/base/firmware_class.c index 652281402c92..5bfa2e9a7c26 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -28,6 +28,7 @@ enum { FW_STATUS_DONE, FW_STATUS_ABORT, FW_STATUS_READY, + FW_STATUS_READY_NOHOTPLUG, }; static int loading_timeout = 10; /* In seconds */ @@ -344,7 +345,7 @@ error_kfree: static int fw_setup_class_device(struct firmware *fw, struct class_device **class_dev_p, - const char *fw_name, struct device *device) + const char *fw_name, struct device *device, int hotplug) { struct class_device *class_dev; struct firmware_priv *fw_priv; @@ -376,7 +377,10 @@ fw_setup_class_device(struct firmware *fw, struct class_device **class_dev_p, goto error_unreg; } - set_bit(FW_STATUS_READY, &fw_priv->status); + if (hotplug) + set_bit(FW_STATUS_READY, &fw_priv->status); + else + set_bit(FW_STATUS_READY_NOHOTPLUG, &fw_priv->status); *class_dev_p = class_dev; goto out; @@ -386,21 +390,9 @@ out: return retval; } -/** - * request_firmware: - request firmware to hotplug and wait for it - * Description: - * @firmware will be used to return a firmware image by the name - * of @name for device @device. - * - * Should be called from user context where sleeping is allowed. - * - * @name will be use as $FIRMWARE in the hotplug environment and - * should be distinctive enough not to be confused with any other - * firmware image for this or any other device. - **/ -int -request_firmware(const struct firmware **firmware_p, const char *name, - struct device *device) +static int +_request_firmware(const struct firmware **firmware_p, const char *name, + struct device *device, int hotplug) { struct class_device *class_dev; struct firmware_priv *fw_priv; @@ -419,22 +411,25 @@ request_firmware(const struct firmware **firmware_p, const char *name, } memset(firmware, 0, sizeof (*firmware)); - retval = fw_setup_class_device(firmware, &class_dev, name, device); + retval = fw_setup_class_device(firmware, &class_dev, name, device, + hotplug); if (retval) goto error_kfree_fw; fw_priv = class_get_devdata(class_dev); - if (loading_timeout > 0) { - fw_priv->timeout.expires = jiffies + loading_timeout * HZ; - add_timer(&fw_priv->timeout); - } - - kobject_hotplug(&class_dev->kobj, KOBJ_ADD); - wait_for_completion(&fw_priv->completion); - set_bit(FW_STATUS_DONE, &fw_priv->status); + if (hotplug) { + if (loading_timeout > 0) { + fw_priv->timeout.expires = jiffies + loading_timeout * HZ; + add_timer(&fw_priv->timeout); + } - del_timer_sync(&fw_priv->timeout); + kobject_hotplug(&class_dev->kobj, KOBJ_ADD); + wait_for_completion(&fw_priv->completion); + set_bit(FW_STATUS_DONE, &fw_priv->status); + del_timer_sync(&fw_priv->timeout); + } else + wait_for_completion(&fw_priv->completion); down(&fw_lock); if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) { @@ -455,6 +450,26 @@ out: } /** + * request_firmware: - request firmware to hotplug and wait for it + * Description: + * @firmware will be used to return a firmware image by the name + * of @name for device @device. + * + * Should be called from user context where sleeping is allowed. + * + * @name will be use as $FIRMWARE in the hotplug environment and + * should be distinctive enough not to be confused with any other + * firmware image for this or any other device. + **/ +int +request_firmware(const struct firmware **firmware_p, const char *name, + struct device *device) +{ + int hotplug = 1; + return _request_firmware(firmware_p, name, device, hotplug); +} + +/** * release_firmware: - release the resource associated with a firmware image **/ void @@ -491,6 +506,7 @@ struct firmware_work { struct device *device; void *context; void (*cont)(const struct firmware *fw, void *context); + int hotplug; }; static int @@ -503,7 +519,8 @@ request_firmware_work_func(void *arg) return 0; } daemonize("%s/%s", "firmware", fw_work->name); - request_firmware(&fw, fw_work->name, fw_work->device); + _request_firmware(&fw, fw_work->name, fw_work->device, + fw_work->hotplug); fw_work->cont(fw, fw_work->context); release_firmware(fw); module_put(fw_work->module); @@ -518,6 +535,9 @@ request_firmware_work_func(void *arg) * Asynchronous variant of request_firmware() for contexts where * it is not possible to sleep. * + * @hotplug invokes hotplug event to copy the firmware image if this flag + * is non-zero else the firmware copy must be done manually. + * * @cont will be called asynchronously when the firmware request is over. * * @context will be passed over to @cont. @@ -527,7 +547,7 @@ request_firmware_work_func(void *arg) **/ int request_firmware_nowait( - struct module *module, + struct module *module, int hotplug, const char *name, struct device *device, void *context, void (*cont)(const struct firmware *fw, void *context)) { @@ -548,6 +568,7 @@ request_firmware_nowait( .device = device, .context = context, .cont = cont, + .hotplug = hotplug, }; ret = kernel_thread(request_firmware_work_func, fw_work, 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 214b96435409..3431eb6004c3 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -288,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. @@ -305,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; } @@ -362,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; |