diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/Kconfig | 51 | ||||
-rw-r--r-- | drivers/base/bus.c | 26 | ||||
-rw-r--r-- | drivers/base/class.c | 16 | ||||
-rw-r--r-- | drivers/base/core.c | 46 | ||||
-rw-r--r-- | drivers/base/cpu.c | 105 | ||||
-rw-r--r-- | drivers/base/dd.c | 38 | ||||
-rw-r--r-- | drivers/base/devtmpfs.c | 13 | ||||
-rw-r--r-- | drivers/base/firmware_class.c | 11 | ||||
-rw-r--r-- | drivers/base/memory.c | 35 | ||||
-rw-r--r-- | drivers/base/node.c | 81 | ||||
-rw-r--r-- | drivers/base/platform.c | 76 | ||||
-rw-r--r-- | drivers/base/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/base/power/generic_ops.c | 233 | ||||
-rw-r--r-- | drivers/base/power/main.c | 20 | ||||
-rw-r--r-- | drivers/base/sys.c | 17 |
15 files changed, 543 insertions, 226 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index ee377270beb9..fd52c48ee762 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -3,35 +3,50 @@ menu "Generic Driver Options" config UEVENT_HELPER_PATH string "path to uevent helper" depends on HOTPLUG - default "/sbin/hotplug" + default "" help Path to uevent helper program forked by the kernel for every uevent. + Before the switch to the netlink-based uevent source, this was + used to hook hotplug scripts into kernel device events. It + usually pointed to a shell script at /sbin/hotplug. + This should not be used today, because usual systems create + many events at bootup or device discovery in a very short time + frame. One forked process per event can create so many processes + that it creates a high system load, or on smaller systems + it is known to create out-of-memory situations during bootup. config DEVTMPFS - bool "Create a kernel maintained /dev tmpfs (EXPERIMENTAL)" + bool "Maintain a devtmpfs filesystem to mount at /dev" depends on HOTPLUG && SHMEM && TMPFS help - This creates a tmpfs filesystem, and mounts it at bootup - and mounts it at /dev. The kernel driver core creates device - nodes for all registered devices in that filesystem. All device - nodes are owned by root and have the default mode of 0600. - Userspace can add and delete the nodes as needed. This is - intended to simplify bootup, and make it possible to delay - the initial coldplug at bootup done by udev in userspace. - It should also provide a simpler way for rescue systems - to bring up a kernel with dynamic major/minor numbers. - Meaningful symlinks, permissions and device ownership must - still be handled by userspace. - If unsure, say N here. + This creates a tmpfs 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. + Userspace can modify the filesystem content as needed, add + symlinks, and apply needed permissions. + It provides a fully functional /dev directory, where usually + udev runs on top, managing permissions and adding meaningful + symlinks. + In very limited environments, it may provide a sufficient + functional /dev without any further help. It also allows simple + rescue systems, and reliably handles dynamic major/minor numbers. config DEVTMPFS_MOUNT - bool "Automount devtmpfs at /dev" + bool "Automount devtmpfs at /dev, after the kernel mounted the rootfs" depends on DEVTMPFS help - This will mount devtmpfs at /dev if the kernel mounts the root - filesystem. It will not affect initramfs based mounting. - If unsure, say N here. + This will instruct the kernel to automatically mount the + devtmpfs filesystem at /dev, directly after the kernel has + mounted the root filesystem. The behavior can be overridden + with the commandline parameter: devtmpfs.mount=0|1. + This option does not affect initramfs based booting, here + the devtmpfs filesystem always needs to be mounted manually + after the roots is mounted. + With this option enabled, it allows to bring up a system in + rescue mode with init=/bin/sh, even when the /dev directory + on the rootfs is completely empty. config STANDALONE bool "Select only drivers that don't need compile-time external firmware" if EXPERIMENTAL diff --git a/drivers/base/bus.c b/drivers/base/bus.c index c0c5a43d9fb3..71f6af5c8b0b 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -70,7 +70,7 @@ static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr, return ret; } -static struct sysfs_ops driver_sysfs_ops = { +static const struct sysfs_ops driver_sysfs_ops = { .show = drv_attr_show, .store = drv_attr_store, }; @@ -115,7 +115,7 @@ static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr, return ret; } -static struct sysfs_ops bus_sysfs_ops = { +static const struct sysfs_ops bus_sysfs_ops = { .show = bus_attr_show, .store = bus_attr_store, }; @@ -154,7 +154,7 @@ static int bus_uevent_filter(struct kset *kset, struct kobject *kobj) return 0; } -static struct kset_uevent_ops bus_uevent_ops = { +static const struct kset_uevent_ops bus_uevent_ops = { .filter = bus_uevent_filter, }; @@ -173,10 +173,10 @@ static ssize_t driver_unbind(struct device_driver *drv, dev = bus_find_device_by_name(bus, NULL, buf); if (dev && dev->driver == drv) { if (dev->parent) /* Needed for USB */ - down(&dev->parent->sem); + device_lock(dev->parent); device_release_driver(dev); if (dev->parent) - up(&dev->parent->sem); + device_unlock(dev->parent); err = count; } put_device(dev); @@ -200,12 +200,12 @@ static ssize_t driver_bind(struct device_driver *drv, dev = bus_find_device_by_name(bus, NULL, buf); if (dev && dev->driver == NULL && driver_match_device(drv, dev)) { if (dev->parent) /* Needed for USB */ - down(&dev->parent->sem); - down(&dev->sem); + device_lock(dev->parent); + device_lock(dev); err = driver_probe_device(drv, dev); - up(&dev->sem); + device_unlock(dev); if (dev->parent) - up(&dev->parent->sem); + device_unlock(dev->parent); if (err > 0) { /* success */ @@ -744,10 +744,10 @@ static int __must_check bus_rescan_devices_helper(struct device *dev, if (!dev->driver) { if (dev->parent) /* Needed for USB */ - down(&dev->parent->sem); + device_lock(dev->parent); ret = device_attach(dev); if (dev->parent) - up(&dev->parent->sem); + device_unlock(dev->parent); } return ret < 0 ? ret : 0; } @@ -779,10 +779,10 @@ int device_reprobe(struct device *dev) { if (dev->driver) { if (dev->parent) /* Needed for USB */ - down(&dev->parent->sem); + device_lock(dev->parent); device_release_driver(dev); if (dev->parent) - up(&dev->parent->sem); + device_unlock(dev->parent); } return bus_rescan_devices_helper(dev, NULL); } diff --git a/drivers/base/class.c b/drivers/base/class.c index 6e2c3b064f53..0147f476b8a9 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -31,7 +31,7 @@ static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr, ssize_t ret = -EIO; if (class_attr->show) - ret = class_attr->show(cp->class, buf); + ret = class_attr->show(cp->class, class_attr, buf); return ret; } @@ -43,7 +43,7 @@ static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr, ssize_t ret = -EIO; if (class_attr->store) - ret = class_attr->store(cp->class, buf, count); + ret = class_attr->store(cp->class, class_attr, buf, count); return ret; } @@ -63,7 +63,7 @@ static void class_release(struct kobject *kobj) kfree(cp); } -static struct sysfs_ops class_sysfs_ops = { +static const struct sysfs_ops class_sysfs_ops = { .show = class_attr_show, .store = class_attr_store, }; @@ -490,6 +490,16 @@ void class_interface_unregister(struct class_interface *class_intf) class_put(parent); } +ssize_t show_class_attr_string(struct class *class, struct class_attribute *attr, + char *buf) +{ + struct class_attribute_string *cs; + cs = container_of(attr, struct class_attribute_string, attr); + return snprintf(buf, PAGE_SIZE, "%s\n", cs->str); +} + +EXPORT_SYMBOL_GPL(show_class_attr_string); + struct class_compat { struct kobject *kobj; }; diff --git a/drivers/base/core.c b/drivers/base/core.c index 282025770429..ef55df34ddd0 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -100,7 +100,7 @@ static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr, return ret; } -static struct sysfs_ops dev_sysfs_ops = { +static const struct sysfs_ops dev_sysfs_ops = { .show = dev_attr_show, .store = dev_attr_store, }; @@ -252,7 +252,7 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, return retval; } -static struct kset_uevent_ops device_uevent_ops = { +static const struct kset_uevent_ops device_uevent_ops = { .filter = dev_uevent_filter, .name = dev_uevent_name, .uevent = dev_uevent, @@ -306,15 +306,10 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, { enum kobject_action action; - if (kobject_action_type(buf, count, &action) == 0) { + if (kobject_action_type(buf, count, &action) == 0) kobject_uevent(&dev->kobj, action); - goto out; - } - - dev_err(dev, "uevent: unsupported action-string; this will " - "be ignored in a future kernel version\n"); - kobject_uevent(&dev->kobj, KOBJ_ADD); -out: + else + dev_err(dev, "uevent: unknown action-string\n"); return count; } @@ -607,6 +602,7 @@ static struct kobject *get_device_parent(struct device *dev, int retval; if (dev->class) { + static DEFINE_MUTEX(gdp_mutex); struct kobject *kobj = NULL; struct kobject *parent_kobj; struct kobject *k; @@ -623,6 +619,8 @@ static struct kobject *get_device_parent(struct device *dev, else parent_kobj = &parent->kobj; + mutex_lock(&gdp_mutex); + /* find our class-directory at the parent and reference it */ spin_lock(&dev->class->p->class_dirs.list_lock); list_for_each_entry(k, &dev->class->p->class_dirs.list, entry) @@ -631,20 +629,26 @@ static struct kobject *get_device_parent(struct device *dev, break; } spin_unlock(&dev->class->p->class_dirs.list_lock); - if (kobj) + if (kobj) { + mutex_unlock(&gdp_mutex); return kobj; + } /* or create a new class-directory at the parent device */ k = kobject_create(); - if (!k) + 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; } /* do not emit an uevent for this simple "glue" directory */ + mutex_unlock(&gdp_mutex); return k; } @@ -1574,22 +1578,16 @@ int device_rename(struct device *dev, char *new_name) if (old_class_name) { new_class_name = make_class_name(dev->class->name, &dev->kobj); if (new_class_name) { - error = sysfs_create_link_nowarn(&dev->parent->kobj, - &dev->kobj, - new_class_name); - if (error) - goto out; - sysfs_remove_link(&dev->parent->kobj, old_class_name); + error = sysfs_rename_link(&dev->parent->kobj, + &dev->kobj, + old_class_name, + new_class_name); } } #else if (dev->class) { - error = sysfs_create_link_nowarn(&dev->class->p->class_subsys.kobj, - &dev->kobj, dev_name(dev)); - if (error) - goto out; - sysfs_remove_link(&dev->class->p->class_subsys.kobj, - old_device_name); + error = sysfs_rename_link(&dev->class->p->class_subsys.kobj, + &dev->kobj, old_device_name, new_name); } #endif diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 958bd1540c30..7036e8e96ab8 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -13,8 +13,11 @@ #include "base.h" +static struct sysdev_class_attribute *cpu_sysdev_class_attrs[]; + struct sysdev_class cpu_sysdev_class = { .name = "cpu", + .attrs = cpu_sysdev_class_attrs, }; EXPORT_SYMBOL(cpu_sysdev_class); @@ -76,34 +79,24 @@ void unregister_cpu(struct cpu *cpu) } #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE -static ssize_t cpu_probe_store(struct class *class, const char *buf, +static ssize_t cpu_probe_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { return arch_cpu_probe(buf, count); } -static ssize_t cpu_release_store(struct class *class, const char *buf, +static ssize_t cpu_release_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { return arch_cpu_release(buf, count); } -static CLASS_ATTR(probe, S_IWUSR, NULL, cpu_probe_store); -static CLASS_ATTR(release, S_IWUSR, NULL, cpu_release_store); - -int __init cpu_probe_release_init(void) -{ - int rc; - - rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj, - &class_attr_probe.attr); - if (!rc) - rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj, - &class_attr_release.attr); - - return rc; -} -device_initcall(cpu_probe_release_init); +static SYSDEV_ATTR(probe, S_IWUSR, NULL, cpu_probe_store); +static SYSDEV_ATTR(release, S_IWUSR, NULL, cpu_release_store); #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ #else /* ... !CONFIG_HOTPLUG_CPU */ @@ -141,31 +134,39 @@ static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL); /* * Print cpu online, possible, present, and system maps */ -static ssize_t print_cpus_map(char *buf, const struct cpumask *map) + +struct cpu_attr { + struct sysdev_class_attribute attr; + const struct cpumask *const * const map; +}; + +static ssize_t show_cpus_attr(struct sysdev_class *class, + struct sysdev_class_attribute *attr, + char *buf) { - int n = cpulist_scnprintf(buf, PAGE_SIZE-2, map); + struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr); + int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *(ca->map)); buf[n++] = '\n'; buf[n] = '\0'; return n; } -#define print_cpus_func(type) \ -static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf) \ -{ \ - return print_cpus_map(buf, cpu_##type##_mask); \ -} \ -static struct sysdev_class_attribute attr_##type##_map = \ - _SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL) +#define _CPU_ATTR(name, map) \ + { _SYSDEV_CLASS_ATTR(name, 0444, show_cpus_attr, NULL), map } -print_cpus_func(online); -print_cpus_func(possible); -print_cpus_func(present); +/* Keep in sync with cpu_sysdev_class_attrs */ +static struct cpu_attr cpu_attrs[] = { + _CPU_ATTR(online, &cpu_online_mask), + _CPU_ATTR(possible, &cpu_possible_mask), + _CPU_ATTR(present, &cpu_present_mask), +}; /* * Print values for NR_CPUS and offlined cpus */ -static ssize_t print_cpus_kernel_max(struct sysdev_class *class, char *buf) +static ssize_t print_cpus_kernel_max(struct sysdev_class *class, + struct sysdev_class_attribute *attr, char *buf) { int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1); return n; @@ -175,7 +176,8 @@ static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL); /* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */ unsigned int total_cpus; -static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf) +static ssize_t print_cpus_offline(struct sysdev_class *class, + struct sysdev_class_attribute *attr, char *buf) { int n = 0, len = PAGE_SIZE-2; cpumask_var_t offline; @@ -204,29 +206,6 @@ static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf) } static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL); -static struct sysdev_class_attribute *cpu_state_attr[] = { - &attr_online_map, - &attr_possible_map, - &attr_present_map, - &attr_kernel_max, - &attr_offline, -}; - -static int cpu_states_init(void) -{ - int i; - int err = 0; - - for (i = 0; i < ARRAY_SIZE(cpu_state_attr); i++) { - int ret; - ret = sysdev_class_create_file(&cpu_sysdev_class, - cpu_state_attr[i]); - if (!err) - err = ret; - } - return err; -} - /* * register_cpu - Setup a sysfs device for a CPU. * @cpu - cpu->hotpluggable field set to 1 will generate a control file in @@ -272,9 +251,6 @@ int __init cpu_dev_init(void) int err; err = sysdev_class_register(&cpu_sysdev_class); - if (!err) - err = cpu_states_init(); - #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) if (!err) err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class); @@ -282,3 +258,16 @@ int __init cpu_dev_init(void) return err; } + +static struct sysdev_class_attribute *cpu_sysdev_class_attrs[] = { +#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE + &attr_probe, + &attr_release, +#endif + &cpu_attrs[0].attr, + &cpu_attrs[1].attr, + &cpu_attrs[2].attr, + &attr_kernel_max, + &attr_offline, + NULL +}; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index ee95c76bfd3d..c89291f8a16b 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -85,7 +85,7 @@ static void driver_sysfs_remove(struct device *dev) * for before calling this. (It is ok to call with no other effort * from a driver's probe() method.) * - * This function must be called with @dev->sem held. + * This function must be called with the device lock held. */ int device_bind_driver(struct device *dev) { @@ -190,8 +190,8 @@ EXPORT_SYMBOL_GPL(wait_for_device_probe); * This function returns -ENODEV if the device is not registered, * 1 if the device is bound successfully and 0 otherwise. * - * This function must be called with @dev->sem held. When called for a - * USB interface, @dev->parent->sem must be held as well. + * This function must be called with @dev lock held. When called for a + * USB interface, @dev->parent lock must be held as well. */ int driver_probe_device(struct device_driver *drv, struct device *dev) { @@ -233,13 +233,13 @@ static int __device_attach(struct device_driver *drv, void *data) * 0 if no matching driver was found; * -ENODEV if the device is not registered. * - * When called for a USB interface, @dev->parent->sem must be held. + * When called for a USB interface, @dev->parent lock must be held. */ int device_attach(struct device *dev) { int ret = 0; - down(&dev->sem); + device_lock(dev); if (dev->driver) { ret = device_bind_driver(dev); if (ret == 0) @@ -253,7 +253,7 @@ int device_attach(struct device *dev) ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); pm_runtime_put_sync(dev); } - up(&dev->sem); + device_unlock(dev); return ret; } EXPORT_SYMBOL_GPL(device_attach); @@ -276,13 +276,13 @@ static int __driver_attach(struct device *dev, void *data) return 0; if (dev->parent) /* Needed for USB */ - down(&dev->parent->sem); - down(&dev->sem); + device_lock(dev->parent); + device_lock(dev); if (!dev->driver) driver_probe_device(drv, dev); - up(&dev->sem); + device_unlock(dev); if (dev->parent) - up(&dev->parent->sem); + device_unlock(dev->parent); return 0; } @@ -303,8 +303,8 @@ int driver_attach(struct device_driver *drv) EXPORT_SYMBOL_GPL(driver_attach); /* - * __device_release_driver() must be called with @dev->sem held. - * When called for a USB interface, @dev->parent->sem must be held as well. + * __device_release_driver() must be called with @dev lock held. + * When called for a USB interface, @dev->parent lock must be held as well. */ static void __device_release_driver(struct device *dev) { @@ -343,7 +343,7 @@ static void __device_release_driver(struct device *dev) * @dev: device. * * Manually detach device from driver. - * When called for a USB interface, @dev->parent->sem must be held. + * When called for a USB interface, @dev->parent lock must be held. */ void device_release_driver(struct device *dev) { @@ -352,9 +352,9 @@ void device_release_driver(struct device *dev) * within their ->remove callback for the same device, they * will deadlock right here. */ - down(&dev->sem); + device_lock(dev); __device_release_driver(dev); - up(&dev->sem); + device_unlock(dev); } EXPORT_SYMBOL_GPL(device_release_driver); @@ -381,13 +381,13 @@ void driver_detach(struct device_driver *drv) spin_unlock(&drv->p->klist_devices.k_lock); if (dev->parent) /* Needed for USB */ - down(&dev->parent->sem); - down(&dev->sem); + device_lock(dev->parent); + device_lock(dev); if (dev->driver == drv) __device_release_driver(dev); - up(&dev->sem); + device_unlock(dev); if (dev->parent) - up(&dev->parent->sem); + device_unlock(dev->parent); put_device(dev); } } diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 42ae452b36b0..dac478c6e460 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -301,6 +301,19 @@ int devtmpfs_delete_node(struct device *dev) if (dentry->d_inode) { err = vfs_getattr(nd.path.mnt, dentry, &stat); if (!err && dev_mynode(dev, dentry->d_inode, &stat)) { + struct iattr newattrs; + /* + * before unlinking this node, reset permissions + * of possible references like hardlinks + */ + newattrs.ia_uid = 0; + newattrs.ia_gid = 0; + newattrs.ia_mode = stat.mode & ~0777; + newattrs.ia_valid = + ATTR_UID|ATTR_GID|ATTR_MODE; + mutex_lock(&dentry->d_inode->i_mutex); + notify_change(dentry, &newattrs); + mutex_unlock(&dentry->d_inode->i_mutex); err = vfs_unlink(nd.path.dentry->d_inode, dentry); if (!err || err == -ENOENT) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index a95024166b66..d0dc26ad5387 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -19,7 +19,6 @@ #include <linux/kthread.h> #include <linux/highmem.h> #include <linux/firmware.h> -#include "base.h" #define to_dev(obj) container_of(obj, struct device, kobj) @@ -69,7 +68,9 @@ fw_load_abort(struct firmware_priv *fw_priv) } static ssize_t -firmware_timeout_show(struct class *class, char *buf) +firmware_timeout_show(struct class *class, + struct class_attribute *attr, + char *buf) { return sprintf(buf, "%d\n", loading_timeout); } @@ -87,7 +88,9 @@ firmware_timeout_show(struct class *class, char *buf) * Note: zero means 'wait forever'. **/ static ssize_t -firmware_timeout_store(struct class *class, const char *buf, size_t count) +firmware_timeout_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) { loading_timeout = simple_strtol(buf, NULL, 10); if (loading_timeout < 0) @@ -610,7 +613,7 @@ request_firmware_work_func(void *arg) } /** - * request_firmware_nowait: asynchronous version of request_firmware + * request_firmware_nowait - asynchronous version of request_firmware * @module: module requesting the firmware * @uevent: sends uevent to copy the firmware image if this flag * is non-zero else the firmware copy must be done manually. diff --git a/drivers/base/memory.c b/drivers/base/memory.c index bd025059711f..db0848e54cc6 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -44,7 +44,7 @@ static int memory_uevent(struct kset *kset, struct kobject *obj, struct kobj_uev return retval; } -static struct kset_uevent_ops memory_uevent_ops = { +static const struct kset_uevent_ops memory_uevent_ops = { .name = memory_uevent_name, .uevent = memory_uevent, }; @@ -309,17 +309,18 @@ static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL); * Block size attribute stuff */ static ssize_t -print_block_size(struct class *class, char *buf) +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); } -static CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL); +static SYSDEV_CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL); static int block_size_init(void) { return sysfs_create_file(&memory_sysdev_class.kset.kobj, - &class_attr_block_size_bytes.attr); + &attr_block_size_bytes.attr); } /* @@ -330,7 +331,8 @@ static int block_size_init(void) */ #ifdef CONFIG_ARCH_MEMORY_PROBE static ssize_t -memory_probe_store(struct class *class, const char *buf, size_t count) +memory_probe_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t count) { u64 phys_addr; int nid; @@ -367,7 +369,9 @@ static inline int memory_probe_init(void) /* Soft offline a page */ static ssize_t -store_soft_offline_page(struct class *class, const char *buf, size_t count) +store_soft_offline_page(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) { int ret; u64 pfn; @@ -384,7 +388,9 @@ store_soft_offline_page(struct class *class, const char *buf, size_t count) /* Forcibly offline a page, including killing processes. */ static ssize_t -store_hard_offline_page(struct class *class, const char *buf, size_t count) +store_hard_offline_page(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) { int ret; u64 pfn; @@ -423,12 +429,16 @@ static inline int memory_fail_init(void) * differentiation between which *physical* devices each * section belongs to... */ +int __weak arch_get_memory_phys_device(unsigned long start_pfn) +{ + return 0; +} static int add_memory_block(int nid, struct mem_section *section, - unsigned long state, int phys_device, - enum mem_add_context context) + unsigned long state, enum mem_add_context context) { struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL); + unsigned long start_pfn; int ret = 0; if (!mem) @@ -437,7 +447,8 @@ static int add_memory_block(int nid, struct mem_section *section, mem->phys_index = __section_nr(section); mem->state = state; mutex_init(&mem->state_mutex); - mem->phys_device = phys_device; + start_pfn = section_nr_to_pfn(mem->phys_index); + mem->phys_device = arch_get_memory_phys_device(start_pfn); ret = register_memory(mem, section); if (!ret) @@ -509,7 +520,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, */ int register_new_memory(int nid, struct mem_section *section) { - return add_memory_block(nid, section, MEM_OFFLINE, 0, HOTPLUG); + return add_memory_block(nid, section, MEM_OFFLINE, HOTPLUG); } int unregister_memory_section(struct mem_section *section) @@ -542,7 +553,7 @@ int __init memory_dev_init(void) if (!present_section_nr(i)) continue; err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE, - 0, BOOT); + BOOT); if (!ret) ret = err; } diff --git a/drivers/base/node.c b/drivers/base/node.c index 70122791683d..ad43185ec15a 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -16,8 +16,11 @@ #include <linux/device.h> #include <linux/swap.h> +static struct sysdev_class_attribute *node_state_attrs[]; + static struct sysdev_class node_class = { .name = "node", + .attrs = node_state_attrs, }; @@ -544,76 +547,52 @@ static ssize_t print_nodes_state(enum node_states state, char *buf) return n; } -static ssize_t print_nodes_possible(struct sysdev_class *class, char *buf) -{ - return print_nodes_state(N_POSSIBLE, buf); -} - -static ssize_t print_nodes_online(struct sysdev_class *class, char *buf) -{ - return print_nodes_state(N_ONLINE, buf); -} - -static ssize_t print_nodes_has_normal_memory(struct sysdev_class *class, - char *buf) -{ - return print_nodes_state(N_NORMAL_MEMORY, buf); -} +struct node_attr { + struct sysdev_class_attribute attr; + enum node_states state; +}; -static ssize_t print_nodes_has_cpu(struct sysdev_class *class, char *buf) +static ssize_t show_node_state(struct sysdev_class *class, + struct sysdev_class_attribute *attr, char *buf) { - return print_nodes_state(N_CPU, buf); + struct node_attr *na = container_of(attr, struct node_attr, attr); + return print_nodes_state(na->state, buf); } -static SYSDEV_CLASS_ATTR(possible, 0444, print_nodes_possible, NULL); -static SYSDEV_CLASS_ATTR(online, 0444, print_nodes_online, NULL); -static SYSDEV_CLASS_ATTR(has_normal_memory, 0444, print_nodes_has_normal_memory, - NULL); -static SYSDEV_CLASS_ATTR(has_cpu, 0444, print_nodes_has_cpu, NULL); +#define _NODE_ATTR(name, state) \ + { _SYSDEV_CLASS_ATTR(name, 0444, show_node_state, NULL), state } +static struct node_attr node_state_attr[] = { + _NODE_ATTR(possible, N_POSSIBLE), + _NODE_ATTR(online, N_ONLINE), + _NODE_ATTR(has_normal_memory, N_NORMAL_MEMORY), + _NODE_ATTR(has_cpu, N_CPU), #ifdef CONFIG_HIGHMEM -static ssize_t print_nodes_has_high_memory(struct sysdev_class *class, - char *buf) -{ - return print_nodes_state(N_HIGH_MEMORY, buf); -} - -static SYSDEV_CLASS_ATTR(has_high_memory, 0444, print_nodes_has_high_memory, - NULL); + _NODE_ATTR(has_high_memory, N_HIGH_MEMORY), #endif +}; -struct sysdev_class_attribute *node_state_attr[] = { - &attr_possible, - &attr_online, - &attr_has_normal_memory, +static struct sysdev_class_attribute *node_state_attrs[] = { + &node_state_attr[0].attr, + &node_state_attr[1].attr, + &node_state_attr[2].attr, + &node_state_attr[3].attr, #ifdef CONFIG_HIGHMEM - &attr_has_high_memory, + &node_state_attr[4].attr, #endif - &attr_has_cpu, + NULL }; -static int node_states_init(void) -{ - int i; - int err = 0; - - for (i = 0; i < NR_NODE_STATES; i++) { - int ret; - ret = sysdev_class_create_file(&node_class, node_state_attr[i]); - if (!err) - err = ret; - } - return err; -} - #define NODE_CALLBACK_PRI 2 /* lower than SLAB */ static int __init register_node_type(void) { int ret; + BUILD_BUG_ON(ARRAY_SIZE(node_state_attr) != NR_NODE_STATES); + BUILD_BUG_ON(ARRAY_SIZE(node_state_attrs)-1 != NR_NODE_STATES); + ret = sysdev_class_register(&node_class); if (!ret) { - ret = node_states_init(); hotplug_memory_notifier(node_memory_callback, NODE_CALLBACK_PRI); } diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 58efaf2f1259..1ba9d617d241 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -128,7 +128,7 @@ struct platform_object { }; /** - * platform_device_put + * platform_device_put - destroy a platform device * @pdev: platform device to free * * Free all memory associated with a platform device. This function must @@ -152,7 +152,7 @@ static void platform_device_release(struct device *dev) } /** - * platform_device_alloc + * platform_device_alloc - create a platform device * @name: base name of the device we're adding * @id: instance id * @@ -177,7 +177,7 @@ struct platform_device *platform_device_alloc(const char *name, int id) EXPORT_SYMBOL_GPL(platform_device_alloc); /** - * platform_device_add_resources + * platform_device_add_resources - add resources to a platform device * @pdev: platform device allocated by platform_device_alloc to add resources to * @res: set of resources that needs to be allocated for the device * @num: number of resources @@ -202,7 +202,7 @@ int platform_device_add_resources(struct platform_device *pdev, EXPORT_SYMBOL_GPL(platform_device_add_resources); /** - * platform_device_add_data + * platform_device_add_data - add platform-specific data to a platform device * @pdev: platform device allocated by platform_device_alloc to add resources to * @data: platform specific data for this platform device * @size: size of platform specific data @@ -344,7 +344,7 @@ void platform_device_unregister(struct platform_device *pdev) EXPORT_SYMBOL_GPL(platform_device_unregister); /** - * platform_device_register_simple + * platform_device_register_simple - add a platform-level device and its resources * @name: base name of the device we're adding * @id: instance id * @res: set of resources that needs to be allocated for the device @@ -396,7 +396,7 @@ error: EXPORT_SYMBOL_GPL(platform_device_register_simple); /** - * platform_device_register_data + * platform_device_register_data - add a platform-level device with platform-specific data * @parent: parent device for the device we're adding * @name: base name of the device we're adding * @id: instance id @@ -473,7 +473,7 @@ static void platform_drv_shutdown(struct device *_dev) } /** - * platform_driver_register + * platform_driver_register - register a driver for platform-level devices * @drv: platform driver structure */ int platform_driver_register(struct platform_driver *drv) @@ -491,7 +491,7 @@ int platform_driver_register(struct platform_driver *drv) EXPORT_SYMBOL_GPL(platform_driver_register); /** - * platform_driver_unregister + * platform_driver_unregister - unregister a driver for platform-level devices * @drv: platform driver structure */ void platform_driver_unregister(struct platform_driver *drv) @@ -548,6 +548,64 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv, } EXPORT_SYMBOL_GPL(platform_driver_probe); +/** + * platform_create_bundle - register driver and create corresponding device + * @driver: platform driver structure + * @probe: the driver probe routine, probably from an __init section + * @res: set of resources that needs to be allocated for the device + * @n_res: number of resources + * @data: platform specific data for this platform device + * @size: size of platform specific data + * + * Use this in legacy-style modules that probe hardware directly and + * register a single platform device and corresponding platform driver. + */ +struct platform_device * __init_or_module platform_create_bundle( + struct platform_driver *driver, + int (*probe)(struct platform_device *), + struct resource *res, unsigned int n_res, + const void *data, size_t size) +{ + struct platform_device *pdev; + int error; + + pdev = platform_device_alloc(driver->driver.name, -1); + if (!pdev) { + error = -ENOMEM; + goto err_out; + } + + if (res) { + error = platform_device_add_resources(pdev, res, n_res); + if (error) + goto err_pdev_put; + } + + if (data) { + error = platform_device_add_data(pdev, data, size); + if (error) + goto err_pdev_put; + } + + error = platform_device_add(pdev); + if (error) + goto err_pdev_put; + + error = platform_driver_probe(driver, probe); + if (error) + goto err_pdev_del; + + return pdev; + +err_pdev_del: + platform_device_del(pdev); +err_pdev_put: + platform_device_put(pdev); +err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL_GPL(platform_create_bundle); + /* modalias support enables more hands-off userspace setup: * (a) environment variable lets new-style hotplug events work once system is * fully running: "modprobe $MODALIAS" @@ -578,7 +636,7 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) } static const struct platform_device_id *platform_match_id( - struct platform_device_id *id, + const struct platform_device_id *id, struct platform_device *pdev) { while (id->name[0]) { diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 3ce3519e8f30..89de75325cea 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_PM) += sysfs.o obj-$(CONFIG_PM_SLEEP) += main.o obj-$(CONFIG_PM_RUNTIME) += runtime.o +obj-$(CONFIG_PM_OPS) += generic_ops.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c new file mode 100644 index 000000000000..4b29d4981253 --- /dev/null +++ b/drivers/base/power/generic_ops.c @@ -0,0 +1,233 @@ +/* + * drivers/base/power/generic_ops.c - Generic PM callbacks for subsystems + * + * Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. + * + * This file is released under the GPLv2. + */ + +#include <linux/pm.h> +#include <linux/pm_runtime.h> + +#ifdef CONFIG_PM_RUNTIME +/** + * pm_generic_runtime_idle - Generic runtime idle callback for subsystems. + * @dev: Device to handle. + * + * If PM operations are defined for the @dev's driver and they include + * ->runtime_idle(), execute it and return its error code, if nonzero. + * Otherwise, execute pm_runtime_suspend() for the device and return 0. + */ +int pm_generic_runtime_idle(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm && pm->runtime_idle) { + int ret = pm->runtime_idle(dev); + if (ret) + return ret; + } + + pm_runtime_suspend(dev); + return 0; +} +EXPORT_SYMBOL_GPL(pm_generic_runtime_idle); + +/** + * pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems. + * @dev: Device to suspend. + * + * If PM operations are defined for the @dev's driver and they include + * ->runtime_suspend(), execute it and return its error code. Otherwise, + * return -EINVAL. + */ +int pm_generic_runtime_suspend(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int ret; + + ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend); + +/** + * pm_generic_runtime_resume - Generic runtime resume callback for subsystems. + * @dev: Device to resume. + * + * If PM operations are defined for the @dev's driver and they include + * ->runtime_resume(), execute it and return its error code. Otherwise, + * return -EINVAL. + */ +int pm_generic_runtime_resume(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int ret; + + ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(pm_generic_runtime_resume); +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_SLEEP +/** + * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback. + * @dev: Device to handle. + * @event: PM transition of the system under way. + * + * If the device has not been suspended at run time, execute the + * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and + * return its error code. Otherwise, return zero. + */ +static int __pm_generic_call(struct device *dev, int event) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int (*callback)(struct device *); + + if (!pm || pm_runtime_suspended(dev)) + return 0; + + switch (event) { + case PM_EVENT_SUSPEND: + callback = pm->suspend; + break; + case PM_EVENT_FREEZE: + callback = pm->freeze; + break; + case PM_EVENT_HIBERNATE: + callback = pm->poweroff; + break; + case PM_EVENT_THAW: + callback = pm->thaw; + break; + default: + callback = NULL; + break; + } + + return callback ? callback(dev) : 0; +} + +/** + * pm_generic_suspend - Generic suspend callback for subsystems. + * @dev: Device to suspend. + */ +int pm_generic_suspend(struct device *dev) +{ + return __pm_generic_call(dev, PM_EVENT_SUSPEND); +} +EXPORT_SYMBOL_GPL(pm_generic_suspend); + +/** + * pm_generic_freeze - Generic freeze callback for subsystems. + * @dev: Device to freeze. + */ +int pm_generic_freeze(struct device *dev) +{ + return __pm_generic_call(dev, PM_EVENT_FREEZE); +} +EXPORT_SYMBOL_GPL(pm_generic_freeze); + +/** + * pm_generic_poweroff - Generic poweroff callback for subsystems. + * @dev: Device to handle. + */ +int pm_generic_poweroff(struct device *dev) +{ + return __pm_generic_call(dev, PM_EVENT_HIBERNATE); +} +EXPORT_SYMBOL_GPL(pm_generic_poweroff); + +/** + * pm_generic_thaw - Generic thaw callback for subsystems. + * @dev: Device to thaw. + */ +int pm_generic_thaw(struct device *dev) +{ + return __pm_generic_call(dev, PM_EVENT_THAW); +} +EXPORT_SYMBOL_GPL(pm_generic_thaw); + +/** + * __pm_generic_resume - Generic resume/restore callback for subsystems. + * @dev: Device to handle. + * @event: PM transition of the system under way. + * + * Execute the resume/resotre callback provided by the @dev's driver, if + * defined. If it returns 0, change the device's runtime PM status to 'active'. + * Return the callback's error code. + */ +static int __pm_generic_resume(struct device *dev, int event) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int (*callback)(struct device *); + int ret; + + if (!pm) + return 0; + + switch (event) { + case PM_EVENT_RESUME: + callback = pm->resume; + break; + case PM_EVENT_RESTORE: + callback = pm->restore; + break; + default: + callback = NULL; + break; + } + + if (!callback) + return 0; + + ret = callback(dev); + if (!ret) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + + return ret; +} + +/** + * pm_generic_resume - Generic resume callback for subsystems. + * @dev: Device to resume. + */ +int pm_generic_resume(struct device *dev) +{ + return __pm_generic_resume(dev, PM_EVENT_RESUME); +} +EXPORT_SYMBOL_GPL(pm_generic_resume); + +/** + * pm_generic_restore - Generic restore callback for subsystems. + * @dev: Device to restore. + */ +int pm_generic_restore(struct device *dev) +{ + return __pm_generic_resume(dev, PM_EVENT_RESTORE); +} +EXPORT_SYMBOL_GPL(pm_generic_restore); +#endif /* CONFIG_PM_SLEEP */ + +struct dev_pm_ops generic_subsys_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = pm_generic_suspend, + .resume = pm_generic_resume, + .freeze = pm_generic_freeze, + .thaw = pm_generic_thaw, + .poweroff = pm_generic_poweroff, + .restore = pm_generic_restore, +#endif +#ifdef CONFIG_PM_RUNTIME + .runtime_suspend = pm_generic_runtime_suspend, + .runtime_resume = pm_generic_runtime_resume, + .runtime_idle = pm_generic_runtime_idle, +#endif +}; +EXPORT_SYMBOL_GPL(generic_subsys_pm_ops); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 0e26a6f6fd48..d477f4dc5e51 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -35,8 +35,8 @@ * because children are guaranteed to be discovered after parents, and * are inserted at the back of the list on discovery. * - * Since device_pm_add() may be called with a device semaphore held, - * we must never try to acquire a device semaphore while holding + * Since device_pm_add() may be called with a device lock held, + * we must never try to acquire a device lock while holding * dpm_list_mutex. */ @@ -508,7 +508,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) TRACE_RESUME(0); dpm_wait(dev->parent, async); - down(&dev->sem); + device_lock(dev); dev->power.status = DPM_RESUMING; @@ -543,7 +543,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) } } End: - up(&dev->sem); + device_unlock(dev); complete_all(&dev->power.completion); TRACE_RESUME(error); @@ -629,7 +629,7 @@ static void dpm_resume(pm_message_t state) */ static void device_complete(struct device *dev, pm_message_t state) { - down(&dev->sem); + device_lock(dev); if (dev->class && dev->class->pm && dev->class->pm->complete) { pm_dev_dbg(dev, state, "completing class "); @@ -646,7 +646,7 @@ static void device_complete(struct device *dev, pm_message_t state) dev->bus->pm->complete(dev); } - up(&dev->sem); + device_unlock(dev); } /** @@ -809,7 +809,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) int error = 0; dpm_wait_for_children(dev, async); - down(&dev->sem); + device_lock(dev); if (async_error) goto End; @@ -849,7 +849,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) dev->power.status = DPM_OFF; End: - up(&dev->sem); + device_unlock(dev); complete_all(&dev->power.completion); return error; @@ -938,7 +938,7 @@ static int device_prepare(struct device *dev, pm_message_t state) { int error = 0; - down(&dev->sem); + device_lock(dev); if (dev->bus && dev->bus->pm && dev->bus->pm->prepare) { pm_dev_dbg(dev, state, "preparing "); @@ -962,7 +962,7 @@ static int device_prepare(struct device *dev, pm_message_t state) suspend_report_result(dev->class->pm->prepare, error); } End: - up(&dev->sem); + device_unlock(dev); return error; } diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 0d903909af7e..8980feec5d14 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -54,7 +54,7 @@ sysdev_store(struct kobject *kobj, struct attribute *attr, return -EIO; } -static struct sysfs_ops sysfs_ops = { +static const struct sysfs_ops sysfs_ops = { .show = sysdev_show, .store = sysdev_store, }; @@ -89,7 +89,7 @@ static ssize_t sysdev_class_show(struct kobject *kobj, struct attribute *attr, struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr); if (class_attr->show) - return class_attr->show(class, buffer); + return class_attr->show(class, class_attr, buffer); return -EIO; } @@ -100,11 +100,11 @@ static ssize_t sysdev_class_store(struct kobject *kobj, struct attribute *attr, struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr); if (class_attr->store) - return class_attr->store(class, buffer, count); + return class_attr->store(class, class_attr, buffer, count); return -EIO; } -static struct sysfs_ops sysfs_class_ops = { +static const struct sysfs_ops sysfs_class_ops = { .show = sysdev_class_show, .store = sysdev_class_store, }; @@ -145,13 +145,20 @@ int sysdev_class_register(struct sysdev_class *cls) if (retval) return retval; - return kset_register(&cls->kset); + retval = kset_register(&cls->kset); + if (!retval && cls->attrs) + retval = sysfs_create_files(&cls->kset.kobj, + (const struct attribute **)cls->attrs); + return retval; } void sysdev_class_unregister(struct sysdev_class *cls) { pr_debug("Unregistering sysdev class '%s'\n", kobject_name(&cls->kset.kobj)); + if (cls->attrs) + sysfs_remove_files(&cls->kset.kobj, + (const struct attribute **)cls->attrs); kset_unregister(&cls->kset); } |