diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/Kconfig | 7 | ||||
-rw-r--r-- | drivers/base/attribute_container.c | 1 | ||||
-rw-r--r-- | drivers/base/bus.c | 1 | ||||
-rw-r--r-- | drivers/base/class.c | 11 | ||||
-rw-r--r-- | drivers/base/core.c | 202 | ||||
-rw-r--r-- | drivers/base/cpu.c | 19 | ||||
-rw-r--r-- | drivers/base/dd.c | 4 | ||||
-rw-r--r-- | drivers/base/devres.c | 1 | ||||
-rw-r--r-- | drivers/base/devtmpfs.c | 6 | ||||
-rw-r--r-- | drivers/base/dma-coherent.c | 1 | ||||
-rw-r--r-- | drivers/base/dma-mapping.c | 1 | ||||
-rw-r--r-- | drivers/base/driver.c | 1 | ||||
-rw-r--r-- | drivers/base/firmware_class.c | 209 | ||||
-rw-r--r-- | drivers/base/iommu.c | 43 | ||||
-rw-r--r-- | drivers/base/memory.c | 3 | ||||
-rw-r--r-- | drivers/base/module.c | 5 | ||||
-rw-r--r-- | drivers/base/node.c | 11 | ||||
-rw-r--r-- | drivers/base/platform.c | 71 | ||||
-rw-r--r-- | drivers/base/power/Makefile | 2 | ||||
-rw-r--r-- | drivers/base/power/main.c | 32 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 64 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 123 | ||||
-rw-r--r-- | drivers/base/power/wakeup.c | 247 | ||||
-rw-r--r-- | drivers/base/sys.c | 1 | ||||
-rw-r--r-- | drivers/base/topology.c | 2 |
25 files changed, 880 insertions, 188 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index fd52c48..ef38aff 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -18,9 +18,9 @@ config UEVENT_HELPER_PATH config DEVTMPFS bool "Maintain a devtmpfs filesystem to mount at /dev" - depends on HOTPLUG && SHMEM && TMPFS + depends on HOTPLUG help - This creates a tmpfs filesystem instance early at bootup. + This creates a tmpfs/ramfs filesystem instance early at bootup. In this filesystem, the kernel driver core maintains device nodes with their default names and permissions for all registered devices with an assigned major/minor number. @@ -33,6 +33,9 @@ config DEVTMPFS functional /dev without any further help. It also allows simple rescue systems, and reliably handles dynamic major/minor numbers. + Notice: if CONFIG_TMPFS isn't enabled, the simpler ramfs + file system will be used instead. + config DEVTMPFS_MOUNT bool "Automount devtmpfs at /dev, after the kernel mounted the rootfs" depends on DEVTMPFS diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index b9cda05..8fc200b 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -328,6 +328,7 @@ attribute_container_add_attrs(struct device *classdev) return sysfs_create_group(&classdev->kobj, cont->grp); for (i = 0; attrs[i]; i++) { + sysfs_attr_init(&attrs[i]->attr); error = device_create_file(classdev, attrs[i]); if (error) return error; diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 71f6af5..12eec3f 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -13,6 +13,7 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/errno.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/string.h> #include "base.h" diff --git a/drivers/base/class.c b/drivers/base/class.c index 0147f47..8e231d0 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -63,6 +63,14 @@ static void class_release(struct kobject *kobj) kfree(cp); } +static const struct kobj_ns_type_operations *class_child_ns_type(struct kobject *kobj) +{ + struct class_private *cp = to_class(kobj); + struct class *class = cp->class; + + return class->ns_type; +} + static const struct sysfs_ops class_sysfs_ops = { .show = class_attr_show, .store = class_attr_store, @@ -71,6 +79,7 @@ static const struct sysfs_ops class_sysfs_ops = { static struct kobj_type class_ktype = { .sysfs_ops = &class_sysfs_ops, .release = class_release, + .child_ns_type = class_child_ns_type, }; /* Hotplug events for classes go to the class class_subsys */ @@ -219,6 +228,8 @@ static void class_create_release(struct class *cls) * This is used to create a struct class pointer that can then be used * in calls to device_create(). * + * Returns &struct class pointer on success, or ERR_PTR() on error. + * * Note, the pointer created here is to be destroyed when finished by * making a call to class_destroy(). */ diff --git a/drivers/base/core.c b/drivers/base/core.c index ef55df3..f8e7272 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -20,7 +20,6 @@ #include <linux/notifier.h> #include <linux/genhd.h> #include <linux/kallsyms.h> -#include <linux/semaphore.h> #include <linux/mutex.h> #include <linux/async.h> @@ -132,9 +131,21 @@ static void device_release(struct kobject *kobj) kfree(p); } +static const void *device_namespace(struct kobject *kobj) +{ + struct device *dev = to_dev(kobj); + const void *ns = NULL; + + if (dev->class && dev->class->ns_type) + ns = dev->class->namespace(dev); + + return ns; +} + static struct kobj_type device_ktype = { .release = device_release, .sysfs_ops = &dev_sysfs_ops, + .namespace = device_namespace, }; @@ -559,10 +570,10 @@ void device_initialize(struct device *dev) dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); INIT_LIST_HEAD(&dev->dma_pools); - init_MUTEX(&dev->sem); + mutex_init(&dev->mutex); + lockdep_set_novalidate_class(&dev->mutex); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); - device_init_wakeup(dev, 0); device_pm_init(dev); set_dev_node(dev, -1); } @@ -596,11 +607,59 @@ static struct kobject *virtual_device_parent(struct device *dev) return virtual_dir; } -static struct kobject *get_device_parent(struct device *dev, - struct device *parent) +struct class_dir { + struct kobject kobj; + struct class *class; +}; + +#define to_class_dir(obj) container_of(obj, struct class_dir, kobj) + +static void class_dir_release(struct kobject *kobj) +{ + struct class_dir *dir = to_class_dir(kobj); + kfree(dir); +} + +static const +struct kobj_ns_type_operations *class_dir_child_ns_type(struct kobject *kobj) +{ + struct class_dir *dir = to_class_dir(kobj); + return dir->class->ns_type; +} + +static struct kobj_type class_dir_ktype = { + .release = class_dir_release, + .sysfs_ops = &kobj_sysfs_ops, + .child_ns_type = class_dir_child_ns_type +}; + +static struct kobject * +class_dir_create_and_add(struct class *class, struct kobject *parent_kobj) { + struct class_dir *dir; int retval; + dir = kzalloc(sizeof(*dir), GFP_KERNEL); + if (!dir) + return NULL; + + dir->class = class; + kobject_init(&dir->kobj, &class_dir_ktype); + + dir->kobj.kset = &class->p->class_dirs; + + retval = kobject_add(&dir->kobj, parent_kobj, "%s", class->name); + if (retval < 0) { + kobject_put(&dir->kobj); + return NULL; + } + return &dir->kobj; +} + + +static struct kobject *get_device_parent(struct device *dev, + struct device *parent) +{ if (dev->class) { static DEFINE_MUTEX(gdp_mutex); struct kobject *kobj = NULL; @@ -614,7 +673,7 @@ static struct kobject *get_device_parent(struct device *dev, */ if (parent == NULL) parent_kobj = virtual_device_parent(dev); - else if (parent->class) + else if (parent->class && !dev->class->ns_type) return &parent->kobj; else parent_kobj = &parent->kobj; @@ -635,18 +694,7 @@ static struct kobject *get_device_parent(struct device *dev, } /* or create a new class-directory at the parent device */ - k = kobject_create(); - if (!k) { - mutex_unlock(&gdp_mutex); - return NULL; - } - k->kset = &dev->class->p->class_dirs; - retval = kobject_add(k, parent_kobj, "%s", dev->class->name); - if (retval < 0) { - mutex_unlock(&gdp_mutex); - kobject_put(k); - return NULL; - } + k = class_dir_create_and_add(dev->class, parent_kobj); /* do not emit an uevent for this simple "glue" directory */ mutex_unlock(&gdp_mutex); return k; @@ -738,7 +786,7 @@ out_device: out_busid: if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && device_is_not_partition(dev)) - sysfs_remove_link(&dev->class->p->class_subsys.kobj, + sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); #else /* link in the class directory pointing to the device */ @@ -756,7 +804,7 @@ out_busid: return 0; out_busid: - sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev)); + sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); #endif out_subsys: @@ -784,13 +832,13 @@ static void device_remove_class_symlinks(struct device *dev) if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && device_is_not_partition(dev)) - sysfs_remove_link(&dev->class->p->class_subsys.kobj, + sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); #else if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); - sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev)); + sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); #endif sysfs_remove_link(&dev->kobj, "subsystem"); @@ -1345,6 +1393,8 @@ static void root_device_release(struct device *dev) * 'module' symlink which points to the @owner directory * in sysfs. * + * Returns &struct device pointer on success, or ERR_PTR() on error. + * * Note: You probably want to use root_device_register(). */ struct device *__root_device_register(const char *name, struct module *owner) @@ -1370,7 +1420,7 @@ struct device *__root_device_register(const char *name, struct module *owner) return ERR_PTR(err); } -#ifdef CONFIG_MODULE /* gotta find a "cleaner" way to do this */ +#ifdef CONFIG_MODULES /* gotta find a "cleaner" way to do this */ if (owner) { struct module_kobject *mk = &owner->mkobj; @@ -1432,6 +1482,8 @@ static void device_create_release(struct device *dev) * Any further sysfs files that might be required can be created using this * pointer. * + * Returns &struct device pointer on success, or ERR_PTR() on error. + * * Note: the struct class passed to this function must have previously * been created with a call to class_create(). */ @@ -1492,6 +1544,8 @@ EXPORT_SYMBOL_GPL(device_create_vargs); * Any further sysfs files that might be required can be created using this * pointer. * + * Returns &struct device pointer on success, or ERR_PTR() on error. + * * Note: the struct class passed to this function must have previously * been created with a call to class_create(). */ @@ -1570,6 +1624,14 @@ int device_rename(struct device *dev, char *new_name) goto out; } +#ifndef CONFIG_SYSFS_DEPRECATED + if (dev->class) { + error = sysfs_rename_link(&dev->class->p->class_subsys.kobj, + &dev->kobj, old_device_name, new_name); + if (error) + goto out; + } +#endif error = kobject_rename(&dev->kobj, new_name); if (error) goto out; @@ -1584,11 +1646,6 @@ int device_rename(struct device *dev, char *new_name) new_class_name); } } -#else - if (dev->class) { - error = sysfs_rename_link(&dev->class->p->class_subsys.kobj, - &dev->kobj, old_device_name, new_name); - } #endif out: @@ -1729,10 +1786,25 @@ EXPORT_SYMBOL_GPL(device_move); */ void device_shutdown(void) { - struct device *dev, *devn; + struct device *dev; + + spin_lock(&devices_kset->list_lock); + /* + * Walk the devices list backward, shutting down each in turn. + * Beware that device unplug events may also start pulling + * devices offline, even as the system is shutting down. + */ + while (!list_empty(&devices_kset->list)) { + dev = list_entry(devices_kset->list.prev, struct device, + kobj.entry); + get_device(dev); + /* + * Make sure the device is off the kset list, in the + * event that dev->*->shutdown() doesn't remove it. + */ + list_del_init(&dev->kobj.entry); + spin_unlock(&devices_kset->list_lock); - list_for_each_entry_safe_reverse(dev, devn, &devices_kset->list, - kobj.entry) { if (dev->bus && dev->bus->shutdown) { dev_dbg(dev, "shutdown\n"); dev->bus->shutdown(dev); @@ -1740,6 +1812,74 @@ void device_shutdown(void) dev_dbg(dev, "shutdown\n"); dev->driver->shutdown(dev); } + put_device(dev); + + spin_lock(&devices_kset->list_lock); } + spin_unlock(&devices_kset->list_lock); async_synchronize_full(); } + +/* + * Device logging functions + */ + +#ifdef CONFIG_PRINTK + +static int __dev_printk(const char *level, const struct device *dev, + struct va_format *vaf) +{ + if (!dev) + return printk("%s(NULL device *): %pV", level, vaf); + + return printk("%s%s %s: %pV", + level, dev_driver_string(dev), dev_name(dev), vaf); +} + +int dev_printk(const char *level, const struct device *dev, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int r; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + r = __dev_printk(level, dev, &vaf); + va_end(args); + + return r; +} +EXPORT_SYMBOL(dev_printk); + +#define define_dev_printk_level(func, kern_level) \ +int func(const struct device *dev, const char *fmt, ...) \ +{ \ + struct va_format vaf; \ + va_list args; \ + int r; \ + \ + va_start(args, fmt); \ + \ + vaf.fmt = fmt; \ + vaf.va = &args; \ + \ + r = __dev_printk(kern_level, dev, &vaf); \ + va_end(args); \ + \ + return r; \ +} \ +EXPORT_SYMBOL(func); + +define_dev_printk_level(dev_emerg, KERN_EMERG); +define_dev_printk_level(dev_alert, KERN_ALERT); +define_dev_printk_level(dev_crit, KERN_CRIT); +define_dev_printk_level(dev_err, KERN_ERR); +define_dev_printk_level(dev_warn, KERN_WARNING); +define_dev_printk_level(dev_notice, KERN_NOTICE); +define_dev_printk_level(_dev_info, KERN_INFO); + +#endif diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 7036e8e..251acea 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -10,6 +10,7 @@ #include <linux/topology.h> #include <linux/device.h> #include <linux/node.h> +#include <linux/gfp.h> #include "base.h" @@ -79,24 +80,24 @@ void unregister_cpu(struct cpu *cpu) } #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE -static ssize_t cpu_probe_store(struct sys_device *dev, - struct sysdev_attribute *attr, - const char *buf, +static ssize_t cpu_probe_store(struct sysdev_class *class, + struct sysdev_class_attribute *attr, + const char *buf, size_t count) { return arch_cpu_probe(buf, count); } -static ssize_t cpu_release_store(struct sys_device *dev, - struct sysdev_attribute *attr, - const char *buf, +static ssize_t cpu_release_store(struct sysdev_class *class, + struct sysdev_class_attribute *attr, + const char *buf, size_t count) { return arch_cpu_release(buf, count); } -static SYSDEV_ATTR(probe, S_IWUSR, NULL, cpu_probe_store); -static SYSDEV_ATTR(release, S_IWUSR, NULL, cpu_release_store); +static SYSDEV_CLASS_ATTR(probe, S_IWUSR, NULL, cpu_probe_store); +static SYSDEV_CLASS_ATTR(release, S_IWUSR, NULL, cpu_release_store); #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ #else /* ... !CONFIG_HOTPLUG_CPU */ @@ -185,7 +186,7 @@ static ssize_t print_cpus_offline(struct sysdev_class *class, /* display offline cpus < nr_cpu_ids */ if (!alloc_cpumask_var(&offline, GFP_KERNEL)) return -ENOMEM; - cpumask_complement(offline, cpu_online_mask); + cpumask_andnot(offline, cpu_possible_mask, cpu_online_mask); n = cpulist_scnprintf(buf, len, offline); free_cpumask_var(offline); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index c89291f..503c262 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -40,11 +40,11 @@ static void driver_bound(struct device *dev) pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev), __func__, dev->driver->name); + klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); + if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_BOUND_DRIVER, dev); - - klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); } static int driver_sysfs_add(struct device *dev) diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 05dd307..cf7a0c7 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -9,6 +9,7 @@ #include <linux/device.h> #include <linux/module.h> +#include <linux/slab.h> #include "base.h" diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index dac478c..af06001 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -20,9 +20,11 @@ #include <linux/namei.h> #include <linux/fs.h> #include <linux/shmem_fs.h> +#include <linux/ramfs.h> #include <linux/cred.h> #include <linux/sched.h> #include <linux/init_task.h> +#include <linux/slab.h> static struct vfsmount *dev_mnt; @@ -44,7 +46,11 @@ __setup("devtmpfs.mount=", mount_param); static int dev_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { +#ifdef CONFIG_TMPFS return get_sb_single(fs_type, flags, data, shmem_fill_super, mnt); +#else + return get_sb_single(fs_type, flags, data, ramfs_fill_super, mnt); +#endif } static struct file_system_type dev_fs_type = { diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index 962a3b5..d4d8ce5 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -2,6 +2,7 @@ * Coherent per-device memory handling. * Borrowed from i386 */ +#include <linux/slab.h> #include <linux/kernel.h> #include <linux/dma-mapping.h> diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index ca9186f..763d59c 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c @@ -8,6 +8,7 @@ */ #include <linux/dma-mapping.h> +#include <linux/gfp.h> /* * Managed DMA API diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 90c9fff..b631f7c 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -13,6 +13,7 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/errno.h> +#include <linux/slab.h> #include <linux/string.h> #include "base.h" diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index d0dc26a..3f093b0 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -19,6 +19,7 @@ #include <linux/kthread.h> #include <linux/highmem.h> #include <linux/firmware.h> +#include <linux/slab.h> #define to_dev(obj) container_of(obj, struct device, kobj) @@ -26,6 +27,52 @@ MODULE_AUTHOR("Manuel Estrada Sainz"); MODULE_DESCRIPTION("Multi purpose firmware loading support"); MODULE_LICENSE("GPL"); +/* Builtin firmware support */ + +#ifdef CONFIG_FW_LOADER + +extern struct builtin_fw __start_builtin_fw[]; +extern struct builtin_fw __end_builtin_fw[]; + +static bool fw_get_builtin_firmware(struct firmware *fw, const char *name) +{ + struct builtin_fw *b_fw; + + for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) { + if (strcmp(name, b_fw->name) == 0) { + fw->size = b_fw->size; + fw->data = b_fw->data; + return true; + } + } + + return false; +} + +static bool fw_is_builtin_firmware(const struct firmware *fw) +{ + struct builtin_fw *b_fw; + + for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) + if (fw->data == b_fw->data) + return true; + + return false; +} + +#else /* Module case - no builtin firmware support */ + +static inline bool fw_get_builtin_firmware(struct firmware *fw, const char *name) +{ + return false; +} + +static inline bool fw_is_builtin_firmware(const struct firmware *fw) +{ + return false; +} +#endif + enum { FW_STATUS_LOADING, FW_STATUS_DONE, @@ -39,7 +86,6 @@ static int loading_timeout = 60; /* In seconds */ static DEFINE_MUTEX(fw_lock); struct firmware_priv { - char *fw_id; struct completion completion; struct bin_attribute attr_data; struct firmware *fw; @@ -47,18 +93,11 @@ struct firmware_priv { struct page **pages; int nr_pages; int page_array_size; - const char *vdata; struct timer_list timeout; + bool nowait; + char fw_id[]; }; -#ifdef CONFIG_FW_LOADER -extern struct builtin_fw __start_builtin_fw[]; -extern struct builtin_fw __end_builtin_fw[]; -#else /* Module case. Avoid ifdefs later; it'll all optimise out */ -static struct builtin_fw *__start_builtin_fw; -static struct builtin_fw *__end_builtin_fw; -#endif - static void fw_load_abort(struct firmware_priv *fw_priv) { @@ -78,6 +117,7 @@ firmware_timeout_show(struct class *class, /** * firmware_timeout_store - set number of seconds to wait for firmware * @class: device class pointer + * @attr: device attribute pointer * @buf: buffer to scan for timeout value * @count: number of bytes in @buf * @@ -98,9 +138,25 @@ firmware_timeout_store(struct class *class, return count; } -static CLASS_ATTR(timeout, 0644, firmware_timeout_show, firmware_timeout_store); +static struct class_attribute firmware_class_attrs[] = { + __ATTR(timeout, S_IWUSR | S_IRUGO, + firmware_timeout_show, firmware_timeout_store), + __ATTR_NULL +}; -static void fw_dev_release(struct device *dev); +static void fw_dev_release(struct device *dev) +{ + struct firmware_priv *fw_priv = dev_get_drvdata(dev); + int i; + + for (i = 0; i < fw_priv->nr_pages; i++) + __free_page(fw_priv->pages[i]); + kfree(fw_priv->pages); + kfree(fw_priv); + kfree(dev); + + module_put(THIS_MODULE); +} static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env) { @@ -110,12 +166,15 @@ static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env) return -ENOMEM; if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout)) return -ENOMEM; + if (add_uevent_var(env, "ASYNC=%d", fw_priv->nowait)) + return -ENOMEM; return 0; } static struct class firmware_class = { .name = "firmware", + .class_attrs = firmware_class_attrs, .dev_uevent = firmware_uevent, .dev_release = fw_dev_release, }; @@ -128,6 +187,17 @@ static ssize_t firmware_loading_show(struct device *dev, return sprintf(buf, "%d\n", loading); } +static void firmware_free_data(const struct firmware *fw) +{ + int i; + vunmap(fw->data); + if (fw->pages) { + for (i = 0; i < PFN_UP(fw->size); i++) + __free_page(fw->pages[i]); + kfree(fw->pages); + } +} + /* Some architectures don't have PAGE_KERNEL_RO */ #ifndef PAGE_KERNEL_RO #define PAGE_KERNEL_RO PAGE_KERNEL @@ -160,21 +230,21 @@ static ssize_t firmware_loading_store(struct device *dev, mutex_unlock(&fw_lock); break; } - vfree(fw_priv->fw->data); - fw_priv->fw->data = NULL; + firmware_free_data(fw_priv->fw); + memset(fw_priv->fw, 0, sizeof(struct firmware)); + /* If the pages are not owned by 'struct firmware' */ for (i = 0; i < fw_priv->nr_pages; i++) __free_page(fw_priv->pages[i]); kfree(fw_priv->pages); fw_priv->pages = NULL; fw_priv->page_array_size = 0; fw_priv->nr_pages = 0; - fw_priv->fw->size = 0; set_bit(FW_STATUS_LOADING, &fw_priv->status); mutex_unlock(&fw_lock); break; case 0: if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { - vfree(fw_priv->fw->data); + vunmap(fw_priv->fw->data); fw_priv->fw->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0, PAGE_KERNEL_RO); @@ -182,7 +252,10 @@ static ssize_t firmware_loading_store(struct device *dev, dev_err(dev, "%s: vmap() failed\n", __func__); goto err; } - /* Pages will be freed by vfree() */ + /* Pages are now owned by 'struct firmware' */ + fw_priv->fw->pages = fw_priv->pages; + fw_priv->pages = NULL; + fw_priv->page_array_size = 0; fw_priv->nr_pages = 0; complete(&fw_priv->completion); @@ -205,8 +278,9 @@ static ssize_t firmware_loading_store(struct device *dev, static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); static ssize_t -firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buffer, loff_t offset, size_t count) +firmware_data_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buffer, loff_t offset, + size_t count) { struct device *dev = to_dev(kobj); struct firmware_priv *fw_priv = dev_get_drvdata(dev); @@ -289,6 +363,7 @@ fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) /** * firmware_data_write - write method for firmware + * @filp: open sysfs file * @kobj: kobject for the device * @bin_attr: bin_attr structure * @buffer: buffer being written @@ -299,8 +374,9 @@ fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) * the driver as a firmware image. **/ static ssize_t -firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buffer, loff_t offset, size_t count) +firmware_data_write(struct file* filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buffer, + loff_t offset, size_t count) { struct device *dev = to_dev(kobj); struct firmware_priv *fw_priv = dev_get_drvdata(dev); @@ -351,21 +427,6 @@ static struct bin_attribute firmware_attr_data_tmpl = { .write = firmware_data_write, }; -static void fw_dev_release(struct device *dev) -{ - struct firmware_priv *fw_priv = dev_get_drvdata(dev); - int i; - - for (i = 0; i < fw_priv->nr_pages; i++) - __free_page(fw_priv->pages[i]); - kfree(fw_priv->pages); - kfree(fw_priv->fw_id); - kfree(fw_priv); - kfree(dev); - - module_put(THIS_MODULE); -} - static void firmware_class_timeout(u_long data) { @@ -377,8 +438,8 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, struct device *device) { int retval; - struct firmware_priv *fw_priv = kzalloc(sizeof(*fw_priv), - GFP_KERNEL); + struct firmware_priv *fw_priv = + kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL); struct device *f_dev = kzalloc(sizeof(*f_dev), GFP_KERNEL); *dev_p = NULL; @@ -389,16 +450,9 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, goto error_kfree; } + strcpy(fw_priv->fw_id, fw_name); init_completion(&fw_priv->completion); fw_priv->attr_data = firmware_attr_data_tmpl; - fw_priv->fw_id = kstrdup(fw_name, GFP_KERNEL); - if (!fw_priv->fw_id) { - dev_err(device, "%s: Firmware name allocation failed\n", - __func__); - retval = -ENOMEM; - goto error_kfree; - } - fw_priv->timeout.function = firmware_class_timeout; fw_priv->timeout.data = (u_long) fw_priv; init_timer(&fw_priv->timeout); @@ -425,7 +479,7 @@ error_kfree: static int fw_setup_device(struct firmware *fw, struct device **dev_p, const char *fw_name, struct device *device, - int uevent) + int uevent, bool nowait) { struct device *f_dev; struct firmware_priv *fw_priv; @@ -441,7 +495,10 @@ static int fw_setup_device(struct firmware *fw, struct device **dev_p, fw_priv = dev_get_drvdata(f_dev); + fw_priv->nowait = nowait; + fw_priv->fw = fw; + sysfs_bin_attr_init(&fw_priv->attr_data); retval = sysfs_create_bin_file(&f_dev->kobj, &fw_priv->attr_data); if (retval) { dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__); @@ -467,12 +524,11 @@ out: static int _request_firmware(const struct firmware **firmware_p, const char *name, - struct device *device, int uevent) + struct device *device, int uevent, bool nowait) { struct device *f_dev; struct firmware_priv *fw_priv; struct firmware *firmware; - struct builtin_fw *builtin; int retval; if (!firmware_p) @@ -486,21 +542,16 @@ _request_firmware(const struct firmware **firmware_p, const char *name, goto out; } - for (builtin = __start_builtin_fw; builtin != __end_builtin_fw; - builtin++) { - if (strcmp(name, builtin->name)) - continue; - dev_info(device, "firmware: using built-in firmware %s\n", - name); - firmware->size = builtin->size; - firmware->data = builtin->data; + if (fw_get_builtin_firmware(firmware, name)) { + dev_dbg(device, "firmware: using built-in firmware %s\n", name); return 0; } if (uevent) - dev_info(device, "firmware: requesting %s\n", name); + dev_dbg(device, "firmware: requesting %s\n", name); - retval = fw_setup_device(firmware, &f_dev, name, device, uevent); + retval = fw_setup_device(firmware, &f_dev, name, device, + uevent, nowait); if (retval) goto error_kfree_fw; @@ -557,26 +608,18 @@ request_firmware(const struct firmware **firmware_p, const char *name, struct device *device) { int uevent = 1; - return _request_firmware(firmware_p, name, device, uevent); + return _request_firmware(firmware_p, name, device, uevent, false); } /** * release_firmware: - release the resource associated with a firmware image * @fw: firmware resource to release **/ -void -release_firmware(const struct firmware *fw) +void release_firmware(const struct firmware *fw) { - struct builtin_fw *builtin; - if (fw) { - for (builtin = __start_builtin_fw; builtin != __end_builtin_fw; - builtin++) { - if (fw->data == builtin->data) - goto free_fw; - } - vfree(fw->data); - free_fw: + if (!fw_is_builtin_firmware(fw)) + firmware_free_data(fw); kfree(fw); } } @@ -603,7 +646,7 @@ request_firmware_work_func(void *arg) return 0; } ret = _request_firmware(&fw, fw_work->name, fw_work->device, - fw_work->uevent); + fw_work->uevent, true); fw_work->cont(fw, fw_work->context); @@ -667,26 +710,12 @@ request_firmware_nowait( return 0; } -static int __init -firmware_class_init(void) +static int __init firmware_class_init(void) { - int error; - error = class_register(&firmware_class); - if (error) { - printk(KERN_ERR "%s: class_register failed\n", __func__); - return error; - } - error = class_create_file(&firmware_class, &class_attr_timeout); - if (error) { - printk(KERN_ERR "%s: class_create_file failed\n", - __func__); - class_unregister(&firmware_class); - } - return error; - + return class_register(&firmware_class); } -static void __exit -firmware_class_exit(void) + +static void __exit firmware_class_exit(void) { class_unregister(&firmware_class); } diff --git a/drivers/base/iommu.c b/drivers/base/iommu.c index 8ad4ffe..6e6b6a1 100644 --- a/drivers/base/iommu.c +++ b/drivers/base/iommu.c @@ -80,20 +80,6 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) } EXPORT_SYMBOL_GPL(iommu_detach_device); -int iommu_map_range(struct iommu_domain *domain, unsigned long iova, - phys_addr_t paddr, size_t size, int prot) -{ - return iommu_ops->map(domain, iova, paddr, size, prot); -} -EXPORT_SYMBOL_GPL(iommu_map_range); - -void iommu_unmap_range(struct iommu_domain *domain, unsigned long iova, - size_t size) -{ - iommu_ops->unmap(domain, iova, size); -} -EXPORT_SYMBOL_GPL(iommu_unmap_range); - phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) { @@ -107,3 +93,32 @@ int iommu_domain_has_cap(struct iommu_domain *domain, return iommu_ops->domain_has_cap(domain, cap); } EXPORT_SYMBOL_GPL(iommu_domain_has_cap); + +int iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, int gfp_order, int prot) +{ + unsigned long invalid_mask; + size_t size; + + size = 0x1000UL << gfp_order; + invalid_mask = size - 1; + + BUG_ON((iova | paddr) & invalid_mask); + + return iommu_ops->map(domain, iova, paddr, gfp_order, prot); +} +EXPORT_SYMBOL_GPL(iommu_map); + +int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order) +{ + unsigned long invalid_mask; + size_t size; + + size = 0x1000UL << gfp_order; + invalid_mask = size - 1; + + BUG_ON(iova & invalid_mask); + + return iommu_ops->unmap(domain, iova, gfp_order); +} +EXPORT_SYMBOL_GPL(iommu_unmap); diff --git a/drivers/base/memory.c b/drivers/base/memory.c index db0848e..933442f 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -22,6 +22,7 @@ #include <linux/mm.h> #include <linux/mutex.h> #include <linux/stat.h> +#include <linux/slab.h> #include <asm/atomic.h> #include <asm/uaccess.h> @@ -312,7 +313,7 @@ static ssize_t print_block_size(struct sysdev_class *class, struct sysdev_class_attribute *attr, char *buf) { - return sprintf(buf, "%#lx\n", (unsigned long)PAGES_PER_SECTION * PAGE_SIZE); + return sprintf(buf, "%lx\n", (unsigned long)PAGES_PER_SECTION * PAGE_SIZE); } static SYSDEV_CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL); diff --git a/drivers/base/module.c b/drivers/base/module.c index 103be9c..db930d3 100644 --- a/drivers/base/module.c +++ b/drivers/base/module.c @@ -7,6 +7,7 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/errno.h> +#include <linux/slab.h> #include <linux/string.h> #include "base.h" @@ -14,12 +15,10 @@ static char *make_driver_name(struct device_driver *drv) { char *driver_name; - driver_name = kmalloc(strlen(drv->name) + strlen(drv->bus->name) + 2, - GFP_KERNEL); + driver_name = kasprintf(GFP_KERNEL, "%s:%s", drv->bus->name, drv->name); if (!driver_name) return NULL; - sprintf(driver_name, "%s:%s", drv->bus->name, drv->name); return driver_name; } diff --git a/drivers/base/node.c b/drivers/base/node.c index ad43185..2bdd8a9 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -9,12 +9,14 @@ #include <linux/memory.h> #include <linux/node.h> #include <linux/hugetlb.h> +#include <linux/compaction.h> #include <linux/cpumask.h> #include <linux/topology.h> #include <linux/nodemask.h> #include <linux/cpu.h> #include <linux/device.h> #include <linux/swap.h> +#include <linux/slab.h> static struct sysdev_class_attribute *node_state_attrs[]; @@ -165,8 +167,11 @@ static ssize_t node_read_distance(struct sys_device * dev, int len = 0; int i; - /* buf currently PAGE_SIZE, need ~4 chars per node */ - BUILD_BUG_ON(MAX_NUMNODES*4 > PAGE_SIZE/2); + /* + * buf is currently PAGE_SIZE in length and each node needs 4 chars + * at the most (distance + space or newline). + */ + BUILD_BUG_ON(MAX_NUMNODES * 4 > PAGE_SIZE); for_each_online_node(i) len += sprintf(buf + len, "%s%d", i ? " " : "", node_distance(nid, i)); @@ -242,6 +247,8 @@ int register_node(struct node *node, int num, struct node *parent) scan_unevictable_register_node(node); hugetlb_register_node(node); + + compaction_register_node(node); } return error; } diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 1ba9d617..4d99c8b 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -187,7 +187,7 @@ EXPORT_SYMBOL_GPL(platform_device_alloc); * released. */ int platform_device_add_resources(struct platform_device *pdev, - struct resource *res, unsigned int num) + const struct resource *res, unsigned int num) { struct resource *r; @@ -362,10 +362,12 @@ EXPORT_SYMBOL_GPL(platform_device_unregister); * enumeration tasks, they don't fully conform to the Linux driver model. * In particular, when such drivers are built as modules, they can't be * "hotplugged". + * + * Returns &struct platform_device pointer on success, or ERR_PTR() on error. */ struct platform_device *platform_device_register_simple(const char *name, int id, - struct resource *res, + const struct resource *res, unsigned int num) { struct platform_device *pdev; @@ -408,6 +410,8 @@ EXPORT_SYMBOL_GPL(platform_device_register_simple); * allocated for the device allows drivers using such devices to be * unloaded without waiting for the last reference to the device to be * dropped. + * + * Returns &struct platform_device pointer on success, or ERR_PTR() on error. */ struct platform_device *platform_device_register_data( struct device *parent, @@ -559,6 +563,8 @@ EXPORT_SYMBOL_GPL(platform_driver_probe); * * Use this in legacy-style modules that probe hardware directly and * register a single platform device and corresponding platform driver. + * + * Returns &struct platform_device pointer on success, or ERR_PTR() on error. */ struct platform_device * __init_or_module platform_create_bundle( struct platform_driver *driver, @@ -729,7 +735,7 @@ static void platform_pm_complete(struct device *dev) #ifdef CONFIG_SUSPEND -static int platform_pm_suspend(struct device *dev) +int __weak platform_pm_suspend(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -747,7 +753,7 @@ static int platform_pm_suspend(struct device *dev) return ret; } -static int platform_pm_suspend_noirq(struct device *dev) +int __weak platform_pm_suspend_noirq(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -763,7 +769,7 @@ static int platform_pm_suspend_noirq(struct device *dev) return ret; } -static int platform_pm_resume(struct device *dev) +int __weak platform_pm_resume(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -781,7 +787,7 @@ static int platform_pm_resume(struct device *dev) return ret; } -static int platform_pm_resume_noirq(struct device *dev) +int __weak platform_pm_resume_noirq(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -961,17 +967,17 @@ static int platform_pm_restore_noirq(struct device *dev) int __weak platform_pm_runtime_suspend(struct device *dev) { - return -ENOSYS; + return pm_generic_runtime_suspend(dev); }; int __weak platform_pm_runtime_resume(struct device *dev) { - return -ENOSYS; + return pm_generic_runtime_resume(dev); }; int __weak platform_pm_runtime_idle(struct device *dev) { - return -ENOSYS; + return pm_generic_runtime_idle(dev); }; #else /* !CONFIG_PM_RUNTIME */ @@ -1052,9 +1058,11 @@ static __initdata LIST_HEAD(early_platform_driver_list); static __initdata LIST_HEAD(early_platform_device_list); /** - * early_platform_driver_register + * early_platform_driver_register - register early platform driver * @epdrv: early_platform driver structure * @buf: string passed from early_param() + * + * Helper function for early_platform_init() / early_platform_init_buffer() */ int __init early_platform_driver_register(struct early_platform_driver *epdrv, char *buf) @@ -1106,9 +1114,12 @@ int __init early_platform_driver_register(struct early_platform_driver *epdrv, } /** - * early_platform_add_devices - add a numbers of early platform devices + * early_platform_add_devices - adds a number of early platform devices * @devs: array of early platform devices to add * @num: number of early platform devices in array + * + * Used by early architecture code to register early platform devices and + * their platform data. */ void __init early_platform_add_devices(struct platform_device **devs, int num) { @@ -1128,8 +1139,12 @@ void __init early_platform_add_devices(struct platform_device **devs, int num) } /** - * early_platform_driver_register_all + * early_platform_driver_register_all - register early platform drivers * @class_str: string to identify early platform driver class + * + * Used by architecture code to register all early platform drivers + * for a certain class. If omitted then only early platform drivers + * with matching kernel command line class parameters will be registered. */ void __init early_platform_driver_register_all(char *class_str) { @@ -1151,7 +1166,7 @@ void __init early_platform_driver_register_all(char *class_str) } /** - * early_platform_match + * early_platform_match - find early platform device matching driver * @epdrv: early platform driver structure * @id: id to match against */ @@ -1169,7 +1184,7 @@ early_platform_match(struct early_platform_driver *epdrv, int id) } /** - * early_platform_left + * early_platform_left - check if early platform driver has matching devices * @epdrv: early platform driver structure * @id: return true if id or above exists */ @@ -1187,7 +1202,7 @@ static __init int early_platform_left(struct early_platform_driver *epdrv, } /** - * early_platform_driver_probe_id + * early_platform_driver_probe_id - probe drivers matching class_str and id * @class_str: string to identify early platform driver class * @id: id to match against * @nr_probe: number of platform devices to successfully probe before exiting @@ -1239,6 +1254,26 @@ static int __init early_platform_driver_probe_id(char *class_str, } if (match) { + /* + * Set up a sensible init_name to enable + * dev_name() and others to be used before the + * rest of the driver core is initialized. + */ + if (!match->dev.init_name && slab_is_available()) { + if (match->id != -1) + match->dev.init_name = + kasprintf(GFP_KERNEL, "%s.%d", + match->name, + match->id); + else + match->dev.init_name = + kasprintf(GFP_KERNEL, "%s", + match->name); + + if (!match->dev.init_name) + return -ENOMEM; + } + if (epdrv->pdrv->probe(match)) pr_warning("%s: unable to probe %s early.\n", class_str, match->name); @@ -1257,10 +1292,14 @@ static int __init early_platform_driver_probe_id(char *class_str, } /** - * early_platform_driver_probe + * early_platform_driver_probe - probe a class of registered drivers * @class_str: string to identify early platform driver class * @nr_probe: number of platform devices to successfully probe before exiting * @user_only: only probe user specified early platform devices + * + * Used by architecture code to probe registered early platform drivers + * within a certain class. For probe to happen a registered early platform + * device matching a registered early platform driver is needed. */ int __init early_platform_driver_probe(char *class_str, int nr_probe, diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 89de753..cbccf9a 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_PM) += sysfs.o -obj-$(CONFIG_PM_SLEEP) += main.o +obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_OPS) += generic_ops.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index d477f4d..5419a49 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -59,6 +59,7 @@ void device_pm_init(struct device *dev) { dev->power.status = DPM_ON; init_completion(&dev->power.completion); + dev->power.wakeup_count = 0; pm_runtime_init(dev); } @@ -439,8 +440,23 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) if (dev->bus && dev->bus->pm) { pm_dev_dbg(dev, state, "EARLY "); error = pm_noirq_op(dev, dev->bus->pm, state); + if (error) + goto End; } + if (dev->type && dev->type->pm) { + pm_dev_dbg(dev, state, "EARLY type "); + error = pm_noirq_op(dev, dev->type->pm, state); + if (error) + goto End; + } + + if (dev->class && dev->class->pm) { + pm_dev_dbg(dev, state, "EARLY class "); + error = pm_noirq_op(dev, dev->class->pm, state); + } + +End: TRACE_RESUME(error); return error; } @@ -735,10 +751,26 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) { int error = 0; + if (dev->class && dev->class->pm) { + pm_dev_dbg(dev, state, "LATE class "); + error = pm_noirq_op(dev, dev->class->pm, state); + if (error) + goto End; + } + + if (dev->type && dev->type->pm) { + pm_dev_dbg(dev, state, "LATE type "); + error = pm_noirq_op(dev, dev->type->pm, state); + if (error) + goto End; + } + if (dev->bus && dev->bus->pm) { pm_dev_dbg(dev, state, "LATE "); error = pm_noirq_op(dev, dev->bus->pm, state); } + +End: return error; } diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 626dd14..b78c401 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -123,6 +123,45 @@ int pm_runtime_idle(struct device *dev) } EXPORT_SYMBOL_GPL(pm_runtime_idle); + +/** + * update_pm_runtime_accounting - Update the time accounting of power states + * @dev: Device to update the accounting for + * + * In order to be able to have time accounting of the various power states + * (as used by programs such as PowerTOP to show the effectiveness of runtime + * PM), we need to track the time spent in each state. + * update_pm_runtime_accounting must be called each time before the + * runtime_status field is updated, to account the time in the old state + * correctly. + */ +void update_pm_runtime_accounting(struct device *dev) +{ + unsigned long now = jiffies; + int delta; + + delta = now - dev->power.accounting_timestamp; + + if (delta < 0) + delta = 0; + + dev->power.accounting_timestamp = now; + + if (dev->power.disable_depth > 0) + return; + + if (dev->power.runtime_status == RPM_SUSPENDED) + dev->power.suspended_jiffies += delta; + else + dev->power.active_jiffies += delta; +} + +static void __update_runtime_status(struct device *dev, enum rpm_status status) +{ + update_pm_runtime_accounting(dev); + dev->power.runtime_status = status; +} + /** * __pm_runtime_suspend - Carry out run-time suspend of given device. * @dev: Device to suspend. @@ -197,7 +236,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) goto repeat; } - dev->power.runtime_status = RPM_SUSPENDING; + __update_runtime_status(dev, RPM_SUSPENDING); dev->power.deferred_resume = false; if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { @@ -228,15 +267,17 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) } if (retval) { - dev->power.runtime_status = RPM_ACTIVE; - pm_runtime_cancel_pending(dev); - + __update_runtime_status(dev, RPM_ACTIVE); if (retval == -EAGAIN || retval == -EBUSY) { - notify = true; + if (dev->power.timer_expires == 0) + notify = true; dev->power.runtime_error = 0; + } else { + pm_runtime_cancel_pending(dev); } } else { - dev->power.runtime_status = RPM_SUSPENDED; + __update_runtime_status(dev, RPM_SUSPENDED); + pm_runtime_deactivate_timer(dev); if (dev->parent) { parent = dev->parent; @@ -379,7 +420,7 @@ int __pm_runtime_resume(struct device *dev, bool from_wq) goto repeat; } - dev->power.runtime_status = RPM_RESUMING; + __update_runtime_status(dev, RPM_RESUMING); if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { spin_unlock_irq(&dev->power.lock); @@ -409,10 +450,10 @@ int __pm_runtime_resume(struct device *dev, bool from_wq) } if (retval) { - dev->power.runtime_status = RPM_SUSPENDED; + __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_cancel_pending(dev); } else { - dev->power.runtime_status = RPM_ACTIVE; + __update_runtime_status(dev, RPM_ACTIVE); if (parent) atomic_inc(&parent->power.child_count); } @@ -659,8 +700,6 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay) if (dev->power.runtime_status == RPM_SUSPENDED) retval = 1; - else if (dev->power.runtime_status == RPM_SUSPENDING) - retval = -EINPROGRESS; else if (atomic_read(&dev->power.usage_count) > 0 || dev->power.disable_depth > 0) retval = -EAGAIN; @@ -848,7 +887,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status) } out_set: - dev->power.runtime_status = status; + __update_runtime_status(dev, status); dev->power.runtime_error = 0; out: spin_unlock_irqrestore(&dev->power.lock, flags); @@ -1077,6 +1116,7 @@ void pm_runtime_init(struct device *dev) dev->power.request_pending = false; dev->power.request = RPM_REQ_NONE; dev->power.deferred_resume = false; + dev->power.accounting_timestamp = jiffies; INIT_WORK(&dev->power.work, pm_runtime_work); dev->power.timer_expires = 0; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 86fd937..e56b438 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -5,6 +5,8 @@ #include <linux/device.h> #include <linux/string.h> #include <linux/pm_runtime.h> +#include <asm/atomic.h> +#include <linux/jiffies.h> #include "power.h" /* @@ -72,6 +74,8 @@ * device are known to the PM core. However, for some devices this * attribute is set to "enabled" by bus type code or device drivers and in * that cases it should be safe to leave the default value. + * + * wakeup_count - Report the number of wakeup events related to the device */ static const char enabled[] = "enabled"; @@ -107,6 +111,65 @@ static ssize_t control_store(struct device * dev, struct device_attribute *attr, } static DEVICE_ATTR(control, 0644, control_show, control_store); + +static ssize_t rtpm_active_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + spin_lock_irq(&dev->power.lock); + update_pm_runtime_accounting(dev); + ret = sprintf(buf, "%i\n", jiffies_to_msecs(dev->power.active_jiffies)); + spin_unlock_irq(&dev->power.lock); + return ret; +} + +static DEVICE_ATTR(runtime_active_time, 0444, rtpm_active_time_show, NULL); + +static ssize_t rtpm_suspended_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + spin_lock_irq(&dev->power.lock); + update_pm_runtime_accounting(dev); + ret = sprintf(buf, "%i\n", + jiffies_to_msecs(dev->power.suspended_jiffies)); + spin_unlock_irq(&dev->power.lock); + return ret; +} + +static DEVICE_ATTR(runtime_suspended_time, 0444, rtpm_suspended_time_show, NULL); + +static ssize_t rtpm_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const char *p; + + if (dev->power.runtime_error) { + p = "error\n"; + } else if (dev->power.disable_depth) { + p = "unsupported\n"; + } else { + switch (dev->power.runtime_status) { + case RPM_SUSPENDED: + p = "suspended\n"; + break; + case RPM_SUSPENDING: + p = "suspending\n"; + break; + case RPM_RESUMING: + p = "resuming\n"; + break; + case RPM_ACTIVE: + p = "active\n"; + break; + default: + return -EIO; + } + } + return sprintf(buf, p); +} + +static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL); #endif static ssize_t @@ -143,7 +206,50 @@ wake_store(struct device * dev, struct device_attribute *attr, static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); -#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG +#ifdef CONFIG_PM_SLEEP +static ssize_t wakeup_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", dev->power.wakeup_count); +} + +static DEVICE_ATTR(wakeup_count, 0444, wakeup_count_show, NULL); +#endif + +#ifdef CONFIG_PM_ADVANCED_DEBUG +#ifdef CONFIG_PM_RUNTIME + +static ssize_t rtpm_usagecount_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", atomic_read(&dev->power.usage_count)); +} + +static ssize_t rtpm_children_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", dev->power.ignore_children ? + 0 : atomic_read(&dev->power.child_count)); +} + +static ssize_t rtpm_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if ((dev->power.disable_depth) && (dev->power.runtime_auto == false)) + return sprintf(buf, "disabled & forbidden\n"); + else if (dev->power.disable_depth) + return sprintf(buf, "disabled\n"); + else if (dev->power.runtime_auto == false) + return sprintf(buf, "forbidden\n"); + return sprintf(buf, "enabled\n"); +} + +static DEVICE_ATTR(runtime_usage, 0444, rtpm_usagecount_show, NULL); +static DEVICE_ATTR(runtime_active_kids, 0444, rtpm_children_show, NULL); +static DEVICE_ATTR(runtime_enabled, 0444, rtpm_enabled_show, NULL); + +#endif + static ssize_t async_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -170,15 +276,26 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(async, 0644, async_show, async_store); -#endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */ +#endif /* CONFIG_PM_ADVANCED_DEBUG */ static struct attribute * power_attrs[] = { #ifdef CONFIG_PM_RUNTIME &dev_attr_control.attr, + &dev_attr_runtime_status.attr, + &dev_attr_runtime_suspended_time.attr, + &dev_attr_runtime_active_time.attr, #endif &dev_attr_wakeup.attr, -#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG +#ifdef CONFIG_PM_SLEEP + &dev_attr_wakeup_count.attr, +#endif +#ifdef CONFIG_PM_ADVANCED_DEBUG &dev_attr_async.attr, +#ifdef CONFIG_PM_RUNTIME + &dev_attr_runtime_usage.attr, + &dev_attr_runtime_active_kids.attr, + &dev_attr_runtime_enabled.attr, +#endif #endif NULL, }; diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c new file mode 100644 index 0000000..eb594fa --- /dev/null +++ b/drivers/base/power/wakeup.c @@ -0,0 +1,247 @@ +/* + * drivers/base/power/wakeup.c - System wakeup events framework + * + * Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. + * + * This file is released under the GPLv2. + */ + +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/capability.h> +#include <linux/suspend.h> +#include <linux/pm.h> + +/* + * If set, the suspend/hibernate code will abort transitions to a sleep state + * if wakeup events are registered during or immediately before the transition. + */ +bool events_check_enabled; + +/* The counter of registered wakeup events. */ +static unsigned long event_count; +/* A preserved old value of event_count. */ +static unsigned long saved_event_count; +/* The counter of wakeup events being processed. */ +static unsigned long events_in_progress; + +static DEFINE_SPINLOCK(events_lock); + +static void pm_wakeup_timer_fn(unsigned long data); + +static DEFINE_TIMER(events_timer, pm_wakeup_timer_fn, 0, 0); +static unsigned long events_timer_expires; + +/* + * The functions below use the observation that each wakeup event starts a + * period in which the system should not be suspended. The moment this period + * will end depends on how the wakeup event is going to be processed after being + * detected and all of the possible cases can be divided into two distinct + * groups. + * + * First, a wakeup event may be detected by the same functional unit that will + * carry out the entire processing of it and possibly will pass it to user space + * for further processing. In that case the functional unit that has detected + * the event may later "close" the "no suspend" period associated with it + * directly as soon as it has been dealt with. The pair of pm_stay_awake() and + * pm_relax(), balanced with each other, is supposed to be used in such + * situations. + * + * Second, a wakeup event may be detected by one functional unit and processed + * by another one. In that case the unit that has detected it cannot really + * "close" the "no suspend" period associated with it, unless it knows in + * advance what's going to happen to the event during processing. This + * knowledge, however, may not be available to it, so it can simply specify time + * to wait before the system can be suspended and pass it as the second + * argument of pm_wakeup_event(). + */ + +/** + * pm_stay_awake - Notify the PM core that a wakeup event is being processed. + * @dev: Device the wakeup event is related to. + * + * Notify the PM core of a wakeup event (signaled by @dev) by incrementing the + * counter of wakeup events being processed. If @dev is not NULL, the counter + * of wakeup events related to @dev is incremented too. + * + * Call this function after detecting of a wakeup event if pm_relax() is going + * to be called directly after processing the event (and possibly passing it to + * user space for further processing). + * + * It is safe to call this function from interrupt context. + */ +void pm_stay_awake(struct device *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&events_lock, flags); + if (dev) + dev->power.wakeup_count++; + + events_in_progress++; + spin_unlock_irqrestore(&events_lock, flags); +} + +/** + * pm_relax - Notify the PM core that processing of a wakeup event has ended. + * + * Notify the PM core that a wakeup event has been processed by decrementing + * the counter of wakeup events being processed and incrementing the counter + * of registered wakeup events. + * + * Call this function for wakeup events whose processing started with calling + * pm_stay_awake(). + * + * It is safe to call it from interrupt context. + */ +void pm_relax(void) +{ + unsigned long flags; + + spin_lock_irqsave(&events_lock, flags); + if (events_in_progress) { + events_in_progress--; + event_count++; + } + spin_unlock_irqrestore(&events_lock, flags); +} + +/** + * pm_wakeup_timer_fn - Delayed finalization of a wakeup event. + * + * Decrease the counter of wakeup events being processed after it was increased + * by pm_wakeup_event(). + */ +static void pm_wakeup_timer_fn(unsigned long data) +{ + unsigned long flags; + + spin_lock_irqsave(&events_lock, flags); + if (events_timer_expires + && time_before_eq(events_timer_expires, jiffies)) { + events_in_progress--; + events_timer_expires = 0; + } + spin_unlock_irqrestore(&events_lock, flags); +} + +/** + * pm_wakeup_event - Notify the PM core of a wakeup event. + * @dev: Device the wakeup event is related to. + * @msec: Anticipated event processing time (in milliseconds). + * + * Notify the PM core of a wakeup event (signaled by @dev) that will take + * approximately @msec milliseconds to be processed by the kernel. Increment + * the counter of registered wakeup events and (if @msec is nonzero) set up + * the wakeup events timer to execute pm_wakeup_timer_fn() in future (if the + * timer has not been set up already, increment the counter of wakeup events + * being processed). If @dev is not NULL, the counter of wakeup events related + * to @dev is incremented too. + * + * It is safe to call this function from interrupt context. + */ +void pm_wakeup_event(struct device *dev, unsigned int msec) +{ + unsigned long flags; + + spin_lock_irqsave(&events_lock, flags); + event_count++; + if (dev) + dev->power.wakeup_count++; + + if (msec) { + unsigned long expires; + + expires = jiffies + msecs_to_jiffies(msec); + if (!expires) + expires = 1; + + if (!events_timer_expires + || time_after(expires, events_timer_expires)) { + if (!events_timer_expires) + events_in_progress++; + + mod_timer(&events_timer, expires); + events_timer_expires = expires; + } + } + spin_unlock_irqrestore(&events_lock, flags); +} + +/** + * pm_check_wakeup_events - Check for new wakeup events. + * + * Compare the current number of registered wakeup events with its preserved + * value from the past to check if new wakeup events have been registered since + * the old value was stored. Check if the current number of wakeup events being + * processed is zero. + */ +bool pm_check_wakeup_events(void) +{ + unsigned long flags; + bool ret = true; + + spin_lock_irqsave(&events_lock, flags); + if (events_check_enabled) { + ret = (event_count == saved_event_count) && !events_in_progress; + events_check_enabled = ret; + } + spin_unlock_irqrestore(&events_lock, flags); + return ret; +} + +/** + * pm_get_wakeup_count - Read the number of registered wakeup events. + * @count: Address to store the value at. + * + * Store the number of registered wakeup events at the address in @count. Block + * if the current number of wakeup events being processed is nonzero. + * + * Return false if the wait for the number of wakeup events being processed to + * drop down to zero has been interrupted by a signal (and the current number + * of wakeup events being processed is still nonzero). Otherwise return true. + */ +bool pm_get_wakeup_count(unsigned long *count) +{ + bool ret; + + spin_lock_irq(&events_lock); + if (capable(CAP_SYS_ADMIN)) + events_check_enabled = false; + + while (events_in_progress && !signal_pending(current)) { + spin_unlock_irq(&events_lock); + + schedule_timeout_interruptible(msecs_to_jiffies(100)); + + spin_lock_irq(&events_lock); + } + *count = event_count; + ret = !events_in_progress; + spin_unlock_irq(&events_lock); + return ret; +} + +/** + * pm_save_wakeup_count - Save the current number of registered wakeup events. + * @count: Value to compare with the current number of registered wakeup events. + * + * If @count is equal to the current number of registered wakeup events and the + * current number of wakeup events being processed is zero, store @count as the + * old number of registered wakeup events to be used by pm_check_wakeup_events() + * and return true. Otherwise return false. + */ +bool pm_save_wakeup_count(unsigned long count) +{ + bool ret = false; + + spin_lock_irq(&events_lock); + if (count == event_count && !events_in_progress) { + saved_event_count = count; + events_check_enabled = true; + ret = true; + } + spin_unlock_irq(&events_lock); + return ret; +} diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 8980fee..9354dc1 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -17,7 +17,6 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/slab.h> #include <linux/string.h> #include <linux/pm.h> #include <linux/device.h> diff --git a/drivers/base/topology.c b/drivers/base/topology.c index bf6b132..9fc630c 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -162,7 +162,7 @@ static int __cpuinit topology_cpu_callback(struct notifier_block *nfb, topology_remove_dev(cpu); break; } - return rc ? NOTIFY_BAD : NOTIFY_OK; + return notifier_from_errno(rc); } static int __cpuinit topology_sysfs_init(void) |