diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/bus.c | 2 | ||||
-rw-r--r-- | drivers/base/core.c | 16 | ||||
-rw-r--r-- | drivers/base/devtmpfs.c | 19 | ||||
-rw-r--r-- | drivers/base/driver.c | 4 | ||||
-rw-r--r-- | drivers/base/memory.c | 19 | ||||
-rw-r--r-- | drivers/base/platform.c | 1 | ||||
-rw-r--r-- | drivers/base/power/main.c | 128 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 45 |
8 files changed, 195 insertions, 39 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 63c143e54a5..c0c5a43d9fb 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -703,9 +703,9 @@ int bus_add_driver(struct device_driver *drv) return 0; out_unregister: + kobject_put(&priv->kobj); kfree(drv->p); drv->p = NULL; - kobject_put(&priv->kobj); out_put_bus: bus_put(bus); return error; diff --git a/drivers/base/core.c b/drivers/base/core.c index f1290cbd135..28202577042 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -446,7 +446,8 @@ struct kset *devices_kset; * @dev: device. * @attr: device attribute descriptor. */ -int device_create_file(struct device *dev, struct device_attribute *attr) +int device_create_file(struct device *dev, + const struct device_attribute *attr) { int error = 0; if (dev) @@ -459,7 +460,8 @@ int device_create_file(struct device *dev, struct device_attribute *attr) * @dev: device. * @attr: device attribute descriptor. */ -void device_remove_file(struct device *dev, struct device_attribute *attr) +void device_remove_file(struct device *dev, + const struct device_attribute *attr) { if (dev) sysfs_remove_file(&dev->kobj, &attr->attr); @@ -470,7 +472,8 @@ void device_remove_file(struct device *dev, struct device_attribute *attr) * @dev: device. * @attr: device binary attribute descriptor. */ -int device_create_bin_file(struct device *dev, struct bin_attribute *attr) +int device_create_bin_file(struct device *dev, + const struct bin_attribute *attr) { int error = -EINVAL; if (dev) @@ -484,7 +487,8 @@ EXPORT_SYMBOL_GPL(device_create_bin_file); * @dev: device. * @attr: device binary attribute descriptor. */ -void device_remove_bin_file(struct device *dev, struct bin_attribute *attr) +void device_remove_bin_file(struct device *dev, + const struct bin_attribute *attr) { if (dev) sysfs_remove_bin_file(&dev->kobj, attr); @@ -905,8 +909,10 @@ int device_add(struct device *dev) dev->init_name = NULL; } - if (!dev_name(dev)) + if (!dev_name(dev)) { + error = -EINVAL; goto name_error; + } pr_debug("device: '%s': %s\n", dev_name(dev), __func__); diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 50375bb8e51..090dd485130 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -32,7 +32,7 @@ static int dev_mount = 1; static int dev_mount; #endif -static rwlock_t dirlock; +static DEFINE_MUTEX(dirlock); static int __init mount_param(char *str) { @@ -93,7 +93,7 @@ static int create_path(const char *nodepath) { int err; - read_lock(&dirlock); + mutex_lock(&dirlock); err = dev_mkdir(nodepath, 0755); if (err == -ENOENT) { char *path; @@ -101,8 +101,10 @@ static int create_path(const char *nodepath) /* parent directories do not exist, create them */ path = kstrdup(nodepath, GFP_KERNEL); - if (!path) - return -ENOMEM; + if (!path) { + err = -ENOMEM; + goto out; + } s = path; for (;;) { s = strchr(s, '/'); @@ -117,7 +119,8 @@ static int create_path(const char *nodepath) } kfree(path); } - read_unlock(&dirlock); +out: + mutex_unlock(&dirlock); return err; } @@ -229,7 +232,7 @@ static int delete_path(const char *nodepath) if (!path) return -ENOMEM; - write_lock(&dirlock); + mutex_lock(&dirlock); for (;;) { char *base; @@ -241,7 +244,7 @@ static int delete_path(const char *nodepath) if (err) break; } - write_unlock(&dirlock); + mutex_unlock(&dirlock); kfree(path); return err; @@ -352,8 +355,6 @@ int __init devtmpfs_init(void) int err; struct vfsmount *mnt; - rwlock_init(&dirlock); - err = register_filesystem(&dev_fs_type); if (err) { printk(KERN_ERR "devtmpfs: unable to register devtmpfs " diff --git a/drivers/base/driver.c b/drivers/base/driver.c index f367885a764..90c9fff09ea 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -98,7 +98,7 @@ EXPORT_SYMBOL_GPL(driver_find_device); * @attr: driver attribute descriptor. */ int driver_create_file(struct device_driver *drv, - struct driver_attribute *attr) + const struct driver_attribute *attr) { int error; if (drv) @@ -115,7 +115,7 @@ EXPORT_SYMBOL_GPL(driver_create_file); * @attr: driver attribute descriptor. */ void driver_remove_file(struct device_driver *drv, - struct driver_attribute *attr) + const struct driver_attribute *attr) { if (drv) sysfs_remove_file(&drv->p->kobj, &attr->attr); diff --git a/drivers/base/memory.c b/drivers/base/memory.c index c4c8f2e1dd1..d7d77d4a402 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -63,6 +63,20 @@ void unregister_memory_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(unregister_memory_notifier); +static ATOMIC_NOTIFIER_HEAD(memory_isolate_chain); + +int register_memory_isolate_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&memory_isolate_chain, nb); +} +EXPORT_SYMBOL(register_memory_isolate_notifier); + +void unregister_memory_isolate_notifier(struct notifier_block *nb) +{ + atomic_notifier_chain_unregister(&memory_isolate_chain, nb); +} +EXPORT_SYMBOL(unregister_memory_isolate_notifier); + /* * register_memory - Setup a sysfs device for a memory block */ @@ -157,6 +171,11 @@ int memory_notify(unsigned long val, void *v) return blocking_notifier_call_chain(&memory_chain, val, v); } +int memory_isolate_notify(unsigned long val, void *v) +{ + return atomic_notifier_call_chain(&memory_isolate_chain, val, v); +} + /* * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is * OK to have direct references to sparsemem variables in here. diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 9d2ee25deaf..58efaf2f125 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -441,6 +441,7 @@ error: platform_device_put(pdev); return ERR_PTR(retval); } +EXPORT_SYMBOL_GPL(platform_device_register_data); static int platform_drv_probe(struct device *_dev) { diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 1a216c114a0..48adf80926a 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -161,6 +161,32 @@ void device_pm_move_last(struct device *dev) list_move_tail(&dev->power.entry, &dpm_list); } +static ktime_t initcall_debug_start(struct device *dev) +{ + ktime_t calltime = ktime_set(0, 0); + + if (initcall_debug) { + pr_info("calling %s+ @ %i\n", + dev_name(dev), task_pid_nr(current)); + calltime = ktime_get(); + } + + return calltime; +} + +static void initcall_debug_report(struct device *dev, ktime_t calltime, + int error) +{ + ktime_t delta, rettime; + + if (initcall_debug) { + rettime = ktime_get(); + delta = ktime_sub(rettime, calltime); + pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), + error, (unsigned long long)ktime_to_ns(delta) >> 10); + } +} + /** * pm_op - Execute the PM operation appropriate for given PM event. * @dev: Device to handle. @@ -172,13 +198,9 @@ static int pm_op(struct device *dev, pm_message_t state) { int error = 0; - ktime_t calltime, delta, rettime; + ktime_t calltime; - if (initcall_debug) { - pr_info("calling %s+ @ %i\n", - dev_name(dev), task_pid_nr(current)); - calltime = ktime_get(); - } + calltime = initcall_debug_start(dev); switch (state.event) { #ifdef CONFIG_SUSPEND @@ -227,12 +249,7 @@ static int pm_op(struct device *dev, error = -EINVAL; } - if (initcall_debug) { - rettime = ktime_get(); - delta = ktime_sub(rettime, calltime); - pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), - error, (unsigned long long)ktime_to_ns(delta) >> 10); - } + initcall_debug_report(dev, calltime, error); return error; } @@ -309,8 +326,9 @@ static int pm_noirq_op(struct device *dev, if (initcall_debug) { rettime = ktime_get(); delta = ktime_sub(rettime, calltime); - printk("initcall %s_i+ returned %d after %Ld usecs\n", dev_name(dev), - error, (unsigned long long)ktime_to_ns(delta) >> 10); + printk("initcall %s_i+ returned %d after %Ld usecs\n", + dev_name(dev), error, + (unsigned long long)ktime_to_ns(delta) >> 10); } return error; @@ -354,6 +372,23 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info, kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) +{ + ktime_t calltime; + s64 usecs64; + int usecs; + + calltime = ktime_get(); + usecs64 = ktime_to_ns(ktime_sub(calltime, starttime)); + do_div(usecs64, NSEC_PER_USEC); + usecs = usecs64; + if (usecs == 0) + usecs = 1; + pr_info("PM: %s%s%s of devices complete after %ld.%03ld msecs\n", + info ?: "", info ? " " : "", pm_verb(state.event), + usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); +} + /*------------------------- Resume routines -------------------------*/ /** @@ -390,6 +425,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) void dpm_resume_noirq(pm_message_t state) { struct device *dev; + ktime_t starttime = ktime_get(); mutex_lock(&dpm_list_mtx); transition_started = false; @@ -403,11 +439,32 @@ void dpm_resume_noirq(pm_message_t state) pm_dev_err(dev, state, " early", error); } mutex_unlock(&dpm_list_mtx); + dpm_show_time(starttime, state, "early"); resume_device_irqs(); } EXPORT_SYMBOL_GPL(dpm_resume_noirq); /** + * legacy_resume - Execute a legacy (bus or class) resume callback for device. + * dev: Device to resume. + * cb: Resume callback to execute. + */ +static int legacy_resume(struct device *dev, int (*cb)(struct device *dev)) +{ + int error; + ktime_t calltime; + + calltime = initcall_debug_start(dev); + + error = cb(dev); + suspend_report_result(cb, error); + + initcall_debug_report(dev, calltime, error); + + return error; +} + +/** * device_resume - Execute "resume" callbacks for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. @@ -427,7 +484,7 @@ static int device_resume(struct device *dev, pm_message_t state) error = pm_op(dev, dev->bus->pm, state); } else if (dev->bus->resume) { pm_dev_dbg(dev, state, "legacy "); - error = dev->bus->resume(dev); + error = legacy_resume(dev, dev->bus->resume); } if (error) goto End; @@ -448,7 +505,7 @@ static int device_resume(struct device *dev, pm_message_t state) error = pm_op(dev, dev->class->pm, state); } else if (dev->class->resume) { pm_dev_dbg(dev, state, "legacy class "); - error = dev->class->resume(dev); + error = legacy_resume(dev, dev->class->resume); } } End: @@ -468,6 +525,7 @@ static int device_resume(struct device *dev, pm_message_t state) static void dpm_resume(pm_message_t state) { struct list_head list; + ktime_t starttime = ktime_get(); INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); @@ -496,6 +554,7 @@ static void dpm_resume(pm_message_t state) } list_splice(&list, &dpm_list); mutex_unlock(&dpm_list_mtx); + dpm_show_time(starttime, state, NULL); } /** @@ -548,7 +607,7 @@ static void dpm_complete(pm_message_t state) mutex_unlock(&dpm_list_mtx); device_complete(dev, state); - pm_runtime_put_noidle(dev); + pm_runtime_put_sync(dev); mutex_lock(&dpm_list_mtx); } @@ -628,6 +687,7 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) int dpm_suspend_noirq(pm_message_t state) { struct device *dev; + ktime_t starttime = ktime_get(); int error = 0; suspend_device_irqs(); @@ -643,11 +703,34 @@ int dpm_suspend_noirq(pm_message_t state) mutex_unlock(&dpm_list_mtx); if (error) dpm_resume_noirq(resume_event(state)); + else + dpm_show_time(starttime, state, "late"); return error; } EXPORT_SYMBOL_GPL(dpm_suspend_noirq); /** + * legacy_suspend - Execute a legacy (bus or class) suspend callback for device. + * dev: Device to suspend. + * cb: Suspend callback to execute. + */ +static int legacy_suspend(struct device *dev, pm_message_t state, + int (*cb)(struct device *dev, pm_message_t state)) +{ + int error; + ktime_t calltime; + + calltime = initcall_debug_start(dev); + + error = cb(dev, state); + suspend_report_result(cb, error); + + initcall_debug_report(dev, calltime, error); + + return error; +} + +/** * device_suspend - Execute "suspend" callbacks for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. @@ -664,8 +747,7 @@ static int device_suspend(struct device *dev, pm_message_t state) error = pm_op(dev, dev->class->pm, state); } else if (dev->class->suspend) { pm_dev_dbg(dev, state, "legacy class "); - error = dev->class->suspend(dev, state); - suspend_report_result(dev->class->suspend, error); + error = legacy_suspend(dev, state, dev->class->suspend); } if (error) goto End; @@ -686,8 +768,7 @@ static int device_suspend(struct device *dev, pm_message_t state) error = pm_op(dev, dev->bus->pm, state); } else if (dev->bus->suspend) { pm_dev_dbg(dev, state, "legacy "); - error = dev->bus->suspend(dev, state); - suspend_report_result(dev->bus->suspend, error); + error = legacy_suspend(dev, state, dev->bus->suspend); } } End: @@ -703,6 +784,7 @@ static int device_suspend(struct device *dev, pm_message_t state) static int dpm_suspend(pm_message_t state) { struct list_head list; + ktime_t starttime = ktime_get(); int error = 0; INIT_LIST_HEAD(&list); @@ -728,6 +810,8 @@ static int dpm_suspend(pm_message_t state) } list_splice(&list, dpm_list.prev); mutex_unlock(&dpm_list_mtx); + if (!error) + dpm_show_time(starttime, state, NULL); return error; } @@ -796,7 +880,7 @@ static int dpm_prepare(pm_message_t state) pm_runtime_get_noresume(dev); if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) { /* Wake-up requested during system sleep transition. */ - pm_runtime_put_noidle(dev); + pm_runtime_put_sync(dev); error = -EBUSY; } else { error = device_prepare(dev, state); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 40d7720a4b2..f8b044e8aef 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -85,6 +85,19 @@ static int __pm_runtime_idle(struct device *dev) dev->bus->pm->runtime_idle(dev); spin_lock_irq(&dev->power.lock); + } else if (dev->type && dev->type->pm && dev->type->pm->runtime_idle) { + spin_unlock_irq(&dev->power.lock); + + dev->type->pm->runtime_idle(dev); + + spin_lock_irq(&dev->power.lock); + } else if (dev->class && dev->class->pm + && dev->class->pm->runtime_idle) { + spin_unlock_irq(&dev->power.lock); + + dev->class->pm->runtime_idle(dev); + + spin_lock_irq(&dev->power.lock); } dev->power.idle_notification = false; @@ -194,6 +207,22 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) spin_lock_irq(&dev->power.lock); dev->power.runtime_error = retval; + } else if (dev->type && dev->type->pm + && dev->type->pm->runtime_suspend) { + spin_unlock_irq(&dev->power.lock); + + retval = dev->type->pm->runtime_suspend(dev); + + spin_lock_irq(&dev->power.lock); + dev->power.runtime_error = retval; + } else if (dev->class && dev->class->pm + && dev->class->pm->runtime_suspend) { + spin_unlock_irq(&dev->power.lock); + + retval = dev->class->pm->runtime_suspend(dev); + + spin_lock_irq(&dev->power.lock); + dev->power.runtime_error = retval; } else { retval = -ENOSYS; } @@ -359,6 +388,22 @@ int __pm_runtime_resume(struct device *dev, bool from_wq) spin_lock_irq(&dev->power.lock); dev->power.runtime_error = retval; + } else if (dev->type && dev->type->pm + && dev->type->pm->runtime_resume) { + spin_unlock_irq(&dev->power.lock); + + retval = dev->type->pm->runtime_resume(dev); + + spin_lock_irq(&dev->power.lock); + dev->power.runtime_error = retval; + } else if (dev->class && dev->class->pm + && dev->class->pm->runtime_resume) { + spin_unlock_irq(&dev->power.lock); + + retval = dev->class->pm->runtime_resume(dev); + + spin_lock_irq(&dev->power.lock); + dev->power.runtime_error = retval; } else { retval = -ENOSYS; } |