summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/Kconfig2
-rw-r--r--drivers/base/bus.c54
-rw-r--r--drivers/base/core.c30
-rw-r--r--drivers/base/dd.c3
-rw-r--r--drivers/base/firmware_class.c24
-rw-r--r--drivers/base/memory.c4
-rw-r--r--drivers/base/node.c69
-rw-r--r--drivers/base/platform.c80
-rw-r--r--drivers/base/power/main.c9
9 files changed, 206 insertions, 69 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 6318f6b5736..d8e8c49c0cb 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -54,7 +54,7 @@ config FIRMWARE_IN_KERNEL
such firmware, and do not wish to use an initrd.
This single option controls the inclusion of firmware for
- every driver which usees request_firmare() and ships its
+ every driver which uses request_firmare() and ships its
firmware in the kernel source tree, to avoid a proliferation
of 'Include firmware for xxx device' options.
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index ef522ae5548..5aee1c0169e 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -333,9 +333,7 @@ static int match_name(struct device *dev, void *data)
{
const char *name = data;
- if (strcmp(name, dev->bus_id) == 0)
- return 1;
- return 0;
+ return sysfs_streq(name, dev->bus_id);
}
/**
@@ -982,6 +980,56 @@ struct klist *bus_get_device_klist(struct bus_type *bus)
}
EXPORT_SYMBOL_GPL(bus_get_device_klist);
+/*
+ * Yes, this forcably breaks the klist abstraction temporarily. It
+ * just wants to sort the klist, not change reference counts and
+ * take/drop locks rapidly in the process. It does all this while
+ * holding the lock for the list, so objects can't otherwise be
+ * added/removed while we're swizzling.
+ */
+static void device_insertion_sort_klist(struct device *a, struct list_head *list,
+ int (*compare)(const struct device *a,
+ const struct device *b))
+{
+ struct list_head *pos;
+ struct klist_node *n;
+ struct device *b;
+
+ list_for_each(pos, list) {
+ n = container_of(pos, struct klist_node, n_node);
+ b = container_of(n, struct device, knode_bus);
+ if (compare(a, b) <= 0) {
+ list_move_tail(&a->knode_bus.n_node,
+ &b->knode_bus.n_node);
+ return;
+ }
+ }
+ list_move_tail(&a->knode_bus.n_node, list);
+}
+
+void bus_sort_breadthfirst(struct bus_type *bus,
+ int (*compare)(const struct device *a,
+ const struct device *b))
+{
+ LIST_HEAD(sorted_devices);
+ struct list_head *pos, *tmp;
+ struct klist_node *n;
+ struct device *dev;
+ struct klist *device_klist;
+
+ device_klist = bus_get_device_klist(bus);
+
+ spin_lock(&device_klist->k_lock);
+ list_for_each_safe(pos, tmp, &device_klist->k_list) {
+ n = container_of(pos, struct klist_node, n_node);
+ dev = container_of(n, struct device, knode_bus);
+ device_insertion_sort_klist(dev, &sorted_devices, compare);
+ }
+ list_splice(&sorted_devices, &device_klist->k_list);
+ spin_unlock(&device_klist->k_lock);
+}
+EXPORT_SYMBOL_GPL(bus_sort_breadthfirst);
+
int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index b98cb1416a2..8c2cc2648f5 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -523,11 +523,16 @@ static void klist_children_put(struct klist_node *n)
* device_initialize - init device structure.
* @dev: device.
*
- * This prepares the device for use by other layers,
- * including adding it to the device hierarchy.
+ * This prepares the device for use by other layers by initializing
+ * its fields.
* It is the first half of device_register(), if called by
- * that, though it can also be called separately, so one
- * may use @dev's fields (e.g. the refcount).
+ * that function, though it can also be called separately, so one
+ * may use @dev's fields. In particular, get_device()/put_device()
+ * may be used for reference counting of @dev after calling this
+ * function.
+ *
+ * NOTE: Use put_device() to give up your reference instead of freeing
+ * @dev directly once you have called this function.
*/
void device_initialize(struct device *dev)
{
@@ -835,9 +840,13 @@ static void device_remove_sys_dev_entry(struct device *dev)
* This is part 2 of device_register(), though may be called
* separately _iff_ device_initialize() has been called separately.
*
- * This adds it to the kobject hierarchy via kobject_add(), adds it
+ * This adds @dev to the kobject hierarchy via kobject_add(), adds it
* to the global and sibling lists for the device, then
* adds it to the other relevant subsystems of the driver model.
+ *
+ * NOTE: _Never_ directly free @dev after calling this function, even
+ * if it returned an error! Always use put_device() to give up your
+ * reference instead.
*/
int device_add(struct device *dev)
{
@@ -965,6 +974,10 @@ done:
* I.e. you should only call the two helpers separately if
* have a clearly defined need to use and refcount the device
* before it is added to the hierarchy.
+ *
+ * NOTE: _Never_ directly free @dev after calling this function, even
+ * if it returned an error! Always use put_device() to give up the
+ * reference initialized in this function instead.
*/
int device_register(struct device *dev)
{
@@ -1243,7 +1256,7 @@ struct device *device_create_vargs(struct class *class, struct device *parent,
return dev;
error:
- kfree(dev);
+ put_device(dev);
return ERR_PTR(retval);
}
EXPORT_SYMBOL_GPL(device_create_vargs);
@@ -1314,6 +1327,11 @@ EXPORT_SYMBOL_GPL(device_destroy);
* device_rename - renames a device
* @dev: the pointer to the struct device to be renamed
* @new_name: the new name of the device
+ *
+ * It is the responsibility of the caller to provide mutual
+ * exclusion between two different calls of device_rename
+ * on the same device to ensure that new_name is valid and
+ * won't conflict with other devices.
*/
int device_rename(struct device *dev, char *new_name)
{
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 3ac443b2ac0..20febc00a52 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -257,6 +257,9 @@ static int __driver_attach(struct device *dev, void *data)
* is an error.
*/
+ if (drv->bus->match && !drv->bus->match(dev, drv))
+ return 0;
+
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index c9c92b00fd5..b7e571031ec 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -164,8 +164,7 @@ static ssize_t firmware_loading_store(struct device *dev,
}
/* fallthrough */
default:
- printk(KERN_ERR "%s: unexpected value (%d)\n", __func__,
- loading);
+ dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
/* fallthrough */
case -1:
fw_load_abort(fw_priv);
@@ -309,7 +308,7 @@ static int fw_register_device(struct device **dev_p, const char *fw_name,
*dev_p = NULL;
if (!fw_priv || !f_dev) {
- printk(KERN_ERR "%s: kmalloc failed\n", __func__);
+ dev_err(device, "%s: kmalloc failed\n", __func__);
retval = -ENOMEM;
goto error_kfree;
}
@@ -329,8 +328,7 @@ static int fw_register_device(struct device **dev_p, const char *fw_name,
f_dev->uevent_suppress = 1;
retval = device_register(f_dev);
if (retval) {
- printk(KERN_ERR "%s: device_register failed\n",
- __func__);
+ dev_err(device, "%s: device_register failed\n", __func__);
goto error_kfree;
}
*dev_p = f_dev;
@@ -363,15 +361,13 @@ static int fw_setup_device(struct firmware *fw, struct device **dev_p,
fw_priv->fw = fw;
retval = sysfs_create_bin_file(&f_dev->kobj, &fw_priv->attr_data);
if (retval) {
- printk(KERN_ERR "%s: sysfs_create_bin_file failed\n",
- __func__);
+ dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__);
goto error_unreg;
}
retval = device_create_file(f_dev, &dev_attr_loading);
if (retval) {
- printk(KERN_ERR "%s: device_create_file failed\n",
- __func__);
+ dev_err(device, "%s: device_create_file failed\n", __func__);
goto error_unreg;
}
@@ -401,8 +397,8 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
if (!firmware) {
- printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
- __func__);
+ dev_err(device, "%s: kmalloc(struct firmware) failed\n",
+ __func__);
retval = -ENOMEM;
goto out;
}
@@ -411,15 +407,15 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
builtin++) {
if (strcmp(name, builtin->name))
continue;
- printk(KERN_INFO "firmware: using built-in firmware %s\n",
- name);
+ dev_info(device, "firmware: using built-in firmware %s\n",
+ name);
firmware->size = builtin->size;
firmware->data = builtin->data;
return 0;
}
if (uevent)
- printk(KERN_INFO "firmware: requesting %s\n", name);
+ dev_info(device, "firmware: requesting %s\n", name);
retval = fw_setup_device(firmware, &f_dev, name, device, uevent);
if (retval)
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index af0d175c025..5260e9e0df4 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -21,6 +21,8 @@
#include <linux/memory_hotplug.h>
#include <linux/mm.h>
#include <linux/mutex.h>
+#include <linux/stat.h>
+
#include <asm/atomic.h>
#include <asm/uaccess.h>
@@ -325,7 +327,7 @@ memory_probe_store(struct class *class, const char *buf, size_t count)
return count;
}
-static CLASS_ATTR(probe, 0700, NULL, memory_probe_store);
+static CLASS_ATTR(probe, S_IWUSR, NULL, memory_probe_store);
static int memory_probe_init(void)
{
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 5116b78c632..f5207090885 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -13,6 +13,7 @@
#include <linux/nodemask.h>
#include <linux/cpu.h>
#include <linux/device.h>
+#include <linux/swap.h>
static struct sysdev_class node_class = {
.name = "node",
@@ -61,34 +62,52 @@ static ssize_t node_read_meminfo(struct sys_device * dev,
si_meminfo_node(&i, nid);
n = sprintf(buf, "\n"
- "Node %d MemTotal: %8lu kB\n"
- "Node %d MemFree: %8lu kB\n"
- "Node %d MemUsed: %8lu kB\n"
- "Node %d Active: %8lu kB\n"
- "Node %d Inactive: %8lu kB\n"
+ "Node %d MemTotal: %8lu kB\n"
+ "Node %d MemFree: %8lu kB\n"
+ "Node %d MemUsed: %8lu kB\n"
+ "Node %d Active: %8lu kB\n"
+ "Node %d Inactive: %8lu kB\n"
+ "Node %d Active(anon): %8lu kB\n"
+ "Node %d Inactive(anon): %8lu kB\n"
+ "Node %d Active(file): %8lu kB\n"
+ "Node %d Inactive(file): %8lu kB\n"
+#ifdef CONFIG_UNEVICTABLE_LRU
+ "Node %d Unevictable: %8lu kB\n"
+ "Node %d Mlocked: %8lu kB\n"
+#endif
#ifdef CONFIG_HIGHMEM
- "Node %d HighTotal: %8lu kB\n"
- "Node %d HighFree: %8lu kB\n"
- "Node %d LowTotal: %8lu kB\n"
- "Node %d LowFree: %8lu kB\n"
+ "Node %d HighTotal: %8lu kB\n"
+ "Node %d HighFree: %8lu kB\n"
+ "Node %d LowTotal: %8lu kB\n"
+ "Node %d LowFree: %8lu kB\n"
#endif
- "Node %d Dirty: %8lu kB\n"
- "Node %d Writeback: %8lu kB\n"
- "Node %d FilePages: %8lu kB\n"
- "Node %d Mapped: %8lu kB\n"
- "Node %d AnonPages: %8lu kB\n"
- "Node %d PageTables: %8lu kB\n"
- "Node %d NFS_Unstable: %8lu kB\n"
- "Node %d Bounce: %8lu kB\n"
- "Node %d WritebackTmp: %8lu kB\n"
- "Node %d Slab: %8lu kB\n"
- "Node %d SReclaimable: %8lu kB\n"
- "Node %d SUnreclaim: %8lu kB\n",
+ "Node %d Dirty: %8lu kB\n"
+ "Node %d Writeback: %8lu kB\n"
+ "Node %d FilePages: %8lu kB\n"
+ "Node %d Mapped: %8lu kB\n"
+ "Node %d AnonPages: %8lu kB\n"
+ "Node %d PageTables: %8lu kB\n"
+ "Node %d NFS_Unstable: %8lu kB\n"
+ "Node %d Bounce: %8lu kB\n"
+ "Node %d WritebackTmp: %8lu kB\n"
+ "Node %d Slab: %8lu kB\n"
+ "Node %d SReclaimable: %8lu kB\n"
+ "Node %d SUnreclaim: %8lu kB\n",
nid, K(i.totalram),
nid, K(i.freeram),
nid, K(i.totalram - i.freeram),
- nid, K(node_page_state(nid, NR_ACTIVE)),
- nid, K(node_page_state(nid, NR_INACTIVE)),
+ nid, K(node_page_state(nid, NR_ACTIVE_ANON) +
+ node_page_state(nid, NR_ACTIVE_FILE)),
+ nid, K(node_page_state(nid, NR_INACTIVE_ANON) +
+ node_page_state(nid, NR_INACTIVE_FILE)),
+ nid, K(node_page_state(nid, NR_ACTIVE_ANON)),
+ nid, K(node_page_state(nid, NR_INACTIVE_ANON)),
+ nid, K(node_page_state(nid, NR_ACTIVE_FILE)),
+ nid, K(node_page_state(nid, NR_INACTIVE_FILE)),
+#ifdef CONFIG_UNEVICTABLE_LRU
+ nid, K(node_page_state(nid, NR_UNEVICTABLE)),
+ nid, K(node_page_state(nid, NR_MLOCK)),
+#endif
#ifdef CONFIG_HIGHMEM
nid, K(i.totalhigh),
nid, K(i.freehigh),
@@ -173,6 +192,8 @@ int register_node(struct node *node, int num, struct node *parent)
sysdev_create_file(&node->sysdev, &attr_meminfo);
sysdev_create_file(&node->sysdev, &attr_numastat);
sysdev_create_file(&node->sysdev, &attr_distance);
+
+ scan_unevictable_register_node(node);
}
return error;
}
@@ -192,6 +213,8 @@ void unregister_node(struct node *node)
sysdev_remove_file(&node->sysdev, &attr_numastat);
sysdev_remove_file(&node->sysdev, &attr_distance);
+ scan_unevictable_unregister_node(node);
+
sysdev_unregister(&node->sysdev);
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 3f940393d6c..dfcbfe50486 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -42,10 +42,8 @@ struct resource *platform_get_resource(struct platform_device *dev,
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
- if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
- IORESOURCE_IRQ|IORESOURCE_DMA)) == type)
- if (num-- == 0)
- return r;
+ if (type == resource_type(r) && num-- == 0)
+ return r;
}
return NULL;
}
@@ -78,10 +76,8 @@ struct resource *platform_get_resource_byname(struct platform_device *dev,
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
- if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
- IORESOURCE_IRQ|IORESOURCE_DMA)) == type)
- if (!strcmp(r->name, name))
- return r;
+ if (type == resource_type(r) && !strcmp(r->name, name))
+ return r;
}
return NULL;
}
@@ -259,9 +255,9 @@ int platform_device_add(struct platform_device *pdev)
p = r->parent;
if (!p) {
- if (r->flags & IORESOURCE_MEM)
+ if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
- else if (r->flags & IORESOURCE_IO)
+ else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
@@ -282,9 +278,14 @@ int platform_device_add(struct platform_device *pdev)
return ret;
failed:
- while (--i >= 0)
- if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
- release_resource(&pdev->resource[i]);
+ while (--i >= 0) {
+ struct resource *r = &pdev->resource[i];
+ unsigned long type = resource_type(r);
+
+ if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
+ release_resource(r);
+ }
+
return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);
@@ -306,7 +307,9 @@ void platform_device_del(struct platform_device *pdev)
for (i = 0; i < pdev->num_resources; i++) {
struct resource *r = &pdev->resource[i];
- if (r->flags & (IORESOURCE_MEM|IORESOURCE_IO))
+ unsigned long type = resource_type(r);
+
+ if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
release_resource(r);
}
}
@@ -391,6 +394,53 @@ error:
}
EXPORT_SYMBOL_GPL(platform_device_register_simple);
+/**
+ * platform_device_register_data
+ * @parent: parent device for the device we're adding
+ * @name: base name of the device we're adding
+ * @id: instance id
+ * @data: platform specific data for this platform device
+ * @size: size of platform specific data
+ *
+ * This function creates a simple platform device that requires minimal
+ * resource and memory management. Canned release function freeing memory
+ * allocated for the device allows drivers using such devices to be
+ * unloaded without waiting for the last reference to the device to be
+ * dropped.
+ */
+struct platform_device *platform_device_register_data(
+ struct device *parent,
+ const char *name, int id,
+ const void *data, size_t size)
+{
+ struct platform_device *pdev;
+ int retval;
+
+ pdev = platform_device_alloc(name, id);
+ if (!pdev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ pdev->dev.parent = parent;
+
+ if (size) {
+ retval = platform_device_add_data(pdev, data, size);
+ if (retval)
+ goto error;
+ }
+
+ retval = platform_device_add(pdev);
+ if (retval)
+ goto error;
+
+ return pdev;
+
+error:
+ platform_device_put(pdev);
+ return ERR_PTR(retval);
+}
+
static int platform_drv_probe(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
@@ -862,7 +912,7 @@ static int platform_pm_restore_noirq(struct device *dev)
#endif /* !CONFIG_HIBERNATION */
-struct pm_ext_ops platform_pm_ops = {
+static struct pm_ext_ops platform_pm_ops = {
.base = {
.prepare = platform_pm_prepare,
.complete = platform_pm_complete,
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 273a944d404..692c20ba514 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -83,7 +83,7 @@ void device_pm_add(struct device *dev)
* transition is in progress in order to avoid leaving them
* unhandled down the road
*/
- WARN_ON(true);
+ dev_WARN(dev, "Parentless device registered during a PM transaction\n");
}
list_add_tail(&dev->power.entry, &dpm_list);
@@ -778,10 +778,7 @@ EXPORT_SYMBOL_GPL(device_suspend);
void __suspend_report_result(const char *function, void *fn, int ret)
{
- if (ret) {
- printk(KERN_ERR "%s(): ", function);
- print_fn_descriptor_symbol("%s returns ", fn);
- printk("%d\n", ret);
- }
+ if (ret)
+ printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret);
}
EXPORT_SYMBOL_GPL(__suspend_report_result);