From d9c9998b1624e615225489da1b52fafb70adf7ad Mon Sep 17 00:00:00 2001 From: Chris Goldsworthy Date: Fri, 20 Sep 2019 14:31:04 -0700 Subject: [PATCH] of: reserved_mem: Check that memory reserved in device tree is in use Inside of dma_alloc_attrs(), verify that devices with a reserved memory region are actually using the region, in order to prevent wastage of memory. Change-Id: I56852a761d3253ec2a3a7db0f6501c73983e55c6 Signed-off-by: Chris Goldsworthy --- drivers/of/Kconfig | 10 ++++++++ drivers/of/of_reserved_mem.c | 45 +++++++++++++++++++++++++++++++++ include/linux/of_reserved_mem.h | 9 +++++++ kernel/dma/mapping.c | 3 +++ 4 files changed, 67 insertions(+) diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 37c2ccbefecd..95cf2a8ef03e 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -86,6 +86,16 @@ config OF_RESERVED_MEM depends on OF_EARLY_FLATTREE default y if DMA_DECLARE_COHERENT || DMA_CMA +config OF_RESERVED_MEM_CHECK + bool "Reserved memory registration check" + depends on OF_RESERVED_MEM + help + If a device has a reserved memory region and allocates memory + through the DMA API, check to see if the device and region have been + associated through a call to of_reserved_mem_device_init_by_idx() + before performing an allocation. If the association hasn't occurred, + WARN_ON. + config OF_RESOLVE bool diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 7989703b883c..3027f8d1de72 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -385,6 +385,51 @@ void of_reserved_mem_device_release(struct device *dev) } EXPORT_SYMBOL_GPL(of_reserved_mem_device_release); +#ifdef CONFIG_OF_RESERVED_MEM_CHECK +/** + * of_reserved_mem_device_is_init() - test if a device's reserved memory + * will be used in DMA allocations + * @dev: device we want to perform test on + * + * Check if a device and its reserved memory region (if it has one) have + * been associated with one another through a call to + * of_reserved_mem_device_init_by_idx(). + * + * Return false if the device has reserved memory and it's not in use, true + * otherwise. + */ +bool of_reserved_mem_device_is_init(struct device *dev) +{ + struct device_node *mem_dev_node; + struct rmem_assigned_device *rd; + bool ret = false; + + if (!dev->of_node) + return true; + + mem_dev_node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!mem_dev_node) + return true; + + if (!of_device_is_available(mem_dev_node) || !__find_rmem(mem_dev_node)) + goto exit; + of_node_put(mem_dev_node); + + mutex_lock(&of_rmem_assigned_device_mutex); + list_for_each_entry(rd, &of_rmem_assigned_device_list, list) { + if (rd->dev == dev) { + ret = true; + break; + } + } + mutex_unlock(&of_rmem_assigned_device_mutex); + return ret; +exit: + of_node_put(mem_dev_node); + return true; +} +#endif /* CONFIG_OF_RESERVED_MEM_CHECK */ + /** * of_reserved_mem_lookup() - acquire reserved_mem from a device node * @np: node pointer of the desired reserved-memory region diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h index 60f541912ccf..b97c33975f1d 100644 --- a/include/linux/of_reserved_mem.h +++ b/include/linux/of_reserved_mem.h @@ -35,6 +35,15 @@ int of_reserved_mem_device_init_by_idx(struct device *dev, struct device_node *np, int idx); void of_reserved_mem_device_release(struct device *dev); +#ifdef CONFIG_OF_RESERVED_MEM_CHECK +bool of_reserved_mem_device_is_init(struct device *dev); +#else /* !CONFIG_OF_RESERVED_MEM_CHECK */ +static inline bool of_reserved_mem_device_is_init(struct device *dev) +{ + return true; +} +#endif /* CONFIG_OF_RESERVED_MEM_CHECK */ + void fdt_init_reserved_mem(void); void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size); diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c index 6a3a7287c804..bfbf17ba273b 100644 --- a/kernel/dma/mapping.c +++ b/kernel/dma/mapping.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -284,6 +285,8 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle, WARN_ON_ONCE(!dev->coherent_dma_mask); + WARN_ON(!of_reserved_mem_device_is_init(dev)); + if (dma_alloc_from_dev_coherent(dev, size, dma_handle, &cpu_addr)) return cpu_addr;