summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/Kconfig1
-rw-r--r--drivers/base/bus.c1
-rw-r--r--drivers/base/core.c80
-rw-r--r--drivers/base/dd.c22
-rw-r--r--drivers/base/devtmpfs.c9
-rw-r--r--drivers/base/dma-buf.c1
-rw-r--r--drivers/base/dma-coherent.c1
-rw-r--r--drivers/base/dma-contiguous.c2
-rw-r--r--drivers/base/dma-mapping.c49
-rw-r--r--drivers/base/driver.c6
-rw-r--r--drivers/base/firmware_class.c6
-rw-r--r--drivers/base/power/clock_ops.c3
-rw-r--r--drivers/base/power/common.c4
-rw-r--r--drivers/base/power/runtime.c13
14 files changed, 136 insertions, 62 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 9b21469482ae..08b4c5209384 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -196,6 +196,7 @@ config CMA
bool "Contiguous Memory Allocator (EXPERIMENTAL)"
depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK && EXPERIMENTAL
select MIGRATION
+ select MEMORY_ISOLATION
help
This enables the Contiguous Memory Allocator which allows drivers
to allocate big physically-contiguous blocks of memory for use with
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 2bcef657a60c..181ed2660b33 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -743,7 +743,6 @@ int bus_add_driver(struct device_driver *drv)
}
}
- kobject_uevent(&priv->kobj, KOBJ_ADD);
return 0;
out_unregister:
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 346be8b78b24..5e6e00bc1652 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -85,14 +85,13 @@ const char *dev_driver_string(const struct device *dev)
}
EXPORT_SYMBOL(dev_driver_string);
-#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
- struct device *dev = to_dev(kobj);
+ struct device *dev = kobj_to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->show)
@@ -108,7 +107,7 @@ static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
- struct device *dev = to_dev(kobj);
+ struct device *dev = kobj_to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->store)
@@ -182,7 +181,7 @@ EXPORT_SYMBOL_GPL(device_show_int);
*/
static void device_release(struct kobject *kobj)
{
- struct device *dev = to_dev(kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct device_private *p = dev->p;
if (dev->release)
@@ -200,7 +199,7 @@ static void device_release(struct kobject *kobj)
static const void *device_namespace(struct kobject *kobj)
{
- struct device *dev = to_dev(kobj);
+ struct device *dev = kobj_to_dev(kobj);
const void *ns = NULL;
if (dev->class && dev->class->ns_type)
@@ -221,7 +220,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
struct kobj_type *ktype = get_ktype(kobj);
if (ktype == &device_ktype) {
- struct device *dev = to_dev(kobj);
+ struct device *dev = kobj_to_dev(kobj);
if (dev->bus)
return 1;
if (dev->class)
@@ -232,7 +231,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
{
- struct device *dev = to_dev(kobj);
+ struct device *dev = kobj_to_dev(kobj);
if (dev->bus)
return dev->bus->name;
@@ -244,7 +243,7 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
static int dev_uevent(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env)
{
- struct device *dev = to_dev(kobj);
+ struct device *dev = kobj_to_dev(kobj);
int retval = 0;
/* add device node properties if present */
@@ -1132,7 +1131,7 @@ int device_register(struct device *dev)
*/
struct device *get_device(struct device *dev)
{
- return dev ? to_dev(kobject_get(&dev->kobj)) : NULL;
+ return dev ? kobj_to_dev(kobject_get(&dev->kobj)) : NULL;
}
/**
@@ -1754,25 +1753,25 @@ int device_move(struct device *dev, struct device *new_parent,
set_dev_node(dev, dev_to_node(new_parent));
}
- if (!dev->class)
- goto out_put;
- error = device_move_class_links(dev, old_parent, new_parent);
- if (error) {
- /* We ignore errors on cleanup since we're hosed anyway... */
- device_move_class_links(dev, new_parent, old_parent);
- if (!kobject_move(&dev->kobj, &old_parent->kobj)) {
- if (new_parent)
- klist_remove(&dev->p->knode_parent);
- dev->parent = old_parent;
- if (old_parent) {
- klist_add_tail(&dev->p->knode_parent,
- &old_parent->p->klist_children);
- set_dev_node(dev, dev_to_node(old_parent));
+ if (dev->class) {
+ error = device_move_class_links(dev, old_parent, new_parent);
+ if (error) {
+ /* We ignore errors on cleanup since we're hosed anyway... */
+ device_move_class_links(dev, new_parent, old_parent);
+ if (!kobject_move(&dev->kobj, &old_parent->kobj)) {
+ if (new_parent)
+ klist_remove(&dev->p->knode_parent);
+ dev->parent = old_parent;
+ if (old_parent) {
+ klist_add_tail(&dev->p->knode_parent,
+ &old_parent->p->klist_children);
+ set_dev_node(dev, dev_to_node(old_parent));
+ }
}
+ cleanup_glue_dir(dev, new_parent_kobj);
+ put_device(new_parent);
+ goto out;
}
- cleanup_glue_dir(dev, new_parent_kobj);
- put_device(new_parent);
- goto out;
}
switch (dpm_order) {
case DPM_ORDER_NONE:
@@ -1787,7 +1786,7 @@ int device_move(struct device *dev, struct device *new_parent,
device_pm_move_last(dev);
break;
}
-out_put:
+
put_device(old_parent);
out:
device_pm_unlock();
@@ -1812,6 +1811,13 @@ void device_shutdown(void)
while (!list_empty(&devices_kset->list)) {
dev = list_entry(devices_kset->list.prev, struct device,
kobj.entry);
+
+ /*
+ * hold reference count of device's parent to
+ * prevent it from being freed because parent's
+ * lock is to be held
+ */
+ get_device(dev->parent);
get_device(dev);
/*
* Make sure the device is off the kset list, in the
@@ -1820,6 +1826,11 @@ void device_shutdown(void)
list_del_init(&dev->kobj.entry);
spin_unlock(&devices_kset->list_lock);
+ /* hold lock to avoid race with probe/release */
+ if (dev->parent)
+ device_lock(dev->parent);
+ device_lock(dev);
+
/* Don't allow any more runtime suspends */
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
@@ -1831,7 +1842,13 @@ void device_shutdown(void)
dev_dbg(dev, "shutdown\n");
dev->driver->shutdown(dev);
}
+
+ device_unlock(dev);
+ if (dev->parent)
+ device_unlock(dev->parent);
+
put_device(dev);
+ put_device(dev->parent);
spin_lock(&devices_kset->list_lock);
}
@@ -1848,6 +1865,7 @@ int __dev_printk(const char *level, const struct device *dev,
struct va_format *vaf)
{
char dict[128];
+ const char *level_extra = "";
size_t dictlen = 0;
const char *subsys;
@@ -1894,10 +1912,14 @@ int __dev_printk(const char *level, const struct device *dev,
"DEVICE=+%s:%s", subsys, dev_name(dev));
}
skip:
+ if (level[2])
+ level_extra = &level[2]; /* skip past KERN_SOH "L" */
+
return printk_emit(0, level[1] - '0',
dictlen ? dict : NULL, dictlen,
- "%s %s: %pV",
- dev_driver_string(dev), dev_name(dev), vaf);
+ "%s %s: %s%pV",
+ dev_driver_string(dev), dev_name(dev),
+ level_extra, vaf);
}
EXPORT_SYMBOL(__dev_printk);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 4b01ab3d2c24..e3bbed8a617c 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -24,7 +24,6 @@
#include <linux/wait.h>
#include <linux/async.h>
#include <linux/pm_runtime.h>
-#include <scsi/scsi_scan.h>
#include "base.h"
#include "power/power.h"
@@ -86,8 +85,20 @@ static void deferred_probe_work_func(struct work_struct *work)
* manipulate the deferred list
*/
mutex_unlock(&deferred_probe_mutex);
+
+ /*
+ * Force the device to the end of the dpm_list since
+ * the PM code assumes that the order we add things to
+ * the list is a good order for suspend but deferred
+ * probe makes that very unsafe.
+ */
+ device_pm_lock();
+ device_pm_move_last(dev);
+ device_pm_unlock();
+
dev_dbg(dev, "Retrying from deferred list\n");
bus_probe_device(dev);
+
mutex_lock(&deferred_probe_mutex);
put_device(dev);
@@ -284,6 +295,7 @@ probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
+ dev_set_drvdata(dev, NULL);
if (ret == -EPROBE_DEFER) {
/* Driver requested deferred probing */
@@ -333,7 +345,6 @@ void wait_for_device_probe(void)
/* wait for the known devices to complete their probing */
wait_event(probe_waitqueue, atomic_read(&probe_count) == 0);
async_synchronize_full();
- scsi_complete_async_scans();
}
EXPORT_SYMBOL_GPL(wait_for_device_probe);
@@ -358,10 +369,9 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
- pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
- pm_runtime_put_sync(dev);
+ pm_runtime_idle(dev);
return ret;
}
@@ -408,9 +418,8 @@ int device_attach(struct device *dev)
ret = 0;
}
} else {
- pm_runtime_get_noresume(dev);
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
- pm_runtime_put_sync(dev);
+ pm_runtime_idle(dev);
}
out_unlock:
device_unlock(dev);
@@ -489,6 +498,7 @@ static void __device_release_driver(struct device *dev)
drv->remove(dev);
devres_release_all(dev);
dev->driver = NULL;
+ dev_set_drvdata(dev, NULL);
klist_remove(&dev->p->knode_driver);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index d91a3a0b2325..deb4a456cf83 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -156,9 +156,7 @@ static int dev_mkdir(const char *name, umode_t mode)
if (!err)
/* mark as kernel-created inode */
dentry->d_inode->i_private = &thread;
- dput(dentry);
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- path_put(&path);
+ done_path_create(&path, dentry);
return err;
}
@@ -218,10 +216,7 @@ static int handle_create(const char *nodename, umode_t mode, struct device *dev)
/* mark as kernel-created inode */
dentry->d_inode->i_private = &thread;
}
- dput(dentry);
-
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- path_put(&path);
+ done_path_create(&path, dentry);
return err;
}
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c
index 24e88fe29ec1..c30f3e1d0efc 100644
--- a/drivers/base/dma-buf.c
+++ b/drivers/base/dma-buf.c
@@ -493,6 +493,7 @@ EXPORT_SYMBOL_GPL(dma_buf_vmap);
/**
* dma_buf_vunmap - Unmap a vmap obtained by dma_buf_vmap.
* @dmabuf: [in] buffer to vunmap
+ * @vaddr: [in] vmap to vunmap
*/
void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
{
diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c
index 1b85949e3d2f..560a7173f810 100644
--- a/drivers/base/dma-coherent.c
+++ b/drivers/base/dma-coherent.c
@@ -186,6 +186,7 @@ EXPORT_SYMBOL(dma_release_from_coherent);
* @vma: vm_area for the userspace memory
* @vaddr: cpu address returned by dma_alloc_from_coherent
* @size: size of the memory buffer allocated by dma_alloc_from_coherent
+ * @ret: result from remap_pfn_range()
*
* This checks whether the memory was allocated from the per-device
* coherent memory pool and if so, maps that memory to the provided vma.
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c
index 78efb0306a44..34d94c762a1e 100644
--- a/drivers/base/dma-contiguous.c
+++ b/drivers/base/dma-contiguous.c
@@ -250,7 +250,7 @@ int __init dma_declare_contiguous(struct device *dev, unsigned long size,
return -EINVAL;
/* Sanitise input arguments */
- alignment = PAGE_SIZE << max(MAX_ORDER, pageblock_order);
+ alignment = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
base = ALIGN(base, alignment);
size = ALIGN(size, alignment);
limit &= ~(alignment - 1);
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c
index 6f3676f1559f..3fbedc75e7c5 100644
--- a/drivers/base/dma-mapping.c
+++ b/drivers/base/dma-mapping.c
@@ -10,6 +10,7 @@
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <linux/gfp.h>
+#include <asm-generic/dma-coherent.h>
/*
* Managed DMA API
@@ -217,4 +218,52 @@ void dmam_release_declared_memory(struct device *dev)
}
EXPORT_SYMBOL(dmam_release_declared_memory);
+/*
+ * Create scatter-list for the already allocated DMA buffer.
+ */
+int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
+ void *cpu_addr, dma_addr_t handle, size_t size)
+{
+ struct page *page = virt_to_page(cpu_addr);
+ int ret;
+
+ ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+ if (unlikely(ret))
+ return ret;
+
+ sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
+ return 0;
+}
+EXPORT_SYMBOL(dma_common_get_sgtable);
+
#endif
+
+/*
+ * Create userspace mapping for the DMA-coherent memory.
+ */
+int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
+ void *cpu_addr, dma_addr_t dma_addr, size_t size)
+{
+ int ret = -ENXIO;
+#ifdef CONFIG_MMU
+ unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+ unsigned long off = vma->vm_pgoff;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret))
+ return ret;
+
+ if (off < count && user_count <= (count - off)) {
+ ret = remap_pfn_range(vma, vma->vm_start,
+ pfn + off,
+ user_count << PAGE_SHIFT,
+ vma->vm_page_prot);
+ }
+#endif /* CONFIG_MMU */
+
+ return ret;
+}
+EXPORT_SYMBOL(dma_common_mmap);
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 207c27ddf828..974e301a1ef0 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -185,8 +185,12 @@ int driver_register(struct device_driver *drv)
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
- if (ret)
+ if (ret) {
bus_remove_driver(drv);
+ return ret;
+ }
+ kobject_uevent(&drv->p->kobj, KOBJ_ADD);
+
return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 5401814c874d..803cfc1597a9 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -22,8 +22,6 @@
#include <linux/slab.h>
#include <linux/sched.h>
-#define to_dev(obj) container_of(obj, struct device, kobj)
-
MODULE_AUTHOR("Manuel Estrada Sainz");
MODULE_DESCRIPTION("Multi purpose firmware loading support");
MODULE_LICENSE("GPL");
@@ -290,7 +288,7 @@ static ssize_t 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 device *dev = kobj_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
struct firmware *fw;
ssize_t ret_count;
@@ -384,7 +382,7 @@ static ssize_t 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 device *dev = kobj_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
struct firmware *fw;
ssize_t retval;
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index 869d7ff2227f..eb78e9640c4a 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -169,8 +169,7 @@ void pm_clk_init(struct device *dev)
*/
int pm_clk_create(struct device *dev)
{
- int ret = dev_pm_get_subsys_data(dev);
- return ret < 0 ? ret : 0;
+ return dev_pm_get_subsys_data(dev);
}
/**
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index a14085cc613f..39c32529b833 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -24,7 +24,6 @@
int dev_pm_get_subsys_data(struct device *dev)
{
struct pm_subsys_data *psd;
- int ret = 0;
psd = kzalloc(sizeof(*psd), GFP_KERNEL);
if (!psd)
@@ -40,7 +39,6 @@ int dev_pm_get_subsys_data(struct device *dev)
dev->power.subsys_data = psd;
pm_clk_init(dev);
psd = NULL;
- ret = 1;
}
spin_unlock_irq(&dev->power.lock);
@@ -48,7 +46,7 @@ int dev_pm_get_subsys_data(struct device *dev)
/* kfree() verifies that its argument is nonzero. */
kfree(psd);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data);
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 59894873a3b3..7d9c1cb1c39a 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -147,6 +147,8 @@ static int rpm_check_suspend_allowed(struct device *dev)
|| (dev->power.request_pending
&& dev->power.request == RPM_REQ_RESUME))
retval = -EAGAIN;
+ else if (__dev_pm_qos_read_value(dev) < 0)
+ retval = -EPERM;
else if (dev->power.runtime_status == RPM_SUSPENDED)
retval = 1;
@@ -388,7 +390,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
goto repeat;
}
- dev->power.deferred_resume = false;
if (dev->power.no_callbacks)
goto no_callback; /* Assume success. */
@@ -403,12 +404,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
goto out;
}
- if (__dev_pm_qos_read_value(dev) < 0) {
- /* Negative PM QoS constraint means "never suspend". */
- retval = -EPERM;
- goto out;
- }
-
__update_runtime_status(dev, RPM_SUSPENDING);
if (dev->pm_domain)
@@ -440,6 +435,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
wake_up_all(&dev->power.wait_queue);
if (dev->power.deferred_resume) {
+ dev->power.deferred_resume = false;
rpm_resume(dev, 0);
retval = -EAGAIN;
goto out;
@@ -584,6 +580,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
|| dev->parent->power.runtime_status == RPM_ACTIVE) {
atomic_inc(&dev->parent->power.child_count);
spin_unlock(&dev->parent->power.lock);
+ retval = 1;
goto no_callback; /* Assume success. */
}
spin_unlock(&dev->parent->power.lock);
@@ -664,7 +661,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
}
wake_up_all(&dev->power.wait_queue);
- if (!retval)
+ if (retval >= 0)
rpm_idle(dev, RPM_ASYNC);
out: