aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drivers/base/Kconfig29
-rw-r--r--drivers/base/Makefile1
-rw-r--r--drivers/base/component.c192
-rw-r--r--drivers/base/devres.c55
-rw-r--r--drivers/base/dma-contiguous.c220
-rw-r--r--drivers/base/firmware_class.c47
-rw-r--r--drivers/base/memory.c30
-rw-r--r--drivers/base/node.c2
-rw-r--r--drivers/base/platform.c56
-rw-r--r--drivers/base/power/main.c5
-rw-r--r--drivers/base/regmap/internal.h3
-rw-r--r--drivers/base/regmap/regcache-rbtree.c9
-rw-r--r--drivers/base/regmap/regcache.c2
-rw-r--r--drivers/base/regmap/regmap-debugfs.c12
-rw-r--r--drivers/base/regmap/regmap.c15
-rw-r--r--drivers/base/reservation.c39
-rw-r--r--drivers/dma-buf/dma-buf.c (renamed from drivers/base/dma-buf.c)168
17 files changed, 537 insertions, 348 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 23b8726962af..4e7f0ff83ae7 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -149,15 +149,21 @@ config EXTRA_FIRMWARE_DIR
some other directory containing the firmware files.
config FW_LOADER_USER_HELPER
+ bool
+
+config FW_LOADER_USER_HELPER_FALLBACK
bool "Fallback user-helper invocation for firmware loading"
depends on FW_LOADER
- default y
+ select FW_LOADER_USER_HELPER
help
This option enables / disables the invocation of user-helper
(e.g. udev) for loading firmware files as a fallback after the
direct file loading in kernel fails. The user-mode helper is
no longer required unless you have a special firmware file that
- resides in a non-standard path.
+ resides in a non-standard path. Moreover, the udev support has
+ been deprecated upstream.
+
+ If you are unsure about this, say N here.
config DEBUG_DRIVER
bool "Driver Core verbose debug messages"
@@ -208,6 +214,15 @@ config DMA_SHARED_BUFFER
APIs extension; the file's descriptor can then be passed on to other
driver.
+config FENCE_TRACE
+ bool "Enable verbose FENCE_TRACE messages"
+ depends on DMA_SHARED_BUFFER
+ help
+ Enable the FENCE_TRACE printks. This will add extra
+ spam to the console log, but will make it easier to diagnose
+ lockup related problems for dma-buffers shared across multiple
+ devices.
+
config DMA_CMA
bool "DMA Contiguous Memory Allocator"
depends on HAVE_DMA_CONTIGUOUS && CMA
@@ -274,16 +289,6 @@ config CMA_ALIGNMENT
If unsure, leave the default value "8".
-config CMA_AREAS
- int "Maximum count of the CMA device-private areas"
- default 7
- help
- CMA allows to create CMA areas for particular devices. This parameter
- sets the maximum number of such device private CMA areas in the
- system.
-
- If unsure, leave the default value "7".
-
endif
endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 04b314e0fa51..4aab26ec0292 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -10,7 +10,6 @@ obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
obj-y += power/
obj-$(CONFIG_HAS_DMA) += dma-mapping.o
obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
-obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o reservation.o
obj-$(CONFIG_ISA) += isa.o
obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o
diff --git a/drivers/base/component.c b/drivers/base/component.c
index c4778995cd72..f748430bb654 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -18,6 +18,15 @@
#include <linux/mutex.h>
#include <linux/slab.h>
+struct component_match {
+ size_t alloc;
+ size_t num;
+ struct {
+ void *data;
+ int (*fn)(struct device *, void *);
+ } compare[0];
+};
+
struct master {
struct list_head node;
struct list_head components;
@@ -25,6 +34,7 @@ struct master {
const struct component_master_ops *ops;
struct device *dev;
+ struct component_match *match;
};
struct component {
@@ -69,6 +79,11 @@ static void component_detach_master(struct master *master, struct component *c)
c->master = NULL;
}
+/*
+ * Add a component to a master, finding the component via the compare
+ * function and compare data. This is safe to call for duplicate matches
+ * and will not result in the same component being added multiple times.
+ */
int component_master_add_child(struct master *master,
int (*compare)(struct device *, void *), void *compare_data)
{
@@ -76,11 +91,12 @@ int component_master_add_child(struct master *master,
int ret = -ENXIO;
list_for_each_entry(c, &component_list, node) {
- if (c->master)
+ if (c->master && c->master != master)
continue;
if (compare(c->dev, compare_data)) {
- component_attach_master(master, c);
+ if (!c->master)
+ component_attach_master(master, c);
ret = 0;
break;
}
@@ -90,6 +106,34 @@ int component_master_add_child(struct master *master,
}
EXPORT_SYMBOL_GPL(component_master_add_child);
+static int find_components(struct master *master)
+{
+ struct component_match *match = master->match;
+ size_t i;
+ int ret = 0;
+
+ if (!match) {
+ /*
+ * Search the list of components, looking for components that
+ * belong to this master, and attach them to the master.
+ */
+ return master->ops->add_components(master->dev, master);
+ }
+
+ /*
+ * Scan the array of match functions and attach
+ * any components which are found to this master.
+ */
+ for (i = 0; i < match->num; i++) {
+ ret = component_master_add_child(master,
+ match->compare[i].fn,
+ match->compare[i].data);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
/* Detach all attached components from this master */
static void master_remove_components(struct master *master)
{
@@ -113,44 +157,44 @@ static void master_remove_components(struct master *master)
static int try_to_bring_up_master(struct master *master,
struct component *component)
{
- int ret = 0;
+ int ret;
- if (!master->bound) {
- /*
- * Search the list of components, looking for components that
- * belong to this master, and attach them to the master.
- */
- if (master->ops->add_components(master->dev, master)) {
- /* Failed to find all components */
- master_remove_components(master);
- ret = 0;
- goto out;
- }
+ if (master->bound)
+ return 0;
- if (component && component->master != master) {
- master_remove_components(master);
- ret = 0;
- goto out;
- }
+ /*
+ * Search the list of components, looking for components that
+ * belong to this master, and attach them to the master.
+ */
+ if (find_components(master)) {
+ /* Failed to find all components */
+ ret = 0;
+ goto out;
+ }
- if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
- ret = -ENOMEM;
- goto out;
- }
+ if (component && component->master != master) {
+ ret = 0;
+ goto out;
+ }
- /* Found all components */
- ret = master->ops->bind(master->dev);
- if (ret < 0) {
- devres_release_group(master->dev, NULL);
- dev_info(master->dev, "master bind failed: %d\n", ret);
- master_remove_components(master);
- goto out;
- }
+ if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
+ ret = -ENOMEM;
+ goto out;
+ }
- master->bound = true;
- ret = 1;
+ /* Found all components */
+ ret = master->ops->bind(master->dev);
+ if (ret < 0) {
+ devres_release_group(master->dev, NULL);
+ dev_info(master->dev, "master bind failed: %d\n", ret);
+ goto out;
}
+
+ master->bound = true;
+ return 1;
+
out:
+ master_remove_components(master);
return ret;
}
@@ -180,18 +224,89 @@ static void take_down_master(struct master *master)
master_remove_components(master);
}
-int component_master_add(struct device *dev,
- const struct component_master_ops *ops)
+static size_t component_match_size(size_t num)
+{
+ return offsetof(struct component_match, compare[num]);
+}
+
+static struct component_match *component_match_realloc(struct device *dev,
+ struct component_match *match, size_t num)
+{
+ struct component_match *new;
+
+ if (match && match->alloc == num)
+ return match;
+
+ new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL);
+ if (!new)
+ return ERR_PTR(-ENOMEM);
+
+ if (match) {
+ memcpy(new, match, component_match_size(min(match->num, num)));
+ devm_kfree(dev, match);
+ } else {
+ new->num = 0;
+ }
+
+ new->alloc = num;
+
+ return new;
+}
+
+/*
+ * Add a component to be matched.
+ *
+ * The match array is first created or extended if necessary.
+ */
+void component_match_add(struct device *dev, struct component_match **matchptr,
+ int (*compare)(struct device *, void *), void *compare_data)
+{
+ struct component_match *match = *matchptr;
+
+ if (IS_ERR(match))
+ return;
+
+ if (!match || match->num == match->alloc) {
+ size_t new_size = match ? match->alloc + 16 : 15;
+
+ match = component_match_realloc(dev, match, new_size);
+
+ *matchptr = match;
+
+ if (IS_ERR(match))
+ return;
+ }
+
+ match->compare[match->num].fn = compare;
+ match->compare[match->num].data = compare_data;
+ match->num++;
+}
+EXPORT_SYMBOL(component_match_add);
+
+int component_master_add_with_match(struct device *dev,
+ const struct component_master_ops *ops,
+ struct component_match *match)
{
struct master *master;
int ret;
+ if (ops->add_components && match)
+ return -EINVAL;
+
+ if (match) {
+ /* Reallocate the match array for its true size */
+ match = component_match_realloc(dev, match, match->num);
+ if (IS_ERR(match))
+ return PTR_ERR(match);
+ }
+
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return -ENOMEM;
master->dev = dev;
master->ops = ops;
+ master->match = match;
INIT_LIST_HEAD(&master->components);
/* Add to the list of available masters. */
@@ -209,6 +324,13 @@ int component_master_add(struct device *dev,
return ret < 0 ? ret : 0;
}
+EXPORT_SYMBOL_GPL(component_master_add_with_match);
+
+int component_master_add(struct device *dev,
+ const struct component_master_ops *ops)
+{
+ return component_master_add_with_match(dev, ops, NULL);
+}
EXPORT_SYMBOL_GPL(component_master_add);
void component_master_del(struct device *dev,
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 52302946770f..69d9b0c89a01 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -817,6 +817,61 @@ char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp)
EXPORT_SYMBOL_GPL(devm_kstrdup);
/**
+ * devm_kvasprintf - Allocate resource managed space
+ * for the formatted string.
+ * @dev: Device to allocate memory for
+ * @gfp: the GFP mask used in the devm_kmalloc() call when
+ * allocating memory
+ * @fmt: the formatted string to duplicate
+ * @ap: the list of tokens to be placed in the formatted string
+ * RETURNS:
+ * Pointer to allocated string on success, NULL on failure.
+ */
+char *devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt,
+ va_list ap)
+{
+ unsigned int len;
+ char *p;
+ va_list aq;
+
+ va_copy(aq, ap);
+ len = vsnprintf(NULL, 0, fmt, aq);
+ va_end(aq);
+
+ p = devm_kmalloc(dev, len+1, gfp);
+ if (!p)
+ return NULL;
+
+ vsnprintf(p, len+1, fmt, ap);
+
+ return p;
+}
+EXPORT_SYMBOL(devm_kvasprintf);
+
+/**
+ * devm_kasprintf - Allocate resource managed space
+ * and copy an existing formatted string into that
+ * @dev: Device to allocate memory for
+ * @gfp: the GFP mask used in the devm_kmalloc() call when
+ * allocating memory
+ * @fmt: the string to duplicate
+ * RETURNS:
+ * Pointer to allocated string on success, NULL on failure.
+ */
+char *devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...)
+{
+ va_list ap;
+ char *p;
+
+ va_start(ap, fmt);
+ p = devm_kvasprintf(dev, gfp, fmt, ap);
+ va_end(ap);
+
+ return p;
+}
+EXPORT_SYMBOL_GPL(devm_kasprintf);
+
+/**
* devm_kfree - Resource-managed kfree
* @dev: Device this memory belongs to
* @p: Memory to free
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c
index 6467c919c509..6606abdf880c 100644
--- a/drivers/base/dma-contiguous.c
+++ b/drivers/base/dma-contiguous.c
@@ -24,23 +24,9 @@
#include <linux/memblock.h>
#include <linux/err.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <linux/page-isolation.h>
#include <linux/sizes.h>
-#include <linux/slab.h>
-#include <linux/swap.h>
-#include <linux/mm_types.h>
#include <linux/dma-contiguous.h>
-
-struct cma {
- unsigned long base_pfn;
- unsigned long count;
- unsigned long *bitmap;
- struct mutex lock;
-};
-
-struct cma *dma_contiguous_default_area;
+#include <linux/cma.h>
#ifdef CONFIG_CMA_SIZE_MBYTES
#define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES
@@ -48,6 +34,8 @@ struct cma *dma_contiguous_default_area;
#define CMA_SIZE_MBYTES 0
#endif
+struct cma *dma_contiguous_default_area;
+
/*
* Default global CMA area size can be defined in kernel's .config.
* This is useful mainly for distro maintainers to create a kernel
@@ -154,65 +142,6 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
}
}
-static DEFINE_MUTEX(cma_mutex);
-
-static int __init cma_activate_area(struct cma *cma)
-{
- int bitmap_size = BITS_TO_LONGS(cma->count) * sizeof(long);
- unsigned long base_pfn = cma->base_pfn, pfn = base_pfn;
- unsigned i = cma->count >> pageblock_order;
- struct zone *zone;
-
- cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
-
- if (!cma->bitmap)
- return -ENOMEM;
-
- WARN_ON_ONCE(!pfn_valid(pfn));
- zone = page_zone(pfn_to_page(pfn));
-
- do {
- unsigned j;
- base_pfn = pfn;
- for (j = pageblock_nr_pages; j; --j, pfn++) {
- WARN_ON_ONCE(!pfn_valid(pfn));
- /*
- * alloc_contig_range requires the pfn range
- * specified to be in the same zone. Make this
- * simple by forcing the entire CMA resv range
- * to be in the same zone.
- */
- if (page_zone(pfn_to_page(pfn)) != zone)
- goto err;
- }
- init_cma_reserved_pageblock(pfn_to_page(base_pfn));
- } while (--i);
-
- mutex_init(&cma->lock);
- return 0;
-
-err:
- kfree(cma->bitmap);
- return -EINVAL;
-}
-
-static struct cma cma_areas[MAX_CMA_AREAS];
-static unsigned cma_area_count;
-
-static int __init cma_init_reserved_areas(void)
-{
- int i;
-
- for (i = 0; i < cma_area_count; i++) {
- int ret = cma_activate_area(&cma_areas[i]);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-core_initcall(cma_init_reserved_areas);
-
/**
* dma_contiguous_reserve_area() - reserve custom contiguous area
* @size: Size of the reserved area (in bytes),
@@ -234,72 +163,17 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
phys_addr_t limit, struct cma **res_cma,
bool fixed)
{
- struct cma *cma = &cma_areas[cma_area_count];
- phys_addr_t alignment;
- int ret = 0;
-
- pr_debug("%s(size %lx, base %08lx, limit %08lx)\n", __func__,
- (unsigned long)size, (unsigned long)base,
- (unsigned long)limit);
-
- /* Sanity checks */
- if (cma_area_count == ARRAY_SIZE(cma_areas)) {
- pr_err("Not enough slots for CMA reserved regions!\n");
- return -ENOSPC;
- }
-
- if (!size)
- return -EINVAL;
-
- /* Sanitise input arguments */
- alignment = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
- base = ALIGN(base, alignment);
- size = ALIGN(size, alignment);
- limit &= ~(alignment - 1);
-
- /* Reserve memory */
- if (base && fixed) {
- if (memblock_is_region_reserved(base, size) ||
- memblock_reserve(base, size) < 0) {
- ret = -EBUSY;
- goto err;
- }
- } else {
- phys_addr_t addr = memblock_alloc_range(size, alignment, base,
- limit);
- if (!addr) {
- ret = -ENOMEM;
- goto err;
- } else {
- base = addr;
- }
- }
-
- /*
- * Each reserved area must be initialised later, when more kernel
- * subsystems (like slab allocator) are available.
- */
- cma->base_pfn = PFN_DOWN(base);
- cma->count = size >> PAGE_SHIFT;
- *res_cma = cma;
- cma_area_count++;
+ int ret;
- pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M,
- (unsigned long)base);
+ ret = cma_declare_contiguous(base, size, limit, 0, 0, fixed, res_cma);
+ if (ret)
+ return ret;
/* Architecture specific contiguous memory fixup. */
- dma_contiguous_early_fixup(base, size);
- return 0;
-err:
- pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M);
- return ret;
-}
+ dma_contiguous_early_fixup(cma_get_base(*res_cma),
+ cma_get_size(*res_cma));
-static void clear_cma_bitmap(struct cma *cma, unsigned long pfn, int count)
-{
- mutex_lock(&cma->lock);
- bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count);
- mutex_unlock(&cma->lock);
+ return 0;
}
/**
@@ -316,62 +190,10 @@ static void clear_cma_bitmap(struct cma *cma, unsigned long pfn, int count)
struct page *dma_alloc_from_contiguous(struct device *dev, int count,
unsigned int align)
{
- unsigned long mask, pfn, pageno, start = 0;
- struct cma *cma = dev_get_cma_area(dev);
- struct page *page = NULL;
- int ret;
-
- if (!cma || !cma->count)
- return NULL;
-
if (align > CONFIG_CMA_ALIGNMENT)
align = CONFIG_CMA_ALIGNMENT;
- pr_debug("%s(cma %p, count %d, align %d)\n", __func__, (void *)cma,
- count, align);
-
- if (!count)
- return NULL;
-
- mask = (1 << align) - 1;
-
-
- for (;;) {
- mutex_lock(&cma->lock);
- pageno = bitmap_find_next_zero_area(cma->bitmap, cma->count,
- start, count, mask);
- if (pageno >= cma->count) {
- mutex_unlock(&cma->lock);
- break;
- }
- bitmap_set(cma->bitmap, pageno, count);
- /*
- * It's safe to drop the lock here. We've marked this region for
- * our exclusive use. If the migration fails we will take the
- * lock again and unmark it.
- */
- mutex_unlock(&cma->lock);
-
- pfn = cma->base_pfn + pageno;
- mutex_lock(&cma_mutex);
- ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA);
- mutex_unlock(&cma_mutex);
- if (ret == 0) {
- page = pfn_to_page(pfn);
- break;
- } else if (ret != -EBUSY) {
- clear_cma_bitmap(cma, pfn, count);
- break;
- }
- clear_cma_bitmap(cma, pfn, count);
- pr_debug("%s(): memory range at %p is busy, retrying\n",
- __func__, pfn_to_page(pfn));
- /* try again with a bit different memory target */
- start = pageno + mask + 1;
- }
-
- pr_debug("%s(): returned %p\n", __func__, page);
- return page;
+ return cma_alloc(dev_get_cma_area(dev), count, align);
}
/**
@@ -387,23 +209,5 @@ struct page *dma_alloc_from_contiguous(struct device *dev, int count,
bool dma_release_from_contiguous(struct device *dev, struct page *pages,
int count)
{
- struct cma *cma = dev_get_cma_area(dev);
- unsigned long pfn;
-
- if (!cma || !pages)
- return false;
-
- pr_debug("%s(page %p)\n", __func__, (void *)pages);
-
- pfn = page_to_pfn(pages);
-
- if (pfn < cma->base_pfn || pfn >= cma->base_pfn + cma->count)
- return false;
-
- VM_BUG_ON(pfn + count > cma->base_pfn + cma->count);
-
- free_contig_range(pfn, count);
- clear_cma_bitmap(cma, pfn, count);
-
- return true;
+ return cma_release(dev_get_cma_area(dev), pages, count);
}
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 63f165c59da8..bf424305f3dc 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -101,10 +101,16 @@ static inline long firmware_loading_timeout(void)
#define FW_OPT_UEVENT (1U << 0)
#define FW_OPT_NOWAIT (1U << 1)
#ifdef CONFIG_FW_LOADER_USER_HELPER
-#define FW_OPT_FALLBACK (1U << 2)
+#define FW_OPT_USERHELPER (1U << 2)
#else
-#define FW_OPT_FALLBACK 0
+#define FW_OPT_USERHELPER 0
#endif
+#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK
+#define FW_OPT_FALLBACK FW_OPT_USERHELPER
+#else
+#define FW_OPT_FALLBACK 0
+#endif
+#define FW_OPT_NO_WARN (1U << 3)
struct firmware_cache {
/* firmware_buf instance will be added into the below list */
@@ -280,26 +286,15 @@ static const char * const fw_path[] = {
module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
-/* Don't inline this: 'struct kstat' is biggish */
-static noinline_for_stack int fw_file_size(struct file *file)
-{
- struct kstat st;
- if (vfs_getattr(&file->f_path, &st))
- return -1;
- if (!S_ISREG(st.mode))
- return -1;
- if (st.size != (int)st.size)
- return -1;
- return st.size;
-}
-
static int fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf)
{
int size;
char *buf;
int rc;
- size = fw_file_size(file);
+ if (!S_ISREG(file_inode(file)->i_mode))
+ return -EINVAL;
+ size = i_size_read(file_inode(file));
if (size <= 0)
return -EINVAL;
buf = vmalloc(size);
@@ -740,7 +735,7 @@ out:
static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
{
struct firmware_buf *buf = fw_priv->buf;
- int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
+ int pages_needed = PAGE_ALIGN(min_size) >> PAGE_SHIFT;
/* If the array of pages is too small, grow it... */
if (buf->page_array_size < pages_needed) {
@@ -933,7 +928,9 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
wait_for_completion(&buf->completion);
cancel_delayed_work_sync(&fw_priv->timeout_work);
- if (!buf->data)
+ if (is_fw_load_aborted(buf))
+ retval = -EAGAIN;
+ else if (!buf->data)
retval = -ENOMEM;
device_remove_file(f_dev, &dev_attr_loading);
@@ -1133,10 +1130,11 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
ret = fw_get_filesystem_firmware(device, fw->priv);
if (ret) {
- if (opt_flags & FW_OPT_FALLBACK) {
+ if (!(opt_flags & FW_OPT_NO_WARN))
dev_warn(device,
- "Direct firmware load failed with error %d\n",
- ret);
+ "Direct firmware load for %s failed with error %d\n",
+ name, ret);
+ if (opt_flags & FW_OPT_USERHELPER) {
dev_warn(device, "Falling back to user helper\n");
ret = fw_load_from_user_helper(fw, name, device,
opt_flags, timeout);
@@ -1193,7 +1191,6 @@ request_firmware(const struct firmware **firmware_p, const char *name,
}
EXPORT_SYMBOL(request_firmware);
-#ifdef CONFIG_FW_LOADER_USER_HELPER
/**
* request_firmware: - load firmware directly without usermode helper
* @firmware_p: pointer to firmware image
@@ -1210,12 +1207,12 @@ int request_firmware_direct(const struct firmware **firmware_p,
{
int ret;
__module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device, FW_OPT_UEVENT);
+ ret = _request_firmware(firmware_p, name, device,
+ FW_OPT_UEVENT | FW_OPT_NO_WARN);
module_put(THIS_MODULE);
return ret;
}
EXPORT_SYMBOL_GPL(request_firmware_direct);
-#endif
/**
* release_firmware: - release the resource associated with a firmware image
@@ -1299,7 +1296,7 @@ request_firmware_nowait(
fw_work->context = context;
fw_work->cont = cont;
fw_work->opt_flags = FW_OPT_NOWAIT | FW_OPT_FALLBACK |
- (uevent ? FW_OPT_UEVENT : 0);
+ (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER);
if (!try_module_get(module)) {
kfree(fw_work);
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 89f752dd8465..a2e13e250bba 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -284,7 +284,7 @@ static int memory_subsys_online(struct device *dev)
* attribute and need to set the online_type.
*/
if (mem->online_type < 0)
- mem->online_type = ONLINE_KEEP;
+ mem->online_type = MMOP_ONLINE_KEEP;
ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE);
@@ -315,23 +315,23 @@ store_mem_state(struct device *dev,
if (ret)
return ret;
- if (!strncmp(buf, "online_kernel", min_t(int, count, 13)))
- online_type = ONLINE_KERNEL;
- else if (!strncmp(buf, "online_movable", min_t(int, count, 14)))
- online_type = ONLINE_MOVABLE;
- else if (!strncmp(buf, "online", min_t(int, count, 6)))
- online_type = ONLINE_KEEP;
- else if (!strncmp(buf, "offline", min_t(int, count, 7)))
- online_type = -1;
+ if (sysfs_streq(buf, "online_kernel"))
+ online_type = MMOP_ONLINE_KERNEL;
+ else if (sysfs_streq(buf, "online_movable"))
+ online_type = MMOP_ONLINE_MOVABLE;
+ else if (sysfs_streq(buf, "online"))
+ online_type = MMOP_ONLINE_KEEP;
+ else if (sysfs_streq(buf, "offline"))
+ online_type = MMOP_OFFLINE;
else {
ret = -EINVAL;
goto err;
}
switch (online_type) {
- case ONLINE_KERNEL:
- case ONLINE_MOVABLE:
- case ONLINE_KEEP:
+ case MMOP_ONLINE_KERNEL:
+ case MMOP_ONLINE_MOVABLE:
+ case MMOP_ONLINE_KEEP:
/*
* mem->online_type is not protected so there can be a
* race here. However, when racing online, the first
@@ -342,7 +342,7 @@ store_mem_state(struct device *dev,
mem->online_type = online_type;
ret = device_online(&mem->dev);
break;
- case -1:
+ case MMOP_OFFLINE:
ret = device_offline(&mem->dev);
break;
default:
@@ -406,7 +406,9 @@ memory_probe_store(struct device *dev, struct device_attribute *attr,
int i, ret;
unsigned long pages_per_block = PAGES_PER_SECTION * sections_per_block;
- phys_addr = simple_strtoull(buf, NULL, 0);
+ ret = kstrtoull(buf, 0, &phys_addr);
+ if (ret)
+ return ret;
if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1))
return -EINVAL;
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 8f7ed9933a7c..c6d3ae05f1ca 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -126,7 +126,7 @@ static ssize_t node_read_meminfo(struct device *dev,
nid, K(node_page_state(nid, NR_FILE_PAGES)),
nid, K(node_page_state(nid, NR_FILE_MAPPED)),
nid, K(node_page_state(nid, NR_ANON_PAGES)),
- nid, K(node_page_state(nid, NR_SHMEM)),
+ nid, K(i.sharedram),
nid, node_page_state(nid, NR_KERNEL_STACK) *
THREAD_SIZE / 1024,
nid, K(node_page_state(nid, NR_PAGETABLE)),
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index eee48c49f5de..ab4f4ce02722 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -23,6 +23,8 @@
#include <linux/pm_runtime.h>
#include <linux/idr.h>
#include <linux/acpi.h>
+#include <linux/clk/clk-conf.h>
+#include <linux/limits.h>
#include "base.h"
#include "power/power.h"
@@ -175,7 +177,7 @@ EXPORT_SYMBOL_GPL(platform_add_devices);
struct platform_object {
struct platform_device pdev;
- char name[1];
+ char name[];
};
/**
@@ -201,6 +203,7 @@ static void platform_device_release(struct device *dev)
kfree(pa->pdev.dev.platform_data);
kfree(pa->pdev.mfd_cell);
kfree(pa->pdev.resource);
+ kfree(pa->pdev.driver_override);
kfree(pa);
}
@@ -216,7 +219,7 @@ struct platform_device *platform_device_alloc(const char *name, int id)
{
struct platform_object *pa;
- pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
+ pa = kzalloc(sizeof(*pa) + strlen(name) + 1, GFP_KERNEL);
if (pa) {
strcpy(pa->name, name);
pa->pdev.name = pa->name;
@@ -499,6 +502,10 @@ static int platform_drv_probe(struct device *_dev)
struct platform_device *dev = to_platform_device(_dev);
int ret;
+ ret = of_clk_set_defaults(_dev->of_node, false);
+ if (ret < 0)
+ return ret;
+
acpi_dev_pm_attach(_dev, true);
ret = drv->probe(dev);
@@ -708,8 +715,49 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
}
static DEVICE_ATTR_RO(modalias);
+static ssize_t driver_override_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ char *driver_override, *old = pdev->driver_override, *cp;
+
+ if (count > PATH_MAX)
+ return -EINVAL;
+
+ driver_override = kstrndup(buf, count, GFP_KERNEL);
+ if (!driver_override)
+ return -ENOMEM;
+
+ cp = strchr(driver_override, '\n');
+ if (cp)
+ *cp = '\0';
+
+ if (strlen(driver_override)) {
+ pdev->driver_override = driver_override;
+ } else {
+ kfree(driver_override);
+ pdev->driver_override = NULL;
+ }
+
+ kfree(old);
+
+ return count;
+}
+
+static ssize_t driver_override_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return sprintf(buf, "%s\n", pdev->driver_override);
+}
+static DEVICE_ATTR_RW(driver_override);
+
+
static struct attribute *platform_dev_attrs[] = {
&dev_attr_modalias.attr,
+ &dev_attr_driver_override.attr,
NULL,
};
ATTRIBUTE_GROUPS(platform_dev);
@@ -765,6 +813,10 @@ static int platform_match(struct device *dev, struct device_driver *drv)
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
+ /* When driver_override is set, only bind to the matching driver */
+ if (pdev->driver_override)
+ return !strcmp(pdev->driver_override, drv->name);
+
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index bf412961a934..b67d9aef9fe4 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -465,6 +465,7 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
* device_resume_noirq - Execute an "early resume" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
+ * @async: If true, the device is being resumed asynchronously.
*
* The driver of @dev will not receive interrupts while this function is being
* executed.
@@ -594,6 +595,7 @@ static void dpm_resume_noirq(pm_message_t state)
* device_resume_early - Execute an "early resume" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
+ * @async: If true, the device is being resumed asynchronously.
*
* Runtime PM is disabled for @dev while this function is being executed.
*/
@@ -1004,6 +1006,7 @@ static pm_message_t resume_event(pm_message_t sleep_state)
* device_suspend_noirq - Execute a "late suspend" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
+ * @async: If true, the device is being suspended asynchronously.
*
* The driver of @dev will not receive interrupts while this function is being
* executed.
@@ -1144,6 +1147,7 @@ static int dpm_suspend_noirq(pm_message_t state)
* device_suspend_late - Execute a "late suspend" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
+ * @async: If true, the device is being suspended asynchronously.
*
* Runtime PM is disabled for @dev while this function is being executed.
*/
@@ -1298,6 +1302,7 @@ EXPORT_SYMBOL_GPL(dpm_suspend_end);
* @dev: Device to suspend.
* @state: PM transition of the system being carried out.
* @cb: Suspend callback to execute.
+ * @info: string description of caller.
*/
static int legacy_suspend(struct device *dev, pm_message_t state,
int (*cb)(struct device *dev, pm_message_t state),
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 7d1326985bee..bfc90b8547f2 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -146,6 +146,9 @@ struct regcache_ops {
enum regcache_type type;
int (*init)(struct regmap *map);
int (*exit)(struct regmap *map);
+#ifdef CONFIG_DEBUG_FS
+ void (*debugfs_init)(struct regmap *map);
+#endif
int (*read)(struct regmap *map, unsigned int reg, unsigned int *value);
int (*write)(struct regmap *map, unsigned int reg, unsigned int value);
int (*sync)(struct regmap *map, unsigned int min, unsigned int max);
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
index 6a7e4fa12854..f3e8fe0cc650 100644
--- a/drivers/base/regmap/regcache-rbtree.c
+++ b/drivers/base/regmap/regcache-rbtree.c
@@ -194,10 +194,6 @@ static void rbtree_debugfs_init(struct regmap *map)
{
debugfs_create_file("rbtree", 0400, map->debugfs, map, &rbtree_fops);
}
-#else
-static void rbtree_debugfs_init(struct regmap *map)
-{
-}
#endif
static int regcache_rbtree_init(struct regmap *map)
@@ -222,8 +218,6 @@ static int regcache_rbtree_init(struct regmap *map)
goto err;
}
- rbtree_debugfs_init(map);
-
return 0;
err:
@@ -532,6 +526,9 @@ struct regcache_ops regcache_rbtree_ops = {
.name = "rbtree",
.init = regcache_rbtree_init,
.exit = regcache_rbtree_exit,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_init = rbtree_debugfs_init,
+#endif
.read = regcache_rbtree_read,
.write = regcache_rbtree_write,
.sync = regcache_rbtree_sync,
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 29b4128da0b0..5617da6dc898 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -698,7 +698,7 @@ int regcache_sync_block(struct regmap *map, void *block,
unsigned int block_base, unsigned int start,
unsigned int end)
{
- if (regmap_can_raw_write(map))
+ if (regmap_can_raw_write(map) && !map->use_single_rw)
return regcache_sync_block_raw(map, block, cache_present,
block_base, start, end);
else
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 45d812c0ea77..0c94b661c16f 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -512,7 +512,14 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
map, &regmap_reg_ranges_fops);
if (map->max_register || regmap_readable(map, 0)) {
- debugfs_create_file("registers", 0400, map->debugfs,
+ umode_t registers_mode;
+
+ if (IS_ENABLED(REGMAP_ALLOW_WRITE_DEBUGFS))
+ registers_mode = 0600;
+ else
+ registers_mode = 0400;
+
+ debugfs_create_file("registers", registers_mode, map->debugfs,
map, &regmap_map_fops);
debugfs_create_file("access", 0400, map->debugfs,
map, &regmap_access_fops);
@@ -538,6 +545,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
next = rb_next(&range_node->node);
}
+
+ if (map->cache_ops && map->cache_ops->debugfs_init)
+ map->cache_ops->debugfs_init(map);
}
void regmap_debugfs_exit(struct regmap *map)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 74d8c0672cf6..1cf427bc0d4a 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -109,7 +109,7 @@ bool regmap_readable(struct regmap *map, unsigned int reg)
bool regmap_volatile(struct regmap *map, unsigned int reg)
{
- if (!regmap_readable(map, reg))
+ if (!map->format.format_write && !regmap_readable(map, reg))
return false;
if (map->volatile_reg)
@@ -1073,6 +1073,19 @@ struct regmap *dev_get_regmap(struct device *dev, const char *name)
}
EXPORT_SYMBOL_GPL(dev_get_regmap);
+/**
+ * regmap_get_device(): Obtain the device from a regmap
+ *
+ * @map: Register map to operate on.
+ *
+ * Returns the underlying device that the regmap has been created for.
+ */
+struct device *regmap_get_device(struct regmap *map)
+{
+ return map->dev;
+}
+EXPORT_SYMBOL_GPL(regmap_get_device);
+
static int _regmap_select_page(struct regmap *map, unsigned int *reg,
struct regmap_range_node *range,
unsigned int val_num)
diff --git a/drivers/base/reservation.c b/drivers/base/reservation.c
deleted file mode 100644
index a73fbf3b8e56..000000000000
--- a/drivers/base/reservation.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2012-2013 Canonical Ltd
- *
- * Based on bo.c which bears the following copyright notice,
- * but is dual licensed:
- *
- * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
- * All Rights Reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sub license, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
- * USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- **************************************************************************/
-/*
- * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
- */
-
-#include <linux/reservation.h>
-#include <linux/export.h>
-
-DEFINE_WW_CLASS(reservation_ww_class);
-EXPORT_SYMBOL(reservation_ww_class);
diff --git a/drivers/base/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 840c7fa80983..f3014c448e1e 100644
--- a/drivers/base/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -25,10 +25,13 @@
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/dma-buf.h>
+#include <linux/fence.h>
#include <linux/anon_inodes.h>
#include <linux/export.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/poll.h>
+#include <linux/reservation.h>
static inline int is_dma_buf_file(struct file *);
@@ -50,12 +53,25 @@ static int dma_buf_release(struct inode *inode, struct file *file)
BUG_ON(dmabuf->vmapping_counter);
+ /*
+ * Any fences that a dma-buf poll can wait on should be signaled
+ * before releasing dma-buf. This is the responsibility of each
+ * driver that uses the reservation objects.
+ *
+ * If you hit this BUG() it means someone dropped their ref to the
+ * dma-buf while still having pending operation to the buffer.
+ */
+ BUG_ON(dmabuf->cb_shared.active || dmabuf->cb_excl.active);
+
dmabuf->ops->release(dmabuf);
mutex_lock(&db_list.lock);
list_del(&dmabuf->list_node);
mutex_unlock(&db_list.lock);
+ if (dmabuf->resv == (struct reservation_object *)&dmabuf[1])
+ reservation_object_fini(dmabuf->resv);
+
kfree(dmabuf);
return 0;
}
@@ -103,10 +119,141 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence)
return base + offset;
}
+static void dma_buf_poll_cb(struct fence *fence, struct fence_cb *cb)
+{
+ struct dma_buf_poll_cb_t *dcb = (struct dma_buf_poll_cb_t *)cb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dcb->poll->lock, flags);
+ wake_up_locked_poll(dcb->poll, dcb->active);
+ dcb->active = 0;
+ spin_unlock_irqrestore(&dcb->poll->lock, flags);
+}
+
+static unsigned int dma_buf_poll(struct file *file, poll_table *poll)
+{
+ struct dma_buf *dmabuf;
+ struct reservation_object *resv;
+ struct reservation_object_list *fobj;
+ struct fence *fence_excl;
+ unsigned long events;
+ unsigned shared_count, seq;
+
+ dmabuf = file->private_data;
+ if (!dmabuf || !dmabuf->resv)
+ return POLLERR;
+
+ resv = dmabuf->resv;
+
+ poll_wait(file, &dmabuf->poll, poll);
+
+ events = poll_requested_events(poll) & (POLLIN | POLLOUT);
+ if (!events)
+ return 0;
+
+retry:
+ seq = read_seqcount_begin(&resv->seq);
+ rcu_read_lock();
+
+ fobj = rcu_dereference(resv->fence);
+ if (fobj)
+ shared_count = fobj->shared_count;
+ else
+ shared_count = 0;
+ fence_excl = rcu_dereference(resv->fence_excl);
+ if (read_seqcount_retry(&resv->seq, seq)) {
+ rcu_read_unlock();
+ goto retry;
+ }
+
+ if (fence_excl && (!(events & POLLOUT) || shared_count == 0)) {
+ struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_excl;
+ unsigned long pevents = POLLIN;
+
+ if (shared_count == 0)
+ pevents |= POLLOUT;
+
+ spin_lock_irq(&dmabuf->poll.lock);
+ if (dcb->active) {
+ dcb->active |= pevents;
+ events &= ~pevents;
+ } else
+ dcb->active = pevents;
+ spin_unlock_irq(&dmabuf->poll.lock);
+
+ if (events & pevents) {
+ if (!fence_get_rcu(fence_excl)) {
+ /* force a recheck */
+ events &= ~pevents;
+ dma_buf_poll_cb(NULL, &dcb->cb);
+ } else if (!fence_add_callback(fence_excl, &dcb->cb,
+ dma_buf_poll_cb)) {
+ events &= ~pevents;
+ fence_put(fence_excl);
+ } else {
+ /*
+ * No callback queued, wake up any additional
+ * waiters.
+ */
+ fence_put(fence_excl);
+ dma_buf_poll_cb(NULL, &dcb->cb);
+ }
+ }
+ }
+
+ if ((events & POLLOUT) && shared_count > 0) {
+ struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_shared;
+ int i;
+
+ /* Only queue a new callback if no event has fired yet */
+ spin_lock_irq(&dmabuf->poll.lock);
+ if (dcb->active)
+ events &= ~POLLOUT;
+ else
+ dcb->active = POLLOUT;
+ spin_unlock_irq(&dmabuf->poll.lock);
+
+ if (!(events & POLLOUT))
+ goto out;
+
+ for (i = 0; i < shared_count; ++i) {
+ struct fence *fence = rcu_dereference(fobj->shared[i]);
+
+ if (!fence_get_rcu(fence)) {
+ /*
+ * fence refcount dropped to zero, this means
+ * that fobj has been freed
+ *
+ * call dma_buf_poll_cb and force a recheck!
+ */
+ events &= ~POLLOUT;
+ dma_buf_poll_cb(NULL, &dcb->cb);
+ break;
+ }
+ if (!fence_add_callback(fence, &dcb->cb,
+ dma_buf_poll_cb)) {
+ fence_put(fence);
+ events &= ~POLLOUT;
+ break;
+ }
+ fence_put(fence);
+ }
+
+ /* No callback queued, wake up any additional waiters. */
+ if (i == shared_count)
+ dma_buf_poll_cb(NULL, &dcb->cb);
+ }
+
+out:
+ rcu_read_unlock();
+ return events;
+}
+
static const struct file_operations dma_buf_fops = {
.release = dma_buf_release,
.mmap = dma_buf_mmap_internal,
.llseek = dma_buf_llseek,
+ .poll = dma_buf_poll,
};
/*
@@ -128,6 +275,7 @@ static inline int is_dma_buf_file(struct file *file)
* @size: [in] Size of the buffer
* @flags: [in] mode flags for the file.
* @exp_name: [in] name of the exporting module - useful for debugging.
+ * @resv: [in] reservation-object, NULL to allocate default one.
*
* Returns, on success, a newly created dma_buf object, which wraps the
* supplied private data and operations for dma_buf_ops. On either missing
@@ -135,10 +283,17 @@ static inline int is_dma_buf_file(struct file *file)
*
*/
struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
- size_t size, int flags, const char *exp_name)
+ size_t size, int flags, const char *exp_name,
+ struct reservation_object *resv)
{
struct dma_buf *dmabuf;
struct file *file;
+ size_t alloc_size = sizeof(struct dma_buf);
+ if (!resv)
+ alloc_size += sizeof(struct reservation_object);
+ else
+ /* prevent &dma_buf[1] == dma_buf->resv */
+ alloc_size += 1;
if (WARN_ON(!priv || !ops
|| !ops->map_dma_buf
@@ -150,7 +305,7 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
return ERR_PTR(-EINVAL);
}
- dmabuf = kzalloc(sizeof(struct dma_buf), GFP_KERNEL);
+ dmabuf = kzalloc(alloc_size, GFP_KERNEL);
if (dmabuf == NULL)
return ERR_PTR(-ENOMEM);
@@ -158,6 +313,15 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
dmabuf->ops = ops;
dmabuf->size = size;
dmabuf->exp_name = exp_name;
+ init_waitqueue_head(&dmabuf->poll);
+ dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll;
+ dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0;
+
+ if (!resv) {
+ resv = (struct reservation_object *)&dmabuf[1];
+ reservation_object_init(resv);
+ }
+ dmabuf->resv = resv;
file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags);
if (IS_ERR(file)) {