Hi all!
This is an updated version of the second attempt to add basic support for dynamic allocation of memory reserved regions defined in device tree.
The initial code for this feature were posted here [1], merged as commit 9d8eab7af79cb4ce2de5de39f82c455b1f796963 ("drivers: of: add initialization code for dma reserved memory") and later reverted by commit 1931ee143b0ab72924944bc06e363d837ba05063. For more information, see [2]. Finally a new bindings has been proposed [3] and Josh Cartwright a few days ago prepared some code which implements those bindings [4]. This finally pushed me again to find some time to finish this task and review the code. Josh agreed to give me the ownership of this series to continue preparing them for mainline inclusion.
For more information please refer to the changlelog below.
[1]: http://lkml.kernel.org/g/1377527959-5080-1-git-send-email-m.szyprowski@samsu... [2]: http://lkml.kernel.org/g/1381476448-14548-1-git-send-email-m.szyprowski@sams... [3]: http://lkml.kernel.org/g/20131030134702.19B57C402A0@trevor.secretlab.ca [4]: http://thread.gmane.org/gmane.linux.documentation/19579
Changelog:
v2: - removed copying of the node name - split shared-dma-pool handling into separate files (one for CMA and one for dma_declare_coherent based implementations) for making the code easier to understand - added support for AMBA devices, changed prototypes to use struct decice instead of struct platform_device - renamed some functions to better match other names used in drivers/of/ - restructured the rest of the code a bit for better readability - added 'reusable' property to exmaple linux,cma node in documentation - exclusive dma (dma_coherent) is used for only handling 'shared-dma-pool' regions without 'reusable' property and CMA is used only for handling 'shared-dma-pool' regions with 'reusable' property.
v1: http://thread.gmane.org/gmane.linux.documentation/19579 - initial version prepared by Josh Cartwright
Summary:
Grant Likely (1): of: document bindings for reserved-memory nodes
Josh Cartwright (2): drivers: of: implement reserved-memory handling for dma drivers: of: implement reserved-memory handling for cma
Marek Szyprowski (2): drivers: of: add initialization code for reserved memory ARM: init: add support for reserved memory defined by device tree
.../bindings/reserved-memory/reserved-memory.txt | 138 ++++++++++++ arch/arm/mm/init.c | 3 + drivers/of/Kconfig | 20 ++ drivers/of/Makefile | 3 + drivers/of/of_reserved_mem.c | 219 ++++++++++++++++++++ drivers/of/of_reserved_mem_cma.c | 75 +++++++ drivers/of/of_reserved_mem_dma.c | 78 +++++++ drivers/of/platform.c | 7 + include/asm-generic/vmlinux.lds.h | 11 + include/linux/of_reserved_mem.h | 62 ++++++ 10 files changed, 616 insertions(+) create mode 100644 Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt create mode 100644 drivers/of/of_reserved_mem.c create mode 100644 drivers/of/of_reserved_mem_cma.c create mode 100644 drivers/of/of_reserved_mem_dma.c create mode 100644 include/linux/of_reserved_mem.h
This patch adds device tree support for contiguous and reserved memory regions defined in device tree.
Large memory blocks can be reliably reserved only during early boot. This must happen before the whole memory management subsystem is initialized, because we need to ensure that the given contiguous blocks are not yet allocated by kernel. Also it must happen before kernel mappings for the whole low memory are created, to ensure that there will be no mappings (for reserved blocks) or mapping with special properties can be created (for CMA blocks). This all happens before device tree structures are unflattened, so we need to get reserved memory layout directly from fdt.
Later, those reserved memory regions are assigned to devices on each device structure initialization.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com [joshc: rework to implement new DT binding, provide mechanism for plugging in new reserved-memory node handlers via RESERVEDMEM_OF_DECLARE] Signed-off-by: Josh Cartwright joshc@codeaurora.org [mszyprow: little code cleanup] Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/of/Kconfig | 6 + drivers/of/Makefile | 1 + drivers/of/of_reserved_mem.c | 219 +++++++++++++++++++++++++++++++++++++ drivers/of/platform.c | 7 ++ include/asm-generic/vmlinux.lds.h | 11 ++ include/linux/of_reserved_mem.h | 62 +++++++++++ 6 files changed, 306 insertions(+) create mode 100644 drivers/of/of_reserved_mem.c create mode 100644 include/linux/of_reserved_mem.h
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index c6973f101a3e..aba13df56f3a 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -75,4 +75,10 @@ config OF_MTD depends on MTD def_bool y
+config OF_RESERVED_MEM + depends on HAVE_MEMBLOCK + def_bool y + help + Helpers to allow for reservation of memory regions + endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index efd05102c405..ed9660adad77 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o +obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c new file mode 100644 index 000000000000..f17cd56e68d9 --- /dev/null +++ b/drivers/of/of_reserved_mem.c @@ -0,0 +1,219 @@ +/* + * Device tree based initialization code for reserved memory. + * + * Copyright (c) 2013, The Linux Foundation. All Rights Reserved. + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Author: Marek Szyprowski m.szyprowski@samsung.com + * Author: Josh Cartwright joshc@codeaurora.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License or (at your optional) any later version of the license. + */ +#include <linux/memblock.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/mm.h> +#include <linux/sizes.h> +#include <linux/of_reserved_mem.h> + +#define MAX_RESERVED_REGIONS 16 +static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; +static int reserved_mem_count; + +int __init of_parse_flat_dt_reg(unsigned long node, const char *uname, + phys_addr_t *base, phys_addr_t *size) +{ + unsigned long len; + __be32 *prop; + + prop = of_get_flat_dt_prop(node, "reg", &len); + if (!prop) + return -EINVAL; + + if (len < (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32)) { + pr_err("Reserved memory: invalid reg property in '%s' node.\n", + uname); + return -EINVAL; + } + + *base = dt_mem_next_cell(dt_root_addr_cells, &prop); + *size = dt_mem_next_cell(dt_root_size_cells, &prop); + return 0; +} + +int __init of_parse_flat_dt_size(unsigned long node, const char *uname, + phys_addr_t *size) +{ + unsigned long len; + __be32 *prop; + + prop = of_get_flat_dt_prop(node, "size", &len); + if (!prop) + return -EINVAL; + + if (len < dt_root_size_cells * sizeof(__be32)) { + pr_err("Reserved memory: invalid size property in '%s' node.\n", + uname); + return -EINVAL; + } + + *size = dt_mem_next_cell(dt_root_size_cells, &prop); + return 0; +} + +static int __init rmem_default_early_setup(struct reserved_mem *rmem, + unsigned long node, + const char *uname) +{ + int err; + + if (of_get_flat_dt_prop(node, "compatible", NULL)) + return -EINVAL; + + err = of_parse_flat_dt_reg(node, uname, &rmem->base, &rmem->size); + if (err) + return err; + + if (of_get_flat_dt_prop(node, "no-map", NULL)) + err = memblock_remove(rmem->base, rmem->size); + else + err = memblock_reserve(rmem->base, rmem->size); + + if (err == 0) + pr_info("Reserved memory: found '%s', memory base %pa, size %ld MiB\n", + uname, &rmem->base, (unsigned long)rmem->size / SZ_1M); + + return err; +} + +static const struct of_device_id rmem_default_id + __used __section(__reservedmem_of_table_end) = { + .data = rmem_default_early_setup, +}; + +static int __init fdt_scan_reserved_mem(unsigned long node, const char *uname, + int depth, void *data) +{ + struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; + extern const struct of_device_id __reservedmem_of_table[]; + const struct of_device_id *id; + const char *status; + + if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { + pr_err("Reserved memory: not enough space all defined regions.\n"); + return -ENOSPC; + } + + status = of_get_flat_dt_prop(node, "status", NULL); + if (status && strcmp(status, "okay") != 0) + return 0; + + /* + * The default handler above ensures this section is terminated with a + * id whose compatible string is empty + */ + for (id = __reservedmem_of_table; id <= &rmem_default_id; id++) { + reservedmem_of_init_fn initfn = id->data; + const char *compat = id->compatible; + + if (compat[0] && !of_flat_dt_is_compatible(node, compat)) + continue; + + if (initfn(rmem, node, uname) == 0) { + pr_info("Reserved memory: created %s node, compatible id %s\n", + uname, compat); + rmem->name = uname; + reserved_mem_count++; + break; + } + } + + return 0; +} + +static struct reserved_mem *find_rmem(struct device_node *np) +{ + const char *name; + unsigned int i; + + name = kbasename(np->full_name); + + for (i = 0; i < reserved_mem_count; i++) + if (strcmp(name, reserved_mem[i].name) == 0) + return &reserved_mem[i]; + + return NULL; +} + +/** + * of_reserved_mem_device_init() - assign reserved memory region to given device + * + * This function assign memory region pointed by "memory-region" device tree + * property to the given device. + */ +void of_reserved_mem_device_init(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct reserved_mem *rmem; + struct of_phandle_args s; + unsigned int i; + + for (i = 0; of_parse_phandle_with_args(np, "memory-region", + "#memory-region-cells", i, &s) == 0; i++) { + + rmem = find_rmem(s.np); + if (!rmem || !rmem->ops || !rmem->ops->device_init) { + of_node_put(s.np); + continue; + } + + rmem->ops->device_init(rmem, dev, &s); + dev_info(dev, "assigned reserved memory node %s\n", + rmem->name); + of_node_put(s.np); + break; + } +} + +/** + * of_reserved_mem_device_release() - release reserved memory device structures + * + * This function releases structures allocated for memory region handling for + * the given device. + */ +void of_reserved_mem_device_release(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct reserved_mem *rmem; + struct of_phandle_args s; + unsigned int i; + + for (i = 0; of_parse_phandle_with_args(np, "memory-region", + "#memory-region-cells", i, &s) == 0; i++) { + + rmem = find_rmem(s.np); + if (rmem && rmem->ops && rmem->ops->device_release) + rmem->ops->device_release(rmem, dev); + + of_node_put(s.np); + } +} + +/** + * early_init_dt_scan_reserved_mem() - create reserved memory regions + * + * This function grabs memory from early allocator for device exclusive use + * defined in device tree structures. It should be called by arch specific code + * once the early allocator (memblock) has been activated and all other + * subsystems have already allocated/reserved memory. + */ +void __init early_init_dt_scan_reserved_mem(void) +{ + of_scan_flat_dt_by_path("/reserved-memory", fdt_scan_reserved_mem, + NULL); +} diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 404d1daebefa..3df0b1826e8b 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -21,6 +21,7 @@ #include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/of_platform.h> +#include <linux/of_reserved_mem.h> #include <linux/platform_device.h>
const struct of_device_id of_default_bus_match_table[] = { @@ -220,6 +221,8 @@ static struct platform_device *of_platform_device_create_pdata( dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data;
+ of_reserved_mem_device_init(&dev->dev); + /* We do not fill the DMA ops for platform devices by default. * This is currently the responsibility of the platform code * to do such, possibly using a device notifier @@ -227,6 +230,7 @@ static struct platform_device *of_platform_device_create_pdata(
if (of_device_add(dev) != 0) { platform_device_put(dev); + of_reserved_mem_device_release(&dev->dev); return NULL; }
@@ -282,6 +286,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node, else of_device_make_bus_id(&dev->dev);
+ of_reserved_mem_device_init(&dev->dev); + /* Allow the HW Peripheral ID to be overridden */ prop = of_get_property(node, "arm,primecell-periphid", NULL); if (prop) @@ -308,6 +314,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node, return dev;
err_free: + of_reserved_mem_device_release(&dev->dev); amba_device_put(dev); return NULL; } diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index bc2121fa9132..f10f64fcc815 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -167,6 +167,16 @@ #define CLK_OF_TABLES() #endif
+#ifdef CONFIG_OF_RESERVED_MEM +#define RESERVEDMEM_OF_TABLES() \ + . = ALIGN(8); \ + VMLINUX_SYMBOL(__reservedmem_of_table) = .; \ + *(__reservedmem_of_table) \ + *(__reservedmem_of_table_end) +#else +#define RESERVEDMEM_OF_TABLES() +#endif + #define KERNEL_DTB() \ STRUCT_ALIGN(); \ VMLINUX_SYMBOL(__dtb_start) = .; \ @@ -490,6 +500,7 @@ TRACE_SYSCALLS() \ MEM_DISCARD(init.rodata) \ CLK_OF_TABLES() \ + RESERVEDMEM_OF_TABLES() \ CLKSRC_OF_TABLES() \ KERNEL_DTB() \ IRQCHIP_OF_MATCH_TABLE() diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h new file mode 100644 index 000000000000..41f43828e1db --- /dev/null +++ b/include/linux/of_reserved_mem.h @@ -0,0 +1,62 @@ +#ifndef __OF_RESERVED_MEM_H +#define __OF_RESERVED_MEM_H + +struct cma; +struct platform_device; +struct of_phandle_args; +struct reserved_mem_ops; + +struct reserved_mem { + const char *name; + const struct reserved_mem_ops *ops; + phys_addr_t base; + phys_addr_t size; + void *priv; +}; + +struct reserved_mem_ops { + void (*device_init)(struct reserved_mem *rmem, + struct device *dev, + struct of_phandle_args *args); + void (*device_release)(struct reserved_mem *rmem, + struct device *dev); +}; + +typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem, + unsigned long node, const char *uname); + +#ifdef CONFIG_OF_RESERVED_MEM +void of_reserved_mem_device_init(struct device *dev); +void of_reserved_mem_device_release(struct device *dev); +void early_init_dt_scan_reserved_mem(void); + +int of_parse_flat_dt_reg(unsigned long node, const char *uname, + phys_addr_t *base, phys_addr_t *size); +int of_parse_flat_dt_size(unsigned long node, const char *uname, + phys_addr_t *size); + +#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ + static const struct of_device_id __reservedmem_of_table_##name \ + __used __section(__reservedmem_of_table) \ + = { .compatible = compat, \ + .data = (init == (reservedmem_of_init_fn)NULL) ? \ + init : init } + +#else +static inline void of_reserved_mem_device_init(struct device *dev) { } + +static inline +void of_reserved_mem_device_release(struct device *dev) { } + +static inline void early_init_dt_scan_reserved_mem(void) { } + +#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ + static const struct of_device_id __reservedmem_of_table_##name \ + __attribute__((unused)) \ + = { .compatible = compat, \ + .data = (init == (reservedmem_of_init_fn)NULL) ? \ + init : init } + +#endif + +#endif /* __OF_RESERVED_MEM_H */
On Tue, 04 Feb 2014 13:09:29 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
This patch adds device tree support for contiguous and reserved memory regions defined in device tree.
Large memory blocks can be reliably reserved only during early boot. This must happen before the whole memory management subsystem is initialized, because we need to ensure that the given contiguous blocks are not yet allocated by kernel. Also it must happen before kernel mappings for the whole low memory are created, to ensure that there will be no mappings (for reserved blocks) or mapping with special properties can be created (for CMA blocks). This all happens before device tree structures are unflattened, so we need to get reserved memory layout directly from fdt.
Later, those reserved memory regions are assigned to devices on each device structure initialization.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com [joshc: rework to implement new DT binding, provide mechanism for plugging in new reserved-memory node handlers via RESERVEDMEM_OF_DECLARE] Signed-off-by: Josh Cartwright joshc@codeaurora.org [mszyprow: little code cleanup] Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
drivers/of/Kconfig | 6 + drivers/of/Makefile | 1 + drivers/of/of_reserved_mem.c | 219 +++++++++++++++++++++++++++++++++++++ drivers/of/platform.c | 7 ++ include/asm-generic/vmlinux.lds.h | 11 ++ include/linux/of_reserved_mem.h | 62 +++++++++++ 6 files changed, 306 insertions(+) create mode 100644 drivers/of/of_reserved_mem.c create mode 100644 include/linux/of_reserved_mem.h
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index c6973f101a3e..aba13df56f3a 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -75,4 +75,10 @@ config OF_MTD depends on MTD def_bool y +config OF_RESERVED_MEM
- depends on HAVE_MEMBLOCK
- def_bool y
- help
Helpers to allow for reservation of memory regions
endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index efd05102c405..ed9660adad77 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o +obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c new file mode 100644 index 000000000000..f17cd56e68d9 --- /dev/null +++ b/drivers/of/of_reserved_mem.c @@ -0,0 +1,219 @@ +/*
- Device tree based initialization code for reserved memory.
- Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
- Copyright (c) 2013 Samsung Electronics Co., Ltd.
http://www.samsung.com
- Author: Marek Szyprowski m.szyprowski@samsung.com
- Author: Josh Cartwright joshc@codeaurora.org
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License or (at your optional) any later version of the license.
- */
+#include <linux/memblock.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/mm.h> +#include <linux/sizes.h> +#include <linux/of_reserved_mem.h>
+#define MAX_RESERVED_REGIONS 16 +static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; +static int reserved_mem_count;
+int __init of_parse_flat_dt_reg(unsigned long node, const char *uname,
phys_addr_t *base, phys_addr_t *size)
Useful utility function; move to drivers/of/fdt.c
+{
- unsigned long len;
- __be32 *prop;
- prop = of_get_flat_dt_prop(node, "reg", &len);
- if (!prop)
return -EINVAL;
- if (len < (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32)) {
pr_err("Reserved memory: invalid reg property in '%s' node.\n",
uname);
return -EINVAL;
- }
This is /okay/ for an initial implementation, but it is naive. While I suggested making #address-cells and #size-cells equal the root node values for the purpose of simplicity, it should still be perfectly valid to have different values if the ranges property is correctly formed.
- *base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- *size = dt_mem_next_cell(dt_root_size_cells, &prop);
Future enhancement; allow for parsing more than just the first reg tuple.
- return 0;
+}
+int __init of_parse_flat_dt_size(unsigned long node, const char *uname,
phys_addr_t *size)
+{
- unsigned long len;
- __be32 *prop;
- prop = of_get_flat_dt_prop(node, "size", &len);
- if (!prop)
return -EINVAL;
- if (len < dt_root_size_cells * sizeof(__be32)) {
pr_err("Reserved memory: invalid size property in '%s' node.\n",
uname);
return -EINVAL;
- }
More than that, the size should be dead on. '==' would be the correct test I think.
- *size = dt_mem_next_cell(dt_root_size_cells, &prop);
- return 0;
+}
+static int __init rmem_default_early_setup(struct reserved_mem *rmem,
unsigned long node,
const char *uname)
+{
- int err;
- if (of_get_flat_dt_prop(node, "compatible", NULL))
return -EINVAL;
- err = of_parse_flat_dt_reg(node, uname, &rmem->base, &rmem->size);
- if (err)
return err;
- if (of_get_flat_dt_prop(node, "no-map", NULL))
err = memblock_remove(rmem->base, rmem->size);
- else
err = memblock_reserve(rmem->base, rmem->size);
This is the only dependency on memblock. Make it an arch hook, and create a default __weak implementation:
#if defined(CONFIG_HAVE_MEMBLOCK) int __init __weak early_init_dt_reserve_memory_arch(u64 base, u64 size, bool nomap) { if (nomap) return memblock_remove(base, size); return memblock_reserve(base, size); } #else int __init __weak early_init_dt_reserve_memory_arch(u64 base, u64 size, bool nomap) { pr_error("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n", base, size, nomap ? " (nomap)" : ""); } #endif
- if (err == 0)
pr_info("Reserved memory: found '%s', memory base %pa, size %ld MiB\n",
uname, &rmem->base, (unsigned long)rmem->size / SZ_1M);
pr_info will be too chatty I think. pr_debug please.
- return err;
+}
+static const struct of_device_id rmem_default_id
- __used __section(__reservedmem_of_table_end) = {
- .data = rmem_default_early_setup,
+};
I was going to say this isn't very type safe and that the only reason to use of_device_id is when using of_match_node() which will iterate a table for you. But reading further I see you're handling type checking in the macro so this is okay.
+static int __init fdt_scan_reserved_mem(unsigned long node, const char *uname,
int depth, void *data)
Move this function to fdt.c. It should be treated as a fundamental part of memory node parsing. It should also always be enabled, regardless of the CONFIG_OF_RESERVED_MEMORY state because the region should always be set aside even when the kernel doesn't know how to use it.
+{
- struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
- extern const struct of_device_id __reservedmem_of_table[];
- const struct of_device_id *id;
- const char *status;
- if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
pr_err("Reserved memory: not enough space all defined regions.\n");
return -ENOSPC;
- }
It is not okay to bail here. If a region is reserved, then the kernel *MUST* honor it, even if drivers cannot make use of it. As a bare minimum it should be removed from the memblock pool.
- status = of_get_flat_dt_prop(node, "status", NULL);
- if (status && strcmp(status, "okay") != 0)
return 0;
- /*
* The default handler above ensures this section is terminated with a
* id whose compatible string is empty
*/
- for (id = __reservedmem_of_table; id <= &rmem_default_id; id++) {
reservedmem_of_init_fn initfn = id->data;
const char *compat = id->compatible;
if (compat[0] && !of_flat_dt_is_compatible(node, compat))
continue;
if (initfn(rmem, node, uname) == 0) {
pr_info("Reserved memory: created %s node, compatible id %s\n",
uname, compat);
rmem->name = uname;
reserved_mem_count++;
break;
}
- }
What would be the condition that a special setup hook is called, but the memory is not removed from the memblock pool? I seems wrong to me that memory blocks wouldn't always get removed from main memory; even if special setup code is executed on them.
By calling out to extra setup functions it means that each one of them needs to implement its own memory removal code and get it correct. There is a strong possibility for bugs there.
- return 0;
+}
+static struct reserved_mem *find_rmem(struct device_node *np) +{
- const char *name;
- unsigned int i;
- name = kbasename(np->full_name);
- for (i = 0; i < reserved_mem_count; i++)
if (strcmp(name, reserved_mem[i].name) == 0)
return &reserved_mem[i];
This looks fragile. It needs to match on the full path, not just kbasename(), but that is expensive. However, all reserved regions should have a phandle. You can use that for matching.
- return NULL;
+}
+/**
- of_reserved_mem_device_init() - assign reserved memory region to given device
- This function assign memory region pointed by "memory-region" device tree
- property to the given device.
- */
+void of_reserved_mem_device_init(struct device *dev) +{
- struct device_node *np = dev->of_node;
- struct reserved_mem *rmem;
- struct of_phandle_args s;
- unsigned int i;
- for (i = 0; of_parse_phandle_with_args(np, "memory-region",
"#memory-region-cells", i, &s) == 0; i++) {
rmem = find_rmem(s.np);
if (!rmem || !rmem->ops || !rmem->ops->device_init) {
of_node_put(s.np);
continue;
}
rmem->ops->device_init(rmem, dev, &s);
dev_info(dev, "assigned reserved memory node %s\n",
rmem->name);
of_node_put(s.np);
break;
- }
+}
+/**
- of_reserved_mem_device_release() - release reserved memory device structures
- This function releases structures allocated for memory region handling for
- the given device.
- */
+void of_reserved_mem_device_release(struct device *dev) +{
- struct device_node *np = dev->of_node;
- struct reserved_mem *rmem;
- struct of_phandle_args s;
- unsigned int i;
- for (i = 0; of_parse_phandle_with_args(np, "memory-region",
"#memory-region-cells", i, &s) == 0; i++) {
rmem = find_rmem(s.np);
if (rmem && rmem->ops && rmem->ops->device_release)
rmem->ops->device_release(rmem, dev);
of_node_put(s.np);
- }
+}
+/**
- early_init_dt_scan_reserved_mem() - create reserved memory regions
- This function grabs memory from early allocator for device exclusive use
- defined in device tree structures. It should be called by arch specific code
- once the early allocator (memblock) has been activated and all other
- subsystems have already allocated/reserved memory.
As commented on patch 4, this should be called by common parsing code, not arch code.
Why do you require other subsystems to allocate/reserve memory before the reserved regions? I would think that the reserved regions declared in the device tree would take precedence.
- */
+void __init early_init_dt_scan_reserved_mem(void) +{
- of_scan_flat_dt_by_path("/reserved-memory", fdt_scan_reserved_mem,
NULL);
+} diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 404d1daebefa..3df0b1826e8b 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -21,6 +21,7 @@ #include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/of_platform.h> +#include <linux/of_reserved_mem.h> #include <linux/platform_device.h> const struct of_device_id of_default_bus_match_table[] = { @@ -220,6 +221,8 @@ static struct platform_device *of_platform_device_create_pdata( dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data;
- of_reserved_mem_device_init(&dev->dev);
- /* We do not fill the DMA ops for platform devices by default.
- This is currently the responsibility of the platform code
- to do such, possibly using a device notifier
@@ -227,6 +230,7 @@ static struct platform_device *of_platform_device_create_pdata( if (of_device_add(dev) != 0) { platform_device_put(dev);
return NULL; }of_reserved_mem_device_release(&dev->dev);
@@ -282,6 +286,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node, else of_device_make_bus_id(&dev->dev);
- of_reserved_mem_device_init(&dev->dev);
- /* Allow the HW Peripheral ID to be overridden */ prop = of_get_property(node, "arm,primecell-periphid", NULL); if (prop)
@@ -308,6 +314,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node, return dev; err_free:
- of_reserved_mem_device_release(&dev->dev); amba_device_put(dev); return NULL;
} diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index bc2121fa9132..f10f64fcc815 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -167,6 +167,16 @@ #define CLK_OF_TABLES() #endif +#ifdef CONFIG_OF_RESERVED_MEM +#define RESERVEDMEM_OF_TABLES() \
- . = ALIGN(8); \
- VMLINUX_SYMBOL(__reservedmem_of_table) = .; \
- *(__reservedmem_of_table) \
- *(__reservedmem_of_table_end)
+#else +#define RESERVEDMEM_OF_TABLES() +#endif
#define KERNEL_DTB() \ STRUCT_ALIGN(); \ VMLINUX_SYMBOL(__dtb_start) = .; \ @@ -490,6 +500,7 @@ TRACE_SYSCALLS() \ MEM_DISCARD(init.rodata) \ CLK_OF_TABLES() \
- RESERVEDMEM_OF_TABLES() \ CLKSRC_OF_TABLES() \ KERNEL_DTB() \ IRQCHIP_OF_MATCH_TABLE()
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h new file mode 100644 index 000000000000..41f43828e1db --- /dev/null +++ b/include/linux/of_reserved_mem.h @@ -0,0 +1,62 @@ +#ifndef __OF_RESERVED_MEM_H +#define __OF_RESERVED_MEM_H
+struct cma; +struct platform_device; +struct of_phandle_args; +struct reserved_mem_ops;
+struct reserved_mem {
- const char *name;
- const struct reserved_mem_ops *ops;
- phys_addr_t base;
- phys_addr_t size;
- void *priv;
+};
+struct reserved_mem_ops {
- void (*device_init)(struct reserved_mem *rmem,
struct device *dev,
struct of_phandle_args *args);
- void (*device_release)(struct reserved_mem *rmem,
struct device *dev);
+};
+typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem,
unsigned long node, const char *uname);
+#ifdef CONFIG_OF_RESERVED_MEM +void of_reserved_mem_device_init(struct device *dev); +void of_reserved_mem_device_release(struct device *dev); +void early_init_dt_scan_reserved_mem(void);
+int of_parse_flat_dt_reg(unsigned long node, const char *uname,
phys_addr_t *base, phys_addr_t *size);
+int of_parse_flat_dt_size(unsigned long node, const char *uname,
phys_addr_t *size);
+#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
- static const struct of_device_id __reservedmem_of_table_##name \
__used __section(__reservedmem_of_table) \
= { .compatible = compat, \
.data = (init == (reservedmem_of_init_fn)NULL) ? \
init : init }
Clever type checking! I haven't known about doing it that way.
+#else +static inline void of_reserved_mem_device_init(struct device *dev) { }
+static inline +void of_reserved_mem_device_release(struct device *dev) { }
+static inline void early_init_dt_scan_reserved_mem(void) { }
+#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
- static const struct of_device_id __reservedmem_of_table_##name \
__attribute__((unused)) \
= { .compatible = compat, \
.data = (init == (reservedmem_of_init_fn)NULL) ? \
init : init }
+#endif
+#endif /* __OF_RESERVED_MEM_H */
1.7.9.5
Hello,
On 2014-02-05 12:05, Grant Likely wrote:
On Tue, 04 Feb 2014 13:09:29 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
This patch adds device tree support for contiguous and reserved memory regions defined in device tree.
Large memory blocks can be reliably reserved only during early boot. This must happen before the whole memory management subsystem is initialized, because we need to ensure that the given contiguous blocks are not yet allocated by kernel. Also it must happen before kernel mappings for the whole low memory are created, to ensure that there will be no mappings (for reserved blocks) or mapping with special properties can be created (for CMA blocks). This all happens before device tree structures are unflattened, so we need to get reserved memory layout directly from fdt.
Later, those reserved memory regions are assigned to devices on each device structure initialization.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com [joshc: rework to implement new DT binding, provide mechanism for plugging in new reserved-memory node handlers via RESERVEDMEM_OF_DECLARE] Signed-off-by: Josh Cartwright joshc@codeaurora.org [mszyprow: little code cleanup] Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
drivers/of/Kconfig | 6 + drivers/of/Makefile | 1 + drivers/of/of_reserved_mem.c | 219 +++++++++++++++++++++++++++++++++++++ drivers/of/platform.c | 7 ++ include/asm-generic/vmlinux.lds.h | 11 ++ include/linux/of_reserved_mem.h | 62 +++++++++++ 6 files changed, 306 insertions(+) create mode 100644 drivers/of/of_reserved_mem.c create mode 100644 include/linux/of_reserved_mem.h
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index c6973f101a3e..aba13df56f3a 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -75,4 +75,10 @@ config OF_MTD depends on MTD def_bool y
+config OF_RESERVED_MEM
- depends on HAVE_MEMBLOCK
- def_bool y
- help
Helpers to allow for reservation of memory regions
endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index efd05102c405..ed9660adad77 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o +obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c new file mode 100644 index 000000000000..f17cd56e68d9 --- /dev/null +++ b/drivers/of/of_reserved_mem.c @@ -0,0 +1,219 @@ +/*
- Device tree based initialization code for reserved memory.
- Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
- Copyright (c) 2013 Samsung Electronics Co., Ltd.
http://www.samsung.com
- Author: Marek Szyprowski m.szyprowski@samsung.com
- Author: Josh Cartwright joshc@codeaurora.org
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License or (at your optional) any later version of the license.
- */
+#include <linux/memblock.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/mm.h> +#include <linux/sizes.h> +#include <linux/of_reserved_mem.h>
+#define MAX_RESERVED_REGIONS 16 +static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; +static int reserved_mem_count;
+int __init of_parse_flat_dt_reg(unsigned long node, const char *uname,
phys_addr_t *base, phys_addr_t *size)
Useful utility function; move to drivers/of/fdt.c
+{
- unsigned long len;
- __be32 *prop;
- prop = of_get_flat_dt_prop(node, "reg", &len);
- if (!prop)
return -EINVAL;
- if (len < (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32)) {
pr_err("Reserved memory: invalid reg property in '%s' node.\n",
uname);
return -EINVAL;
- }
This is /okay/ for an initial implementation, but it is naive. While I suggested making #address-cells and #size-cells equal the root node values for the purpose of simplicity, it should still be perfectly valid to have different values if the ranges property is correctly formed.
- *base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- *size = dt_mem_next_cell(dt_root_size_cells, &prop);
Future enhancement; allow for parsing more than just the first reg tuple.
One more question. Does it really makes any sense to support more than one tuple for reg property? For consistency we should also allow more than one entry in size, align and alloc-ranges property, but I don't see any benefits for defining more than one range for a single region. Same can be achieved by defining more regions instead if one really needs such configuration.
Best regards
On Tue, 11 Feb 2014 12:45:50 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
Hello,
On 2014-02-05 12:05, Grant Likely wrote:
On Tue, 04 Feb 2014 13:09:29 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
This patch adds device tree support for contiguous and reserved memory regions defined in device tree.
Large memory blocks can be reliably reserved only during early boot. This must happen before the whole memory management subsystem is initialized, because we need to ensure that the given contiguous blocks are not yet allocated by kernel. Also it must happen before kernel mappings for the whole low memory are created, to ensure that there will be no mappings (for reserved blocks) or mapping with special properties can be created (for CMA blocks). This all happens before device tree structures are unflattened, so we need to get reserved memory layout directly from fdt.
Later, those reserved memory regions are assigned to devices on each device structure initialization.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com [joshc: rework to implement new DT binding, provide mechanism for plugging in new reserved-memory node handlers via RESERVEDMEM_OF_DECLARE] Signed-off-by: Josh Cartwright joshc@codeaurora.org [mszyprow: little code cleanup] Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
drivers/of/Kconfig | 6 + drivers/of/Makefile | 1 + drivers/of/of_reserved_mem.c | 219 +++++++++++++++++++++++++++++++++++++ drivers/of/platform.c | 7 ++ include/asm-generic/vmlinux.lds.h | 11 ++ include/linux/of_reserved_mem.h | 62 +++++++++++ 6 files changed, 306 insertions(+) create mode 100644 drivers/of/of_reserved_mem.c create mode 100644 include/linux/of_reserved_mem.h
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index c6973f101a3e..aba13df56f3a 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -75,4 +75,10 @@ config OF_MTD depends on MTD def_bool y
+config OF_RESERVED_MEM
- depends on HAVE_MEMBLOCK
- def_bool y
- help
Helpers to allow for reservation of memory regions
endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index efd05102c405..ed9660adad77 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o +obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c new file mode 100644 index 000000000000..f17cd56e68d9 --- /dev/null +++ b/drivers/of/of_reserved_mem.c @@ -0,0 +1,219 @@ +/*
- Device tree based initialization code for reserved memory.
- Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
- Copyright (c) 2013 Samsung Electronics Co., Ltd.
http://www.samsung.com
- Author: Marek Szyprowski m.szyprowski@samsung.com
- Author: Josh Cartwright joshc@codeaurora.org
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License or (at your optional) any later version of the license.
- */
+#include <linux/memblock.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/mm.h> +#include <linux/sizes.h> +#include <linux/of_reserved_mem.h>
+#define MAX_RESERVED_REGIONS 16 +static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; +static int reserved_mem_count;
+int __init of_parse_flat_dt_reg(unsigned long node, const char *uname,
phys_addr_t *base, phys_addr_t *size)
Useful utility function; move to drivers/of/fdt.c
+{
- unsigned long len;
- __be32 *prop;
- prop = of_get_flat_dt_prop(node, "reg", &len);
- if (!prop)
return -EINVAL;
- if (len < (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32)) {
pr_err("Reserved memory: invalid reg property in '%s' node.\n",
uname);
return -EINVAL;
- }
This is /okay/ for an initial implementation, but it is naive. While I suggested making #address-cells and #size-cells equal the root node values for the purpose of simplicity, it should still be perfectly valid to have different values if the ranges property is correctly formed.
- *base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- *size = dt_mem_next_cell(dt_root_size_cells, &prop);
Future enhancement; allow for parsing more than just the first reg tuple.
One more question. Does it really makes any sense to support more than one tuple for reg property? For consistency we should also allow more than one entry in size, align and alloc-ranges property, but I don't see any benefits for defining more than one range for a single region. Same can be achieved by defining more regions instead if one really needs such configuration.
Yes, if only because it is an define usage of the reg property. If a devtree has multiple tuples in reg, then all of those tuples should be treated as reserved, even if the kernel doesn't know how to use them.
I would not do the same for size/align/alloc-ranges unless there is a very specific use case that you can define. These ones are different from the static regions because they aren't ever used to protect something that already exists in the memory.
g.
Hi,
On 11.02.2014 13:13, Grant Likely wrote:
On Tue, 11 Feb 2014 12:45:50 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
Hello,
On 2014-02-05 12:05, Grant Likely wrote:
On Tue, 04 Feb 2014 13:09:29 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
This patch adds device tree support for contiguous and reserved memory regions defined in device tree.
Large memory blocks can be reliably reserved only during early boot. This must happen before the whole memory management subsystem is initialized, because we need to ensure that the given contiguous blocks are not yet allocated by kernel. Also it must happen before kernel mappings for the whole low memory are created, to ensure that there will be no mappings (for reserved blocks) or mapping with special properties can be created (for CMA blocks). This all happens before device tree structures are unflattened, so we need to get reserved memory layout directly from fdt.
Later, those reserved memory regions are assigned to devices on each device structure initialization.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com [joshc: rework to implement new DT binding, provide mechanism for plugging in new reserved-memory node handlers via RESERVEDMEM_OF_DECLARE] Signed-off-by: Josh Cartwright joshc@codeaurora.org [mszyprow: little code cleanup] Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
drivers/of/Kconfig | 6 + drivers/of/Makefile | 1 + drivers/of/of_reserved_mem.c | 219 +++++++++++++++++++++++++++++++++++++ drivers/of/platform.c | 7 ++ include/asm-generic/vmlinux.lds.h | 11 ++ include/linux/of_reserved_mem.h | 62 +++++++++++ 6 files changed, 306 insertions(+) create mode 100644 drivers/of/of_reserved_mem.c create mode 100644 include/linux/of_reserved_mem.h
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index c6973f101a3e..aba13df56f3a 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -75,4 +75,10 @@ config OF_MTD depends on MTD def_bool y
+config OF_RESERVED_MEM
- depends on HAVE_MEMBLOCK
- def_bool y
- help
Helpers to allow for reservation of memory regions
- endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile index efd05102c405..ed9660adad77 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o +obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c new file mode 100644 index 000000000000..f17cd56e68d9 --- /dev/null +++ b/drivers/of/of_reserved_mem.c @@ -0,0 +1,219 @@ +/*
- Device tree based initialization code for reserved memory.
- Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
- Copyright (c) 2013 Samsung Electronics Co., Ltd.
http://www.samsung.com
- Author: Marek Szyprowski m.szyprowski@samsung.com
- Author: Josh Cartwright joshc@codeaurora.org
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License or (at your optional) any later version of the license.
- */
+#include <linux/memblock.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/mm.h> +#include <linux/sizes.h> +#include <linux/of_reserved_mem.h>
+#define MAX_RESERVED_REGIONS 16 +static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; +static int reserved_mem_count;
+int __init of_parse_flat_dt_reg(unsigned long node, const char *uname,
phys_addr_t *base, phys_addr_t *size)
Useful utility function; move to drivers/of/fdt.c
+{
- unsigned long len;
- __be32 *prop;
- prop = of_get_flat_dt_prop(node, "reg", &len);
- if (!prop)
return -EINVAL;
- if (len < (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32)) {
pr_err("Reserved memory: invalid reg property in '%s' node.\n",
uname);
return -EINVAL;
- }
This is /okay/ for an initial implementation, but it is naive. While I suggested making #address-cells and #size-cells equal the root node values for the purpose of simplicity, it should still be perfectly valid to have different values if the ranges property is correctly formed.
- *base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- *size = dt_mem_next_cell(dt_root_size_cells, &prop);
Future enhancement; allow for parsing more than just the first reg tuple.
One more question. Does it really makes any sense to support more than one tuple for reg property? For consistency we should also allow more than one entry in size, align and alloc-ranges property, but I don't see any benefits for defining more than one range for a single region. Same can be achieved by defining more regions instead if one really needs such configuration.
Yes, if only because it is an define usage of the reg property. If a devtree has multiple tuples in reg, then all of those tuples should be treated as reserved, even if the kernel doesn't know how to use them.
I would not do the same for size/align/alloc-ranges unless there is a very specific use case that you can define. These ones are different from the static regions because they aren't ever used to protect something that already exists in the memory.
Is there a reason why multiple regions could not be used for this purpose, instead of adding extra complexity of having multiple reg entries per region?
I.e. I don't see a difference between
reg1: region@00000000 { reg = <0x00000000 0x1000>; };
reg2: region@10000000 { reg = <0x10000000 0x1000>; };
user { regions = <®1>, <®2>; };
and
reg: region@00000000 { reg = <0x00000000 0x1000>, <0x10000000 0x1000>; };
user { regions = <®>; };
except that the former IMHO better suits the definition of memory region, which I see as a single contiguous range of memory and can be simplified to have a single reg entry per region.
Best regards, Tomasz
On Tue, 11 Feb 2014 15:29:54 +0100, Tomasz Figa t.figa@samsung.com wrote:
Yes, if only because it is an define usage of the reg property. If a devtree has multiple tuples in reg, then all of those tuples should be treated as reserved, even if the kernel doesn't know how to use them.
I would not do the same for size/align/alloc-ranges unless there is a very specific use case that you can define. These ones are different from the static regions because they aren't ever used to protect something that already exists in the memory.
Is there a reason why multiple regions could not be used for this purpose, instead of adding extra complexity of having multiple reg entries per region?
I.e. I don't see a difference between
reg1: region@00000000 { reg = <0x00000000 0x1000>; };
reg2: region@10000000 { reg = <0x10000000 0x1000>; };
user { regions = <®1>, <®2>; };
and
reg: region@00000000 { reg = <0x00000000 0x1000>, <0x10000000 0x1000>; };
user { regions = <®>; };
except that the former IMHO better suits the definition of memory region, which I see as a single contiguous range of memory and can be simplified to have a single reg entry per region.
My point is rather if multiple reg tuples are found in a reserved memory node, the kernel must respect them and reserve the memory. I'm not arguing about whether or not that makes for a good binding.
g.
On Tue, 2014-02-11 at 19:01 +0000, Grant Likely wrote:
except that the former IMHO better suits the definition of memory region, which I see as a single contiguous range of memory and can be simplified to have a single reg entry per region.
My point is rather if multiple reg tuples are found in a reserved memory node, the kernel must respect them and reserve the memory. I'm not arguing about whether or not that makes for a good binding.
agreed.
Cheers, Ben.
On 11.02.2014 21:02, Benjamin Herrenschmidt wrote:
On Tue, 2014-02-11 at 19:01 +0000, Grant Likely wrote:
except that the former IMHO better suits the definition of memory region, which I see as a single contiguous range of memory and can be simplified to have a single reg entry per region.
My point is rather if multiple reg tuples are found in a reserved memory node, the kernel must respect them and reserve the memory. I'm not arguing about whether or not that makes for a good binding.
agreed.
My point is why, if the binding defines that just a single tuple should be provided.
Best regards, Tomasz
On Tue, Feb 11, 2014 at 09:04:21PM +0100, Tomasz Figa wrote:
On 11.02.2014 21:02, Benjamin Herrenschmidt wrote:
On Tue, 2014-02-11 at 19:01 +0000, Grant Likely wrote:
except that the former IMHO better suits the definition of memory region, which I see as a single contiguous range of memory and can be simplified to have a single reg entry per region.
My point is rather if multiple reg tuples are found in a reserved memory node, the kernel must respect them and reserve the memory. I'm not arguing about whether or not that makes for a good binding.
agreed.
My point is why, if the binding defines that just a single tuple should be provided.
FWIW, the usecase I had mentioned in reply to Grant in the patch 5/5 thread [1] could make use of this. The shared memory region is split into a main chunk and several "auxiliary" chunk, but collectively these regions all share the same heap state.
Josh
1: http://lkml.kernel.org/r/20140205192502.GO20228@joshc.qualcomm.com
On 11.02.2014 21:19, Josh Cartwright wrote:
On Tue, Feb 11, 2014 at 09:04:21PM +0100, Tomasz Figa wrote:
On 11.02.2014 21:02, Benjamin Herrenschmidt wrote:
On Tue, 2014-02-11 at 19:01 +0000, Grant Likely wrote:
except that the former IMHO better suits the definition of memory region, which I see as a single contiguous range of memory and can be simplified to have a single reg entry per region.
My point is rather if multiple reg tuples are found in a reserved memory node, the kernel must respect them and reserve the memory. I'm not arguing about whether or not that makes for a good binding.
agreed.
My point is why, if the binding defines that just a single tuple should be provided.
FWIW, the usecase I had mentioned in reply to Grant in the patch 5/5 thread [1] could make use of this. The shared memory region is split into a main chunk and several "auxiliary" chunk, but collectively these regions all share the same heap state.
Josh
1: http://lkml.kernel.org/r/20140205192502.GO20228@joshc.qualcomm.com
The use case seems fine, but I believe it could be properly represented in device tree using multiple single-reg regions as well, unless the consumer can request a block of memory that crosses boundary of two sub-regions specified by reg entries of single region.
Best regards, Tomasz
On Tue, Feb 11, 2014 at 09:27:36PM +0100, Tomasz Figa wrote:
On 11.02.2014 21:19, Josh Cartwright wrote:
On Tue, Feb 11, 2014 at 09:04:21PM +0100, Tomasz Figa wrote:
On 11.02.2014 21:02, Benjamin Herrenschmidt wrote:
On Tue, 2014-02-11 at 19:01 +0000, Grant Likely wrote:
except that the former IMHO better suits the definition of memory region, which I see as a single contiguous range of memory and can be simplified to have a single reg entry per region.
My point is rather if multiple reg tuples are found in a reserved memory node, the kernel must respect them and reserve the memory. I'm not arguing about whether or not that makes for a good binding.
agreed.
My point is why, if the binding defines that just a single tuple should be provided.
FWIW, the usecase I had mentioned in reply to Grant in the patch 5/5 thread [1] could make use of this. The shared memory region is split into a main chunk and several "auxiliary" chunk, but collectively these regions all share the same heap state.
Josh
1: http://lkml.kernel.org/r/20140205192502.GO20228@joshc.qualcomm.com
The use case seems fine, but I believe it could be properly represented in device tree using multiple single-reg regions as well, unless the consumer can request a block of memory that crosses boundary of two sub-regions specified by reg entries of single region.
I could probably make a only-one-reg-entry policy work for me, but it makes things a bit more awkward. I'd lose the ability to describe "this set of regions need to be logically handled together" directly in the reserved memory node, and would need to push it up a layer.
reserved-memory { smem: smem { reg = <...>; }; aux1: auxiliary1 { reg = <...>; }; aux2: auxiliary2 { reg = <...>; }; ... };
heap : heap { compatible = "qcom,shared-memory"; memory-region = <&smem &aux1 &aux2>; #smem-cells = <2>; };
actual_consumer1 { compatible = "..."; smem = <&heap IDENTIFIER1 0x1000>; };
actual_consumer2 { compatible = "..."; smem = <&heap IDENTIFIER2 0x1000>; };
Maybe that's better off, I don't know. This would also eliminate my need for a #memory-region-cells property.
Thanks, Josh
On Thu, 13 Feb 2014 13:48:40 -0600, Josh Cartwright joshc@codeaurora.org wrote:
On Tue, Feb 11, 2014 at 09:27:36PM +0100, Tomasz Figa wrote:
On 11.02.2014 21:19, Josh Cartwright wrote:
On Tue, Feb 11, 2014 at 09:04:21PM +0100, Tomasz Figa wrote:
On 11.02.2014 21:02, Benjamin Herrenschmidt wrote:
On Tue, 2014-02-11 at 19:01 +0000, Grant Likely wrote:
> except that the former IMHO better suits the definition of memory > region, which I see as a single contiguous range of memory and can be > simplified to have a single reg entry per region.
My point is rather if multiple reg tuples are found in a reserved memory node, the kernel must respect them and reserve the memory. I'm not arguing about whether or not that makes for a good binding.
agreed.
My point is why, if the binding defines that just a single tuple should be provided.
FWIW, the usecase I had mentioned in reply to Grant in the patch 5/5 thread [1] could make use of this. The shared memory region is split into a main chunk and several "auxiliary" chunk, but collectively these regions all share the same heap state.
Josh
1: http://lkml.kernel.org/r/20140205192502.GO20228@joshc.qualcomm.com
The use case seems fine, but I believe it could be properly represented in device tree using multiple single-reg regions as well, unless the consumer can request a block of memory that crosses boundary of two sub-regions specified by reg entries of single region.
I could probably make a only-one-reg-entry policy work for me, but it makes things a bit more awkward. I'd lose the ability to describe "this set of regions need to be logically handled together" directly in the reserved memory node, and would need to push it up a layer.
reserved-memory { smem: smem { reg = <...>; }; aux1: auxiliary1 { reg = <...>; }; aux2: auxiliary2 { reg = <...>; }; ... };
If the regions are used for different purposes, it makes sense I think to have a separate node for each. Multiple tuples would make more sense for something like valid DMA regions for a broken device that can only DMA into a few windows; you could have one tuple per window within a single node.
It would be possible to collect multiple associated nodes under one parent node which in turn has reserved-memory for its parent:
reserved-memory { ranges; reserved-group { ranges; smem: smem { reg = <...>; }; aux1: auxiliary1 { reg = <...>; }; aux2: auxiliary2 { reg = <...>; }; }; ... };
g.
On Tue, 11 Feb 2014 21:04:21 +0100, Tomasz Figa tomasz.figa@gmail.com wrote:
On 11.02.2014 21:02, Benjamin Herrenschmidt wrote:
On Tue, 2014-02-11 at 19:01 +0000, Grant Likely wrote:
except that the former IMHO better suits the definition of memory region, which I see as a single contiguous range of memory and can be simplified to have a single reg entry per region.
My point is rather if multiple reg tuples are found in a reserved memory node, the kernel must respect them and reserve the memory. I'm not arguing about whether or not that makes for a good binding.
agreed.
My point is why, if the binding defines that just a single tuple should be provided.
It's irrelevant because it gets processed at a different level. It's important that the core early setup code can quickly parse all the reserved regions without having any idea what the end-user binding is. Multiple reg tuples is just fine in this regard.
Whether or not multiple tuples makes sense is defined for each particular use case by the driver actually using it without impacting core initialization code one iota.
g.
Hi,
On 2/4/2014 4:09 AM, Marek Szyprowski wrote: ...
diff --git a/drivers/of/Makefile b/drivers/of/Makefile index efd05102c405..ed9660adad77 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o +obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c new file mode 100644 index 000000000000..f17cd56e68d9 --- /dev/null +++ b/drivers/of/of_reserved_mem.c @@ -0,0 +1,219 @@ +/*
- Device tree based initialization code for reserved memory.
- Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
- Copyright (c) 2013 Samsung Electronics Co., Ltd.
http://www.samsung.com
- Author: Marek Szyprowski m.szyprowski@samsung.com
- Author: Josh Cartwright joshc@codeaurora.org
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License or (at your optional) any later version of the license.
- */
+#include <linux/memblock.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/mm.h> +#include <linux/sizes.h> +#include <linux/of_reserved_mem.h>
+#define MAX_RESERVED_REGIONS 16
Make this a config option?
+static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; +static int reserved_mem_count;
+int __init of_parse_flat_dt_reg(unsigned long node, const char *uname,
phys_addr_t *base, phys_addr_t *size)
+{
- unsigned long len;
- __be32 *prop;
- prop = of_get_flat_dt_prop(node, "reg", &len);
- if (!prop)
return -EINVAL;
- if (len < (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32)) {
pr_err("Reserved memory: invalid reg property in '%s' node.\n",
uname);
return -EINVAL;
- }
- *base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- *size = dt_mem_next_cell(dt_root_size_cells, &prop);
- return 0;
+}
+int __init of_parse_flat_dt_size(unsigned long node, const char *uname,
phys_addr_t *size)
+{
- unsigned long len;
- __be32 *prop;
- prop = of_get_flat_dt_prop(node, "size", &len);
- if (!prop)
return -EINVAL;
- if (len < dt_root_size_cells * sizeof(__be32)) {
pr_err("Reserved memory: invalid size property in '%s' node.\n",
uname);
return -EINVAL;
- }
- *size = dt_mem_next_cell(dt_root_size_cells, &prop);
- return 0;
+}
+static int __init rmem_default_early_setup(struct reserved_mem *rmem,
unsigned long node,
const char *uname)
+{
- int err;
- if (of_get_flat_dt_prop(node, "compatible", NULL))
return -EINVAL;
- err = of_parse_flat_dt_reg(node, uname, &rmem->base, &rmem->size);
- if (err)
return err;
- if (of_get_flat_dt_prop(node, "no-map", NULL))
err = memblock_remove(rmem->base, rmem->size);
- else
err = memblock_reserve(rmem->base, rmem->size);
The CMA code aligns to pageblock size, do we need to do something similar here as well? With reserved it might not be much of an issue but we've hit issues before with non-pageblock aligned memblock_removed memory because the page structures are not initialized.
- if (err == 0)
pr_info("Reserved memory: found '%s', memory base %pa, size %ld MiB\n",
uname, &rmem->base, (unsigned long)rmem->size / SZ_1M);
- return err;
+}
+static const struct of_device_id rmem_default_id
- __used __section(__reservedmem_of_table_end) = {
- .data = rmem_default_early_setup,
+};
+static int __init fdt_scan_reserved_mem(unsigned long node, const char *uname,
int depth, void *data)
+{
- struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
- extern const struct of_device_id __reservedmem_of_table[];
- const struct of_device_id *id;
- const char *status;
- if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
pr_err("Reserved memory: not enough space all defined regions.\n");
return -ENOSPC;
- }
- status = of_get_flat_dt_prop(node, "status", NULL);
- if (status && strcmp(status, "okay") != 0)
return 0;
strncmp(status, "ok", 2) to handle alternate spellings
Thanks, Laura
From: Josh Cartwright joshc@codeaurora.org
Add support for handling 'shared-dma-pool' reserved-memory nodes using dma exclusive driver (dma_alloc_coherent()).
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Josh Cartwright joshc@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/of/Kconfig | 7 ++++ drivers/of/Makefile | 1 + drivers/of/of_reserved_mem_dma.c | 78 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 drivers/of/of_reserved_mem_dma.c
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index aba13df56f3a..7ac330473ec9 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -81,4 +81,11 @@ config OF_RESERVED_MEM help Helpers to allow for reservation of memory regions
+config OF_RESERVED_MEM_DMA + depends on OF_RESERVED_MEM + depends on HAVE_GENERIC_DMA_COHERENT + def_bool y + help + Helpers for reserving memory regions for DMA use + endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index ed9660adad77..6142227ca854 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o +obj-$(CONFIG_OF_RESERVED_MEM_DMA) += of_reserved_mem_dma.o diff --git a/drivers/of/of_reserved_mem_dma.c b/drivers/of/of_reserved_mem_dma.c new file mode 100644 index 000000000000..53a4cac6084a --- /dev/null +++ b/drivers/of/of_reserved_mem_dma.c @@ -0,0 +1,78 @@ +/* + * Device tree based initialization code for DMA reserved regions. + * + * Copyright (c) 2013, The Linux Foundation. All Rights Reserved. + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Author: Marek Szyprowski m.szyprowski@samsung.com + * Author: Josh Cartwright joshc@codeaurora.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License or (at your optional) any later version of the license. + */ +#include <linux/memblock.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/mm.h> +#include <linux/sizes.h> +#include <linux/mm_types.h> +#include <linux/dma-mapping.h> +#include <linux/of_reserved_mem.h> + +static void rmem_dma_device_init(struct reserved_mem *rmem, + struct device *dev, + struct of_phandle_args *args) +{ + dma_declare_coherent_memory(dev, rmem->base, rmem->base, + rmem->size, DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); +} + +static void rmem_dma_device_release(struct reserved_mem *rmem, + struct device *dev) +{ + dma_release_declared_memory(dev); +} + +static const struct reserved_mem_ops rmem_dma_ops = { + .device_init = rmem_dma_device_init, + .device_release = rmem_dma_device_release, +}; + +static int __init rmem_dma_setup(struct reserved_mem *rmem, + unsigned long node, + const char *uname) +{ + int err; + + if (of_get_flat_dt_prop(node, "reusable", NULL)) + return -EINVAL; + + err = of_parse_flat_dt_reg(node, uname, &rmem->base, &rmem->size); + if (!err) + goto out_done; + + err = of_parse_flat_dt_size(node, uname, &rmem->size); + if (err) + goto out_err; + + rmem->base = memblock_alloc_base(rmem->size, PAGE_SIZE, + MEMBLOCK_ALLOC_ANYWHERE); + memblock_free(rmem->base, rmem->size); + +out_done: + rmem->ops = &rmem_dma_ops; + pr_info("Reserved memory: created DMA memory pool at %pa, size %ld MiB\n", + &rmem->base, (unsigned long)rmem->size / SZ_1M); + + return memblock_remove(rmem->base, rmem->size); + +out_err: + pr_err("Reserved memory: unable to setup '%s' memory region for DMA.\n", + uname); + return err; +} +RESERVEDMEM_OF_DECLARE(dma, "shared-dma-pool", rmem_dma_setup);
From: Josh Cartwright joshc@codeaurora.org
Add support for handling 'shared-dma-pool' reserved-memory nodes using Contiguous Memory Allocator driver.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Josh Cartwright joshc@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/of/Kconfig | 7 ++++ drivers/of/Makefile | 1 + drivers/of/of_reserved_mem_cma.c | 75 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 drivers/of/of_reserved_mem_cma.c
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 7ac330473ec9..5dd3a80910d2 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -81,6 +81,13 @@ config OF_RESERVED_MEM help Helpers to allow for reservation of memory regions
+config OF_RESERVED_MEM_CMA + depends on OF_RESERVED_MEM + depends on DMA_CMA + def_bool y + help + Helpers for reserving memory regions for DMA use + config OF_RESERVED_MEM_DMA depends on OF_RESERVED_MEM depends on HAVE_GENERIC_DMA_COHERENT diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 6142227ca854..49b9078637b8 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -10,4 +10,5 @@ obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o +obj-$(CONFIG_OF_RESERVED_MEM_CMA) += of_reserved_mem_cma.o obj-$(CONFIG_OF_RESERVED_MEM_DMA) += of_reserved_mem_dma.o diff --git a/drivers/of/of_reserved_mem_cma.c b/drivers/of/of_reserved_mem_cma.c new file mode 100644 index 000000000000..601d80655102 --- /dev/null +++ b/drivers/of/of_reserved_mem_cma.c @@ -0,0 +1,75 @@ +/* + * Device tree based initialization code for DMA reserved regions. + * + * Copyright (c) 2013, The Linux Foundation. All Rights Reserved. + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Author: Marek Szyprowski m.szyprowski@samsung.com + * Author: Josh Cartwright joshc@codeaurora.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License or (at your optional) any later version of the license. + */ +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/mm.h> +#include <linux/sizes.h> +#include <linux/mm_types.h> +#include <linux/dma-contiguous.h> +#include <linux/of_reserved_mem.h> + +static void rmem_cma_device_init(struct reserved_mem *rmem, + struct device *dev, + struct of_phandle_args *args) +{ + struct cma *cma = rmem->priv; + dev_set_cma_area(dev, cma); +} + +static const struct reserved_mem_ops rmem_cma_ops = { + .device_init = rmem_cma_device_init, +}; + +static int __init rmem_cma_setup(struct reserved_mem *rmem, + unsigned long node, + const char *uname) +{ + struct cma *cma; + int err; + + if (!of_get_flat_dt_prop(node, "reusable", NULL)) + return -EINVAL; + + err = of_parse_flat_dt_reg(node, uname, &rmem->base, &rmem->size); + if (!err) + goto out_done; + + rmem->base = 0; + err = of_parse_flat_dt_size(node, uname, &rmem->size); + if (err) + goto out_err; + +out_done: + err = dma_contiguous_reserve_area(rmem->size, rmem->base, 0, + &cma); + if (err) { + pr_err("Reserved memory: unable to setup CMA region\n"); + return err; + } + + if (of_get_flat_dt_prop(node, "linux,cma-default", NULL)) + dma_contiguous_set_default(cma); + + rmem->ops = &rmem_cma_ops; + rmem->priv = cma; + + return 0; +out_err: + pr_err("Reseved memory: malformed node '%s'.\n", uname); + return err; +} +RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup);
On Tue, 04 Feb 2014 13:09:31 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
From: Josh Cartwright joshc@codeaurora.org
Add support for handling 'shared-dma-pool' reserved-memory nodes using Contiguous Memory Allocator driver.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Josh Cartwright joshc@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
drivers/of/Kconfig | 7 ++++ drivers/of/Makefile | 1 + drivers/of/of_reserved_mem_cma.c | 75 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 drivers/of/of_reserved_mem_cma.c
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 7ac330473ec9..5dd3a80910d2 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -81,6 +81,13 @@ config OF_RESERVED_MEM help Helpers to allow for reservation of memory regions +config OF_RESERVED_MEM_CMA
- depends on OF_RESERVED_MEM
- depends on DMA_CMA
- def_bool y
- help
Helpers for reserving memory regions for DMA use
config OF_RESERVED_MEM_DMA depends on OF_RESERVED_MEM depends on HAVE_GENERIC_DMA_COHERENT diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 6142227ca854..49b9078637b8 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -10,4 +10,5 @@ obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o +obj-$(CONFIG_OF_RESERVED_MEM_CMA) += of_reserved_mem_cma.o obj-$(CONFIG_OF_RESERVED_MEM_DMA) += of_reserved_mem_dma.o diff --git a/drivers/of/of_reserved_mem_cma.c b/drivers/of/of_reserved_mem_cma.c new file mode 100644 index 000000000000..601d80655102 --- /dev/null +++ b/drivers/of/of_reserved_mem_cma.c @@ -0,0 +1,75 @@ +/*
- Device tree based initialization code for DMA reserved regions.
- Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
- Copyright (c) 2013 Samsung Electronics Co., Ltd.
http://www.samsung.com
- Author: Marek Szyprowski m.szyprowski@samsung.com
- Author: Josh Cartwright joshc@codeaurora.org
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License or (at your optional) any later version of the license.
- */
+#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/mm.h> +#include <linux/sizes.h> +#include <linux/mm_types.h> +#include <linux/dma-contiguous.h> +#include <linux/of_reserved_mem.h>
+static void rmem_cma_device_init(struct reserved_mem *rmem,
struct device *dev,
struct of_phandle_args *args)
+{
- struct cma *cma = rmem->priv;
- dev_set_cma_area(dev, cma);
+}
+static const struct reserved_mem_ops rmem_cma_ops = {
- .device_init = rmem_cma_device_init,
+};
+static int __init rmem_cma_setup(struct reserved_mem *rmem,
unsigned long node,
const char *uname)
+{
- struct cma *cma;
- int err;
- if (!of_get_flat_dt_prop(node, "reusable", NULL))
return -EINVAL;
- err = of_parse_flat_dt_reg(node, uname, &rmem->base, &rmem->size);
- if (!err)
goto out_done;
- rmem->base = 0;
- err = of_parse_flat_dt_size(node, uname, &rmem->size);
- if (err)
goto out_err;
+out_done:
Please restrict gotos for error handling. Why not the following:
err = of_parse_flat_dt_reg(node, uname, &rmem->base, &rmem->size); if (err) { rmem->base = 0; err = of_parse_flat_dt_size(node, uname, &rmem->size); if (err) goto out_err; }
- err = dma_contiguous_reserve_area(rmem->size, rmem->base, 0,
&cma);
- if (err) {
pr_err("Reserved memory: unable to setup CMA region\n");
return err;
- }
- if (of_get_flat_dt_prop(node, "linux,cma-default", NULL))
dma_contiguous_set_default(cma);
- rmem->ops = &rmem_cma_ops;
- rmem->priv = cma;
- return 0;
+out_err:
- pr_err("Reseved memory: malformed node '%s'.\n", uname);
- return err;
+}
+RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup);
1.7.9.5
Enable reserved memory initialization from device tree.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- arch/arm/mm/init.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 804d61566a53..ebafdb479410 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -17,6 +17,7 @@ #include <linux/nodemask.h> #include <linux/initrd.h> #include <linux/of_fdt.h> +#include <linux/of_reserved_mem.h> #include <linux/highmem.h> #include <linux/gfp.h> #include <linux/memblock.h> @@ -323,6 +324,8 @@ void __init arm_memblock_init(struct meminfo *mi, if (mdesc->reserve) mdesc->reserve();
+ early_init_dt_scan_reserved_mem(); + /* * reserve memory for DMA contigouos allocations, * must come from DMA area inside low memory
On Tue, 04 Feb 2014 13:09:32 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
Enable reserved memory initialization from device tree.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
arch/arm/mm/init.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 804d61566a53..ebafdb479410 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -17,6 +17,7 @@ #include <linux/nodemask.h> #include <linux/initrd.h> #include <linux/of_fdt.h> +#include <linux/of_reserved_mem.h> #include <linux/highmem.h> #include <linux/gfp.h> #include <linux/memblock.h> @@ -323,6 +324,8 @@ void __init arm_memblock_init(struct meminfo *mi, if (mdesc->reserve) mdesc->reserve();
- early_init_dt_scan_reserved_mem();
The new binding is being made fundamental. If the reserved-memory node is present, then it needs to be honored, even if the kernel doesn't know how to use the regions. Therefore, This needs to be unconditional for all architectures. The hook should be called in early_init_dt_scan() (drivers/of/fdt.c) immediately after the early_init_dt_scan_memory() hook.
/* * reserve memory for DMA contigouos allocations, * must come from DMA area inside low memory -- 1.7.9.5
-- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Hello,
On 2014-02-05 11:15, Grant Likely wrote:
On Tue, 04 Feb 2014 13:09:32 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
Enable reserved memory initialization from device tree.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
arch/arm/mm/init.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 804d61566a53..ebafdb479410 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -17,6 +17,7 @@ #include <linux/nodemask.h> #include <linux/initrd.h> #include <linux/of_fdt.h> +#include <linux/of_reserved_mem.h> #include <linux/highmem.h> #include <linux/gfp.h> #include <linux/memblock.h> @@ -323,6 +324,8 @@ void __init arm_memblock_init(struct meminfo *mi, if (mdesc->reserve) mdesc->reserve();
- early_init_dt_scan_reserved_mem();
The new binding is being made fundamental. If the reserved-memory node is present, then it needs to be honored, even if the kernel doesn't know how to use the regions. Therefore, This needs to be unconditional for all architectures. The hook should be called in early_init_dt_scan() (drivers/of/fdt.c) immediately after the early_init_dt_scan_memory() hook.
In theory this will be the best solution, but it practice there is a problem. early_init_dt_scan() is called as the first function from kernel booting code. That time there is no memory yet added to the system, so it would be really hard to reserve anything. Memory nodes are being added later either with memblock_add() or by some other arch specific way.
Finally, once all memory has been added to the system we can parse and reserve all regions defined in the device tree. This really requires creating another function which will be called by arch specific code.
Best regards
On Thu, 06 Feb 2014 14:26:13 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
Hello,
On 2014-02-05 11:15, Grant Likely wrote:
On Tue, 04 Feb 2014 13:09:32 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
Enable reserved memory initialization from device tree.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
arch/arm/mm/init.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 804d61566a53..ebafdb479410 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -17,6 +17,7 @@ #include <linux/nodemask.h> #include <linux/initrd.h> #include <linux/of_fdt.h> +#include <linux/of_reserved_mem.h> #include <linux/highmem.h> #include <linux/gfp.h> #include <linux/memblock.h> @@ -323,6 +324,8 @@ void __init arm_memblock_init(struct meminfo *mi, if (mdesc->reserve) mdesc->reserve();
- early_init_dt_scan_reserved_mem();
The new binding is being made fundamental. If the reserved-memory node is present, then it needs to be honored, even if the kernel doesn't know how to use the regions. Therefore, This needs to be unconditional for all architectures. The hook should be called in early_init_dt_scan() (drivers/of/fdt.c) immediately after the early_init_dt_scan_memory() hook.
In theory this will be the best solution, but it practice there is a problem. early_init_dt_scan() is called as the first function from kernel booting code. That time there is no memory yet added to the system, so it would be really hard to reserve anything. Memory nodes are being added later either with memblock_add() or by some other arch specific way.
Hmmm, depends on the architecture. On ARM the memory is loaded into the meminfo structure first, and it isn't until arm_memblock_init() that memblock_add() gets called on all the regions. Some architectures do the memblock_add() directly from early_init_dt_add_memory_arch() function.
The default early_init_dt_add_memory_arch() in drivers/of/fdt.c is overridden by ARM and a number of other architectures. However...
Finally, once all memory has been added to the system we can parse and reserve all regions defined in the device tree. This really requires creating another function which will be called by arch specific code.
...Or it means getting rid of meminfo entirely so that memblock is available earlier. Laura Abbott has just posted v2 of her series to do exactly that. If you base on that then you should be able to do exactly what I suggested.
g.
On 2014-02-10 22:59, Grant Likely wrote:
On Thu, 06 Feb 2014 14:26:13 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
Hello,
On 2014-02-05 11:15, Grant Likely wrote:
On Tue, 04 Feb 2014 13:09:32 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
Enable reserved memory initialization from device tree.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
arch/arm/mm/init.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 804d61566a53..ebafdb479410 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -17,6 +17,7 @@ #include <linux/nodemask.h> #include <linux/initrd.h> #include <linux/of_fdt.h> +#include <linux/of_reserved_mem.h> #include <linux/highmem.h> #include <linux/gfp.h> #include <linux/memblock.h> @@ -323,6 +324,8 @@ void __init arm_memblock_init(struct meminfo *mi, if (mdesc->reserve) mdesc->reserve();
- early_init_dt_scan_reserved_mem();
The new binding is being made fundamental. If the reserved-memory node is present, then it needs to be honored, even if the kernel doesn't know how to use the regions. Therefore, This needs to be unconditional for all architectures. The hook should be called in early_init_dt_scan() (drivers/of/fdt.c) immediately after the early_init_dt_scan_memory() hook.
In theory this will be the best solution, but it practice there is a problem. early_init_dt_scan() is called as the first function from kernel booting code. That time there is no memory yet added to the system, so it would be really hard to reserve anything. Memory nodes are being added later either with memblock_add() or by some other arch specific way.
Hmmm, depends on the architecture. On ARM the memory is loaded into the meminfo structure first, and it isn't until arm_memblock_init() that memblock_add() gets called on all the regions. Some architectures do the memblock_add() directly from early_init_dt_add_memory_arch() function.
The default early_init_dt_add_memory_arch() in drivers/of/fdt.c is overridden by ARM and a number of other architectures. However...
Finally, once all memory has been added to the system we can parse and reserve all regions defined in the device tree. This really requires creating another function which will be called by arch specific code.
...Or it means getting rid of meminfo entirely so that memblock is available earlier. Laura Abbott has just posted v2 of her series to do exactly that. If you base on that then you should be able to do exactly what I suggested.
I've checked Laura's patches and in fact it is possible to do memory reservation as a last step in early_init_dt_scan_memory(). However still see some problem which I have no idea how to resolve. Right now I focus only on ARM, so I have no idea how it is solved by other architectures. On of the key features of the new binding is the ability to automatically allocate reserved regions of the given size. However kernel, initrd, dt and other sub-arch specific critical regions are marked/allocated in arm_memblock_init(), which is called after setup_machine_fdt(). This might lead to some serious failures when automatically reserved region overlaps with some critical resources. Do you have any idea how to solve this without a new callback?
Best regards
On Tue, 11 Feb 2014 11:52:42 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
On 2014-02-10 22:59, Grant Likely wrote:
On Thu, 06 Feb 2014 14:26:13 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
Hello,
On 2014-02-05 11:15, Grant Likely wrote:
On Tue, 04 Feb 2014 13:09:32 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
Enable reserved memory initialization from device tree.
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
arch/arm/mm/init.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 804d61566a53..ebafdb479410 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -17,6 +17,7 @@ #include <linux/nodemask.h> #include <linux/initrd.h> #include <linux/of_fdt.h> +#include <linux/of_reserved_mem.h> #include <linux/highmem.h> #include <linux/gfp.h> #include <linux/memblock.h> @@ -323,6 +324,8 @@ void __init arm_memblock_init(struct meminfo *mi, if (mdesc->reserve) mdesc->reserve();
- early_init_dt_scan_reserved_mem();
The new binding is being made fundamental. If the reserved-memory node is present, then it needs to be honored, even if the kernel doesn't know how to use the regions. Therefore, This needs to be unconditional for all architectures. The hook should be called in early_init_dt_scan() (drivers/of/fdt.c) immediately after the early_init_dt_scan_memory() hook.
In theory this will be the best solution, but it practice there is a problem. early_init_dt_scan() is called as the first function from kernel booting code. That time there is no memory yet added to the system, so it would be really hard to reserve anything. Memory nodes are being added later either with memblock_add() or by some other arch specific way.
Hmmm, depends on the architecture. On ARM the memory is loaded into the meminfo structure first, and it isn't until arm_memblock_init() that memblock_add() gets called on all the regions. Some architectures do the memblock_add() directly from early_init_dt_add_memory_arch() function.
The default early_init_dt_add_memory_arch() in drivers/of/fdt.c is overridden by ARM and a number of other architectures. However...
Finally, once all memory has been added to the system we can parse and reserve all regions defined in the device tree. This really requires creating another function which will be called by arch specific code.
...Or it means getting rid of meminfo entirely so that memblock is available earlier. Laura Abbott has just posted v2 of her series to do exactly that. If you base on that then you should be able to do exactly what I suggested.
I've checked Laura's patches and in fact it is possible to do memory reservation as a last step in early_init_dt_scan_memory(). However still see some problem which I have no idea how to resolve. Right now I focus only on ARM, so I have no idea how it is solved by other architectures.
It isn't at the moment because no other architectures are doing dynamic allocation of regions in this way.
On of the key features of the new binding is the ability to automatically allocate reserved regions of the given size. However kernel, initrd, dt and other sub-arch specific critical regions are marked/allocated in arm_memblock_init(), which is called after setup_machine_fdt(). This might lead to some serious failures when automatically reserved region overlaps with some critical resources. Do you have any idea how to solve this without a new callback?
Not without moving the kernel/initrd/fdt reservation earlier, which can certainly be done after Laura's series is applied. It would also be possible to split up the fixed regions from the dynamic regions so that the dynamic stuff would be done later.
However, I don't want to block this series on Laura's rework. Go ahead and keep it as a separate hook, but make the other changes I suggested, particularly with regard to making the hook architecture independent. Further rework can be done later after Laura's series is merged.
g.
From: Grant Likely grant.likely@linaro.org
Reserved memory nodes allow for the reservation of static (fixed address) regions, or dynamically allocated regions for a specific purpose.
[joshc: Based on binding document proposed (in non-patch form) here: http://lkml.kernel.org/g/20131030134702.19B57C402A0@trevor.secretlab.ca adapted to support #memory-region-cells]
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Josh Cartwright joshc@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- .../bindings/reserved-memory/reserved-memory.txt | 138 ++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
diff --git a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt new file mode 100644 index 000000000000..a606ce90c9c4 --- /dev/null +++ b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt @@ -0,0 +1,138 @@ +*** Reserved memory regions *** + +Reserved memory is specified as a node under the /reserved-memory node. +The operating system shall exclude reserved memory from normal usage +one can create child nodes describing particular reserved (excluded from +normal use) memory regions. Such memory regions are usually designed for +the special usage by various device drivers. + +Parameters for each memory region can be encoded into the device tree +with the following nodes: + +/reserved-memory node +--------------------- +#address-cells, #size-cells (required) - standard definition + - Should use the same values as the root node +#memory-region-cells (required) - dictates number of cells used in the child + nodes memory-region specifier +ranges (required) - standard definition + - Should be empty + +/reserved-memory/ child nodes +----------------------------- +Each child of the reserved-memory node specifies one or more regions of +reserved memory. Each child node may either use a 'reg' property to +specify a specific range of reserved memory, or a 'size' property with +optional constraints to request a dynamically allocated block of memory. + +Following the generic-names recommended practice, node names should +reflect the purpose of the node (ie. "framebuffer" or "dma-pool"). Unit +address (@<address>) should be appended to the name if the node is a +static allocation. + +Properties: +Requires either a) or b) below. +a) static allocation + reg (required) - standard definition +b) dynamic allocation + size (required) - length based on parent's #size-cells + - Size in bytes of memory to reserve. + alignment (optional) - length based on parent's #size-cells + - Address boundary for alignment of allocation. + alloc-ranges (optional) - prop-encoded-array (address, length pairs). + - Specifies regions of memory that are + acceptable to allocate from. + +If both reg and size are present, then the reg property takes precedence +and size is ignored. + +Additional properties: +compatible (optional) - standard definition + - may contain the following strings: + - shared-dma-pool: This indicates a region of memory meant to be + used as a shared pool of DMA buffers for a set of devices. It can + be used by an operating system to instanciate the necessary pool + management subsystem if necessary. + - vendor specific string in the form <vendor>,[<device>-]<usage> +no-map (optional) - empty property + - Indicates the operating system must not create a virtual mapping + of the region as part of its standard mapping of system memory, + nor permit speculative access to it under any circumstances other + than under the control of the device driver using the region. +reusable (optional) - empty property + - The operating system can use the memory in this region with the + limitation that the device driver(s) owning the region need to be + able to reclaim it back. Typically that means that the operating + system can use that region to store volatile or cached data that + can be otherwise regenerated or migrated elsewhere. + +Linux implementation note: +- If a "linux,cma-default" property is present, then Linux will use the + region for the default pool of the contiguous memory allocator. + +Device node references to reserved memory +----------------------------------------- +Regions in the /reserved-memory node may be referenced by other device +nodes by adding a memory-region property to the device node. + +memory-region (optional) - phandle, specifier pairs to children of /reserved-memory + +Example +------- +This example defines 3 contiguous regions are defined for Linux kernel: +one default of all device drivers (named linux,cma@72000000 and 64MiB in size), +one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and +one for multimedia processing (named multimedia-memory@77000000, 64MiB). + +/ { + #address-cells = <1>; + #size-cells = <1>; + + memory { + reg = <0x40000000 0x40000000>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + /* global autoconfigured region for contiguous allocations */ + linux,cma { + compatible = "shared-dma-pool"; + reusable; + #memory-region-cells = <0>; + size = <0x4000000>; + alignment = <0x2000>; + linux,cma-default; + }; + + display_reserved: framebuffer@78000000 { + #memory-region-cells = <0>; + reg = <0x78000000 0x800000>; + }; + + multimedia_reserved: multimedia@77000000 { + compatible = "acme,multimedia-memory"; + #memory-region-cells = <1>; + reg = <0x77000000 0x4000000>; + }; + }; + + /* ... */ + + fb0: video@12300000 { + memory-region = <&display_reserved>; + /* ... */ + }; + + scaler: scaler@12500000 { + memory-region = <&multimedia_reserved 0xdeadbeef>; + /* ... */ + }; + + codec: codec@12600000 { + memory-region = <&multimedia_reserved 0xfeebdaed>; + /* ... */ + }; +};
On Tue, 04 Feb 2014 13:09:33 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
From: Grant Likely grant.likely@linaro.org
Reserved memory nodes allow for the reservation of static (fixed address) regions, or dynamically allocated regions for a specific purpose.
[joshc: Based on binding document proposed (in non-patch form) here: http://lkml.kernel.org/g/20131030134702.19B57C402A0@trevor.secretlab.ca adapted to support #memory-region-cells]
Cc: Benjamin Herrenschmidt benh@kernel.crashing.org Cc: Laura Abbott lauraa@codeaurora.org Signed-off-by: Josh Cartwright joshc@codeaurora.org Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
.../bindings/reserved-memory/reserved-memory.txt | 138 ++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
diff --git a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt new file mode 100644 index 000000000000..a606ce90c9c4 --- /dev/null +++ b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt @@ -0,0 +1,138 @@ +*** Reserved memory regions ***
+Reserved memory is specified as a node under the /reserved-memory node. +The operating system shall exclude reserved memory from normal usage +one can create child nodes describing particular reserved (excluded from +normal use) memory regions. Such memory regions are usually designed for +the special usage by various device drivers.
+Parameters for each memory region can be encoded into the device tree +with the following nodes:
+/reserved-memory node +--------------------- +#address-cells, #size-cells (required) - standard definition
- Should use the same values as the root node
+#memory-region-cells (required) - dictates number of cells used in the child
nodes memory-region specifier
I don't think this isn't defined well enough. These reserved regions may not have a driver attached to them, so there is no central agent to decide what the specifier means. That leaves the interpretation of the memory region in the hands of the client drivers. How do you see the specifier getting parsed and used?
Otherwise the binding looks good to me. Also, please add my s-o-b line to the patch (instead of a-b because I authored part of the text)
Signed-off-by: Grant Likely grant.likely@linaro.org
g.
+ranges (required) - standard definition
- Should be empty
+/reserved-memory/ child nodes +----------------------------- +Each child of the reserved-memory node specifies one or more regions of +reserved memory. Each child node may either use a 'reg' property to +specify a specific range of reserved memory, or a 'size' property with +optional constraints to request a dynamically allocated block of memory.
+Following the generic-names recommended practice, node names should +reflect the purpose of the node (ie. "framebuffer" or "dma-pool"). Unit +address (@<address>) should be appended to the name if the node is a +static allocation.
+Properties: +Requires either a) or b) below. +a) static allocation
- reg (required) - standard definition
+b) dynamic allocation
- size (required) - length based on parent's #size-cells
- Size in bytes of memory to reserve.
- alignment (optional) - length based on parent's #size-cells
- Address boundary for alignment of allocation.
- alloc-ranges (optional) - prop-encoded-array (address, length pairs).
- Specifies regions of memory that are
acceptable to allocate from.
+If both reg and size are present, then the reg property takes precedence +and size is ignored.
+Additional properties: +compatible (optional) - standard definition
- may contain the following strings:
- shared-dma-pool: This indicates a region of memory meant to be
used as a shared pool of DMA buffers for a set of devices. It can
be used by an operating system to instanciate the necessary pool
management subsystem if necessary.
- vendor specific string in the form <vendor>,[<device>-]<usage>
+no-map (optional) - empty property
- Indicates the operating system must not create a virtual mapping
of the region as part of its standard mapping of system memory,
nor permit speculative access to it under any circumstances other
than under the control of the device driver using the region.
+reusable (optional) - empty property
- The operating system can use the memory in this region with the
limitation that the device driver(s) owning the region need to be
able to reclaim it back. Typically that means that the operating
system can use that region to store volatile or cached data that
can be otherwise regenerated or migrated elsewhere.
+Linux implementation note: +- If a "linux,cma-default" property is present, then Linux will use the
- region for the default pool of the contiguous memory allocator.
+Device node references to reserved memory +----------------------------------------- +Regions in the /reserved-memory node may be referenced by other device +nodes by adding a memory-region property to the device node.
+memory-region (optional) - phandle, specifier pairs to children of /reserved-memory
+Example +------- +This example defines 3 contiguous regions are defined for Linux kernel: +one default of all device drivers (named linux,cma@72000000 and 64MiB in size), +one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and +one for multimedia processing (named multimedia-memory@77000000, 64MiB).
+/ {
- #address-cells = <1>;
- #size-cells = <1>;
- memory {
reg = <0x40000000 0x40000000>;
- };
- reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
/* global autoconfigured region for contiguous allocations */
linux,cma {
compatible = "shared-dma-pool";
reusable;
#memory-region-cells = <0>;
size = <0x4000000>;
alignment = <0x2000>;
linux,cma-default;
};
display_reserved: framebuffer@78000000 {
#memory-region-cells = <0>;
reg = <0x78000000 0x800000>;
};
multimedia_reserved: multimedia@77000000 {
compatible = "acme,multimedia-memory";
#memory-region-cells = <1>;
reg = <0x77000000 0x4000000>;
};
- };
- /* ... */
- fb0: video@12300000 {
memory-region = <&display_reserved>;
/* ... */
- };
- scaler: scaler@12500000 {
memory-region = <&multimedia_reserved 0xdeadbeef>;
/* ... */
- };
- codec: codec@12600000 {
memory-region = <&multimedia_reserved 0xfeebdaed>;
/* ... */
- };
+};
1.7.9.5
On Wed, Feb 05, 2014 at 10:07:50AM +0000, Grant Likely wrote:
On Tue, 04 Feb 2014 13:09:33 +0100, Marek Szyprowski m.szyprowski@samsung.com wrote:
From: Grant Likely grant.likely@linaro.org +/reserved-memory node +--------------------- +#address-cells, #size-cells (required) - standard definition
- Should use the same values as the root node
+#memory-region-cells (required) - dictates number of cells used in the child
nodes memory-region specifier
I don't think this isn't defined well enough. These reserved regions may not have a driver attached to them, so there is no central agent to decide what the specifier means. That leaves the interpretation of the memory region in the hands of the client drivers. How do you see the specifier getting parsed and used?
I had a specific usecase in mind when I added it, although admittedly I haven't yet worked through all of the details.
On MSM chips, there is a region of memory accessible from the various processors on the SoC. This region is used for (among other things) inter-processor communication. Inside this region, a heap allocation protocol is implemented by software on all interested processors.
Consumers of this shared memory heap specify a 32-bit identifier and a size, and are either given a matching preexisting chunk (for example, another processor has already allocated a chunk with the corresponding identifier), or are allocated a new chunk for that identifier out of the region.
Given it's shared nature, this region has some specific requirements about how it may be accessed by the kernel (specifically regarding cacheability/how it's mapped), which means it at least needs _some_ representation in a reserved-memory node.
I had envisioned expressing the shared memory/consumer relationship in the device tree:
reserved-memory { smem : smem { compatible = "qcom,shared-memory"; reg = <...>; #memory-region-cells = <2>; no-map; }; };
consumer { /* ... */; memory-region = <&smem 0xDEADBEEF 0x1000>; };
That is, the heap protocol implementation exists as a "driver" for the smem reserved-memory node, and the consumer's 2-cell specifier is a 32-bit identifier and a size.
If your concern is for the case where a "qcom,shared-memory" node is specified in a device tree, but the "driver" hasn't been built into the kernel, then the appropriate behavior would be the same as the DMA/CMA case: fallback to a default case of memblock_reserve/memblock_remove'ing the region.
Would using reserved-memory in this way be outside the scope of what was originally intended?
Thanks, Josh
linaro-mm-sig@lists.linaro.org