The reserved_mem array is used to store data for the different
reserved memory regions defined in the DT of a device. The array
stores information such as region name, node reference, start-address,
and size of the different reserved memory regions.
The array is currently statically allocated with a size of
MAX_RESERVED_REGIONS(64). This means that any system that specifies a
number of reserved memory regions greater than MAX_RESERVED_REGIONS(64)
will not have enough space to store the information for all the regions.
This can be fixed by making the reserved_mem array a dynamically sized
array which is allocated using memblock_alloc() based on the exact
number of reserved memory regions defined in the DT.
On architectures such as arm64, memblock allocated memory is not
writable until after the page tables have been setup.
This is an issue because the current implementation initializes the
reserved memory regions and stores their information in the array before
the page tables are setup. Hence, dynamically allocating the
reserved_mem array and attempting to write information to it at this
point will fail.
Therefore, the allocation of the reserved_mem array will need to be done
after the page tables have been setup, which means that the reserved
memory regions will also need to wait until after the page tables have
been setup to be stored in the array.
When processing the reserved memory regions defined in the DT, these
regions are marked as reserved by calling memblock_reserve(base, size).
Where: base = base address of the reserved region.
size = the size of the reserved memory region.
Depending on if that region is defined using the "no-map" property,
memblock_mark_nomap(base, size) is also called.
The "no-map" property is used to indicate to the operating system that a
mapping of the specified region must NOT be created. This also means
that no access (including speculative accesses) is allowed on this
region of memory except when it is coming from the device driver that
this region of memory is being reserved for.[1]
Therefore, it is important to call memblock_reserve() and
memblock_mark_nomap() on all the reserved memory regions before the
system sets up the page tables so that the system does not unknowingly
include any of the no-map reserved memory regions in the memory map.
There are two ways to define how/where a reserved memory region is
placed in memory:
i) Statically-placed reserved memory regions
i.e. regions defined with a set start address and size using the
"reg" property in the DT.
ii) Dynamically-placed reserved memory regions.
i.e. regions defined by specifying a range of addresses where they can
be placed in memory using the "alloc_ranges" and "size" properties
in the DT.
The dynamically-placed reserved memory regions get assigned a start
address only at runtime. And this needs to be done before the page
tables are setup so that memblock_reserve() and memblock_mark_nomap()
can be called on the allocated region as explained above.
Since the dynamically allocated reserved_mem array can only available
after the page tables have been setup, the information for the
dynamically-placed reserved memory regions needs to be stored somewhere
temporarily until the reserved_mem array is available.
Therefore, this series makes use of a temporary static array to store
the information of the dynamically-placed reserved memory regions until
the reserved_mem array is allocated.
Once the reserved_mem array is available, the information is copied over
from the temporary array into the reserved_mem array, and the memory for
the temporary array is freed back to the system.
The information for the statically-placed reserved memory regions does
not need to be stored in a temporary array because their starting
address is already stored in the devicetree.
Hence, the only thing that needs to be done for these regions before the
page tables are setup is to call memblock_reserve() and
memblock_mark_nomap().
Once the reserved_mem array is allocated, the information for the
statically-placed reserved memory regions is added to the array.
Note:
Because of the use of a temporary array to store the information of the
dynamically-placed reserved memory regions, there still exists a
limitation of 64 for this particular kind of reserved memory regions.
From my observation, these regions are typically small in number and
hence I expect this to not be an issue for now.
Dependency:
This series is dependent on the acceptance of the below patchset for
proper behavior on openrisc and sh architecture. The patchset has
already been sent out and is pending review from the openrisc and sh
maintainters.
https://lore.kernel.org/all/[email protected]/
Patch Versions:
v4 (Current Patchset):
- Move fdt_init_reserved_mem() back into the unflatten_device_tree()
function.
- Fix warnings found by Kernel test robot:
https://lore.kernel.org/all/[email protected]/
https://lore.kernel.org/all/[email protected]/
https://lore.kernel.org/all/[email protected]/
v3:
https://lore.kernel.org/all/[email protected]/
- Make use of __initdata to delete the temporary static array after
dynamically allocating memory for reserved_mem array using memblock.
- Move call to fdt_init_reserved_mem() out of the
unflatten_device_tree() function and into architecture specific setup
code.
- Breaking up the changes for the individual architectures into separate
patches.
v2:
https://lore.kernel.org/all/[email protected]/
- Extend changes to all other relevant architectures by moving
fdt_init_reserved_mem() into the unflatten_device_tree() function.
- Add code to use unflatten devicetree APIs to process the reserved
memory regions.
v1:
https://lore.kernel.org/all/[email protected]/
References:
[1]
https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/reserved-memory/reserved-memory.yaml#L79
Oreoluwa Babatunde (4):
of: reserved_mem: Restruture how the reserved memory regions are
processed
of: reserved_mem: Add code to dynamically allocate reserved_mem array
of: reserved_mem: Use the unflatten_devicetree APIs to scan reserved
mem. nodes
of: reserved_mem: Rename fdt_* functions to refelct use of
unflatten_devicetree APIs
drivers/of/fdt.c | 39 +++++--
drivers/of/of_private.h | 5 +-
drivers/of/of_reserved_mem.c | 186 +++++++++++++++++++++++---------
include/linux/of_reserved_mem.h | 11 +-
kernel/dma/coherent.c | 8 +-
kernel/dma/contiguous.c | 8 +-
kernel/dma/swiotlb.c | 10 +-
7 files changed, 193 insertions(+), 74 deletions(-)
--
2.34.1
The current implementation processes the reserved memory regions in two
stages which are done with two separate functions within the
early_init_fdt_scan_reserved_mem() function.
Within the two stages of processing, the reserved memory regions are
broken up into two groups which are processed differently:
i) Statically-placed reserved memory regions
i.e. regions defined with a static start address and size using the
"reg" property in the DT.
ii) Dynamically-placed reserved memory regions.
i.e. regions defined by specifying a range of addresses where they can
be placed in memory using the "alloc_ranges" and "size" properties
in the DT.
Stage 1: fdt_scan_reserved_mem()
This stage of the reserved memory processing is used to scan through the
reserved memory nodes defined in the devicetree and do the following on
each of the nodes:
1) If the node represents a statically-placed reserved memory region,
i.e. it is defined using the "reg" property:
- Call memblock_reserve() or memblock_mark_nomap() as needed.
- Add the information for the reserved region to the reserved_mem array.
eg: fdt_reserved_mem_save_node(node, name, base, size);
2) If the node represents a dynamically-placed reserved memory region,
i.e. it is defined using "alloc-ranges" and "size" properties:
- Add the information for the region to the reserved_mem array with
the starting address and size set to 0.
eg: fdt_reserved_mem_save_node(node, name, 0, 0);
Stage 2: fdt_init_reserved_mem()
This stage of the reserved memory processing is used to iterate through
the reserved_mem array which was populated in stage 1 and do the
following on each of the entries:
1) If the entry represents a statically-placed reserved memory region:
- Call the region specific init function.
2) If the entry represents a dynamically-placed reserved memory region:
- Call __reserved_mem_alloc_size() which is used to allocate memory
for the region using memblock_phys_alloc_range(), and call
memblock_mark_nomap() on the allocated region if the region is
specified as a no-map region.
- Call the region specific init function.
On architectures such as arm64, the dynamic allocation of the
reserved_mem array needs to be done after the page tables have been
setup because memblock allocated memory is not writable until then. This
means that the reserved_mem array will not be available to store any
reserved memory information until after the page tables have been setup.
It is possible to call memblock_reserve() and memblock_mark_nomap() on
the statically-placed reserved memory regions and not need to save them
to the reserved_mem array until later. This is because all the
information we need is present in the devicetree.
Dynamically-placed reserved memory regions on the other hand get assigned
a start address only at runtime, and since memblock_reserve() and
memblock_mark_nomap() need to be called before the memory mappings are
created, the allocation needs to happen before the page tables are setup.
To make it easier to handle dynamically-placed reserved memory regions
before the page tables are setup, this patch makes changes to the steps
above to process the reserved memory regions in the following ways:
Step 1: fdt_scan_reserved_mem()
This stage of the reserved memory processing is used to scan through the
reserved memory nodes defined in the devicetree and do the following on
each of the nodes:
1) If the node represents a statically-placed reserved memory region,
i.e. it is defined using the "reg" property:
- Call memblock_reserve() or memblock_mark_nomap() as needed.
2) If the node represents a dynamically-placed reserved memory region,
i.e. it is defined using "alloc-ranges" and "size" properties:
- Call __reserved_mem_alloc_size() which will:
i) Allocate memory for the reserved memory region.
ii) Call memblock_mark_nomap() as needed.
Note: There is no need to explicitly call memblock_reserve() here
because it is already called by memblock when the memory for the
region is being allocated.
iii) Save the information for the region in the reserved_mem array.
Step 2: fdt_init_reserved_mem()
This stage of the reserved memory processing is used to:
1) Add the information for the statically-placed reserved memory into
the reserved_mem array.
2) Iterate through all the entries in the array and call the region
specific init function for each of them.
Signed-off-by: Oreoluwa Babatunde <[email protected]>
---
drivers/of/fdt.c | 84 ++++++++++++++++++++++++++++++---
drivers/of/of_private.h | 2 +-
drivers/of/of_reserved_mem.c | 54 +++++++++------------
include/linux/of_fdt.h | 1 +
include/linux/of_reserved_mem.h | 9 ++++
5 files changed, 111 insertions(+), 39 deletions(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index bf502ba8da95..fe6c75c5a8c0 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -504,7 +504,6 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
phys_addr_t base, size;
int len;
const __be32 *prop;
- int first = 1;
bool nomap;
prop = of_get_flat_dt_prop(node, "reg", &len);
@@ -532,10 +531,6 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
uname, &base, (unsigned long)(size / SZ_1M));
len -= t_len;
- if (first) {
- fdt_reserved_mem_save_node(node, uname, base, size);
- first = 0;
- }
}
return 0;
}
@@ -563,12 +558,70 @@ static int __init __reserved_mem_check_root(unsigned long node)
return 0;
}
+/**
+ * fdt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined
+ * reserved memory regions.
+ *
+ * This function is used to scan through the DT and store the
+ * information for the reserved memory regions that are defined using
+ * the "reg" property. The region node number, name, base address, and
+ * size are all stored in the reserved_mem array by calling the
+ * fdt_reserved_mem_save_node() function.
+ */
+void __init fdt_scan_reserved_mem_reg_nodes(void)
+
+{
+ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
+ const void *fdt = initial_boot_params;
+ phys_addr_t base, size;
+ const __be32 *prop;
+ int node, child;
+ int len;
+
+ node = fdt_path_offset(fdt, "/reserved-memory");
+ if (node < 0) {
+ pr_err("Reserved memory: Did not find reserved-memory node\n");
+ return;
+ }
+
+ if (__reserved_mem_check_root(node)) {
+ pr_err("Reserved memory: unsupported node format, ignoring\n");
+ return;
+ }
+
+ fdt_for_each_subnode(child, fdt, node) {
+ const char *uname;
+
+ prop = of_get_flat_dt_prop(child, "reg", &len);
+ if (!prop)
+ continue;
+
+ if (!of_fdt_device_is_available(fdt, child))
+ continue;
+
+ uname = fdt_get_name(fdt, child, NULL);
+ if (len && len % t_len != 0) {
+ pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
+ uname);
+ continue;
+ }
+
+ base = dt_mem_next_cell(dt_root_addr_cells, &prop);
+ size = dt_mem_next_cell(dt_root_size_cells, &prop);
+
+ if (size)
+ fdt_reserved_mem_save_node(child, uname, base, size);
+ }
+}
+
/*
* fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
*/
static int __init fdt_scan_reserved_mem(void)
{
int node, child;
+ int dynamic_nodes_cnt = 0;
+ int dynamic_nodes[MAX_RESERVED_REGIONS];
const void *fdt = initial_boot_params;
node = fdt_path_offset(fdt, "/reserved-memory");
@@ -590,8 +643,25 @@ static int __init fdt_scan_reserved_mem(void)
uname = fdt_get_name(fdt, child, NULL);
err = __reserved_mem_reserve_reg(child, uname);
- if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL))
- fdt_reserved_mem_save_node(child, uname, 0, 0);
+
+ /*
+ * Delay allocation of the dynamically-placed regions
+ * until after all other statically-placed regions have
+ * been reserved or marked as nomap
+ */
+ if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) {
+ dynamic_nodes[dynamic_nodes_cnt] = child;
+ dynamic_nodes_cnt++;
+ }
+ }
+
+ for (int i = 0; i < dynamic_nodes_cnt; i++) {
+ const char *uname;
+
+ child = dynamic_nodes[i];
+ uname = fdt_get_name(fdt, child, NULL);
+
+ __reserved_mem_alloc_size(child, uname);
}
return 0;
}
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index f38397c7b582..542e37a37a24 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -36,6 +36,7 @@ struct alias_prop {
#endif
#define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1
+#define MAX_RESERVED_REGIONS 64
extern struct mutex of_mutex;
extern raw_spinlock_t devtree_lock;
@@ -175,7 +176,6 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node *
}
#endif
-void fdt_init_reserved_mem(void);
void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
phys_addr_t base, phys_addr_t size);
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 7ec94cfcbddb..d62f1956024c 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -26,7 +26,6 @@
#include "of_private.h"
-#define MAX_RESERVED_REGIONS 64
static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
static int reserved_mem_count;
@@ -132,8 +131,7 @@ static int __init __reserved_mem_alloc_in_range(phys_addr_t size,
* __reserved_mem_alloc_size() - allocate reserved memory described by
* 'size', 'alignment' and 'alloc-ranges' properties.
*/
-static int __init __reserved_mem_alloc_size(unsigned long node,
- const char *uname, phys_addr_t *res_base, phys_addr_t *res_size)
+int __init __reserved_mem_alloc_size(unsigned long node, const char *uname)
{
int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
phys_addr_t start = 0, end = 0;
@@ -212,10 +210,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
uname, (unsigned long)(size / SZ_1M));
return -ENOMEM;
}
-
- *res_base = base;
- *res_size = size;
-
+ fdt_reserved_mem_save_node(node, uname, base, size);
return 0;
}
@@ -310,6 +305,8 @@ void __init fdt_init_reserved_mem(void)
{
int i;
+ fdt_scan_reserved_mem_reg_nodes();
+
/* check for overlapping reserved regions */
__rmem_check_for_overlap();
@@ -328,30 +325,25 @@ void __init fdt_init_reserved_mem(void)
if (prop)
rmem->phandle = of_read_number(prop, len/4);
- if (rmem->size == 0)
- err = __reserved_mem_alloc_size(node, rmem->name,
- &rmem->base, &rmem->size);
- if (err == 0) {
- err = __reserved_mem_init_node(rmem);
- if (err != 0 && err != -ENOENT) {
- pr_info("node %s compatible matching fail\n",
- rmem->name);
- if (nomap)
- memblock_clear_nomap(rmem->base, rmem->size);
- else
- memblock_phys_free(rmem->base,
- rmem->size);
- } else {
- phys_addr_t end = rmem->base + rmem->size - 1;
- bool reusable =
- (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
-
- pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
- &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
- nomap ? "nomap" : "map",
- reusable ? "reusable" : "non-reusable",
- rmem->name ? rmem->name : "unknown");
- }
+ err = __reserved_mem_init_node(rmem);
+ if (err != 0 && err != -ENOENT) {
+ pr_info("node %s compatible matching fail\n",
+ rmem->name);
+ if (nomap)
+ memblock_clear_nomap(rmem->base, rmem->size);
+ else
+ memblock_phys_free(rmem->base,
+ rmem->size);
+ } else {
+ phys_addr_t end = rmem->base + rmem->size - 1;
+ bool reusable =
+ (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
+
+ pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
+ &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
+ nomap ? "nomap" : "map",
+ reusable ? "reusable" : "non-reusable",
+ rmem->name ? rmem->name : "unknown");
}
}
}
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
index d69ad5bb1eb1..7b2a5d93d719 100644
--- a/include/linux/of_fdt.h
+++ b/include/linux/of_fdt.h
@@ -73,6 +73,7 @@ extern int early_init_dt_scan_root(void);
extern bool early_init_dt_scan(void *params);
extern bool early_init_dt_verify(void *params);
extern void early_init_dt_scan_nodes(void);
+extern void fdt_scan_reserved_mem_reg_nodes(void);
extern const char *of_flat_dt_get_machine_name(void);
extern const void *of_flat_dt_match_machine(const void *default_match,
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
index 4de2a24cadc9..2a3178920bae 100644
--- a/include/linux/of_reserved_mem.h
+++ b/include/linux/of_reserved_mem.h
@@ -32,12 +32,14 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem);
#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
_OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)
+void fdt_init_reserved_mem(void);
int of_reserved_mem_device_init_by_idx(struct device *dev,
struct device_node *np, int idx);
int of_reserved_mem_device_init_by_name(struct device *dev,
struct device_node *np,
const char *name);
void of_reserved_mem_device_release(struct device *dev);
+int __reserved_mem_alloc_size(unsigned long node, const char *uname);
struct reserved_mem *of_reserved_mem_lookup(struct device_node *np);
#else
@@ -45,6 +47,8 @@ struct reserved_mem *of_reserved_mem_lookup(struct device_node *np);
#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
_OF_DECLARE_STUB(reservedmem, name, compat, init, reservedmem_of_init_fn)
+static inline void fdt_init_reserved_mem(void) { }
+
static inline int of_reserved_mem_device_init_by_idx(struct device *dev,
struct device_node *np, int idx)
{
@@ -60,6 +64,11 @@ static inline int of_reserved_mem_device_init_by_name(struct device *dev,
static inline void of_reserved_mem_device_release(struct device *pdev) { }
+static inline int __reserved_mem_alloc_size(unsigned long node, const char *uname)
+{
+ return -ENOSYS;
+}
+
static inline struct reserved_mem *of_reserved_mem_lookup(struct device_node *np)
{
return NULL;
--
2.34.1
Rename the relevant fdt_* functions to a new naming scheme, dt_*, to
reflect the use of the unflatten_devicetree APIs to scan through the
reserved memory regions defined in the DT.
Signed-off-by: Oreoluwa Babatunde <[email protected]>
---
drivers/of/fdt.c | 2 +-
drivers/of/of_private.h | 4 ++--
drivers/of/of_reserved_mem.c | 14 +++++++-------
include/linux/of_reserved_mem.h | 4 ++--
4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 11630eba965c..e51bb3aa782b 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -1355,7 +1355,7 @@ void __init unflatten_device_tree(void)
of_alias_scan(early_init_dt_alloc_memory_arch);
unittest_unflatten_overlay_base();
- fdt_init_reserved_mem();
+ dt_init_reserved_mem();
}
/**
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index aa8844318257..8970948e10d4 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -177,7 +177,7 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node *
}
#endif
-void fdt_reserved_mem_save_node(struct device_node *node, const char *uname,
- phys_addr_t base, phys_addr_t size);
+void dt_reserved_mem_save_node(struct device_node *node, const char *uname,
+ phys_addr_t base, phys_addr_t size);
#endif /* _LINUX_OF_PRIVATE_H */
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 8d50a5178440..7d154d188a74 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -147,15 +147,15 @@ static void __init dt_scan_reserved_mem_reg_nodes(void)
size = dt_mem_next_cell(dt_root_size_cells, &prop);
if (size)
- fdt_reserved_mem_save_node(child, uname, base, size);
+ dt_reserved_mem_save_node(child, uname, base, size);
}
}
/*
- * fdt_reserved_mem_save_node() - save fdt node for second pass initialization
+ * dt_reserved_mem_save_node() - save dt node for second pass initialization
*/
-void __init fdt_reserved_mem_save_node(struct device_node *node, const char *uname,
- phys_addr_t base, phys_addr_t size)
+void __init dt_reserved_mem_save_node(struct device_node *node, const char *uname,
+ phys_addr_t base, phys_addr_t size)
{
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
@@ -307,7 +307,7 @@ int __init __reserved_mem_alloc_size(unsigned long node, const char *uname)
uname, (unsigned long)(size / SZ_1M));
return -ENOMEM;
}
- fdt_reserved_mem_save_node(NULL, uname, base, size);
+ dt_reserved_mem_save_node(NULL, uname, base, size);
return 0;
}
@@ -391,9 +391,9 @@ static void __init __rmem_check_for_overlap(void)
}
/**
- * fdt_init_reserved_mem() - allocate and init all saved reserved memory regions
+ * dt_init_reserved_mem() - allocate and init all saved reserved memory regions
*/
-void __init fdt_init_reserved_mem(void)
+void __init dt_init_reserved_mem(void)
{
int i, ret;
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
index e92babd669c2..dd67b9b2488e 100644
--- a/include/linux/of_reserved_mem.h
+++ b/include/linux/of_reserved_mem.h
@@ -32,7 +32,7 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem);
#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
_OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)
-void fdt_init_reserved_mem(void);
+void dt_init_reserved_mem(void);
int of_reserved_mem_device_init_by_idx(struct device *dev,
struct device_node *np, int idx);
int of_reserved_mem_device_init_by_name(struct device *dev,
@@ -47,7 +47,7 @@ struct reserved_mem *of_reserved_mem_lookup(struct device_node *np);
#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
_OF_DECLARE_STUB(reservedmem, name, compat, init, reservedmem_of_init_fn)
-static inline void fdt_init_reserved_mem(void) { }
+static inline void dt_init_reserved_mem(void) { }
static inline int of_reserved_mem_device_init_by_idx(struct device *dev,
struct device_node *np, int idx)
--
2.34.1
The reserved_mem array is statically allocated with a size of
MAX_RESERVED_REGIONS(64). Therefore, if the number of reserved_mem
regions exceeds this size, there will not be enough space to store
all the data.
Hence, extend the use of the static array by introducing a
dynamically allocated array based on the number of reserved memory
regions specified in the DT.
On architectures such as arm64, memblock allocated memory is not
writable until after the page tables have been setup. Hence, the
dynamic allocation of the reserved_mem array will need to be done only
after the page tables have been setup.
As a result, a temporary static array is still needed in the initial
stages to store the information of the dynamically-placed reserved memory
regions because the start address is selected only at run-time and is not
stored anywhere else.
It is not possible to wait until the reserved_mem array is allocated
because this is done after the page tables are setup and the reserved
memory regions need to be initialized before then.
After the reserved_mem array is allocated, all entries from the static
array is copied over to the new array, and the rest of the information
for the statically-placed reserved memory regions are read in from the
DT and stored in the new array as well.
Once the init process is completed, the temporary static array is
released back to the system because it is no longer needed. This is
achieved by marking it as __initdata.
Signed-off-by: Oreoluwa Babatunde <[email protected]>
---
drivers/of/fdt.c | 15 +++++++---
drivers/of/of_private.h | 1 +
drivers/of/of_reserved_mem.c | 53 ++++++++++++++++++++++++++++++++++--
3 files changed, 62 insertions(+), 7 deletions(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index fe6c75c5a8c0..2468360d6053 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -614,13 +614,15 @@ void __init fdt_scan_reserved_mem_reg_nodes(void)
}
}
+int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
+
/*
* fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
*/
static int __init fdt_scan_reserved_mem(void)
{
int node, child;
- int dynamic_nodes_cnt = 0;
+ int dynamic_nodes_cnt = 0, count = 0;
int dynamic_nodes[MAX_RESERVED_REGIONS];
const void *fdt = initial_boot_params;
@@ -643,6 +645,8 @@ static int __init fdt_scan_reserved_mem(void)
uname = fdt_get_name(fdt, child, NULL);
err = __reserved_mem_reserve_reg(child, uname);
+ if (!err)
+ count++;
/*
* Delay allocation of the dynamically-placed regions
@@ -657,12 +661,16 @@ static int __init fdt_scan_reserved_mem(void)
for (int i = 0; i < dynamic_nodes_cnt; i++) {
const char *uname;
+ int err;
child = dynamic_nodes[i];
uname = fdt_get_name(fdt, child, NULL);
- __reserved_mem_alloc_size(child, uname);
+ err = __reserved_mem_alloc_size(child, uname);
+ if (!err)
+ count++;
}
+ total_reserved_mem_cnt = count;
return 0;
}
@@ -715,8 +723,6 @@ void __init early_init_fdt_scan_reserved_mem(void)
break;
memblock_reserve(base, size);
}
-
- fdt_init_reserved_mem();
}
/**
@@ -1405,6 +1411,7 @@ void __init unflatten_device_tree(void)
of_alias_scan(early_init_dt_alloc_memory_arch);
unittest_unflatten_overlay_base();
+ fdt_init_reserved_mem();
}
/**
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 542e37a37a24..447b63413b39 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -42,6 +42,7 @@ extern struct mutex of_mutex;
extern raw_spinlock_t devtree_lock;
extern struct list_head aliases_lookup;
extern struct kset *of_kset;
+extern int total_reserved_mem_cnt;
#if defined(CONFIG_OF_DYNAMIC)
extern int of_property_notify(int action, struct device_node *np,
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index d62f1956024c..3c4373b021be 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -26,7 +26,8 @@
#include "of_private.h"
-static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
+static struct reserved_mem reserved_mem_array[MAX_RESERVED_REGIONS] __initdata;
+static struct reserved_mem *reserved_mem __refdata = reserved_mem_array;
static int reserved_mem_count;
static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
@@ -54,6 +55,48 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
return err;
}
+/**
+ * alloc_reserved_mem_array() - allocate memory for the reserved_mem
+ * array using memblock
+ *
+ * This function is used to allocate memory for the reserved_mem array
+ * according to the total number of reserved memory regions defined in
+ * the DT.
+ * After the new array is allocated, the information stored in the
+ * initial static array is copied over to this new array and the
+ * new array is used from this point on.
+ */
+static int __init alloc_reserved_mem_array(void)
+{
+ struct reserved_mem *new_array;
+ size_t alloc_size, copy_size, memset_size;
+
+ alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array));
+ if (alloc_size == SIZE_MAX)
+ return -1;
+
+ new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES);
+ if (!new_array)
+ return -ENOMEM;
+
+ copy_size = array_size(reserved_mem_count, sizeof(*new_array));
+ if (copy_size == SIZE_MAX)
+ goto overlow_err;
+
+ memset_size = alloc_size - copy_size;
+
+ memcpy(new_array, reserved_mem, copy_size);
+ memset(new_array + reserved_mem_count, 0, memset_size);
+
+ reserved_mem = new_array;
+ return 0;
+
+overlow_err:
+ memblock_free(new_array, alloc_size);
+ total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
+ return -1;
+}
+
/*
* fdt_reserved_mem_save_node() - save fdt node for second pass initialization
*/
@@ -62,7 +105,7 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
{
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
- if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
+ if (reserved_mem_count == total_reserved_mem_cnt) {
pr_err("not enough space for all defined regions.\n");
return;
}
@@ -303,7 +346,11 @@ static void __init __rmem_check_for_overlap(void)
*/
void __init fdt_init_reserved_mem(void)
{
- int i;
+ int i, ret;
+
+ ret = alloc_reserved_mem_array();
+ if (ret)
+ pr_err("Failed to allocate memory for reserved_mem array with err: %d", ret);
fdt_scan_reserved_mem_reg_nodes();
--
2.34.1
The unflatten_devicetree APIs have been setup and are available to be
used by the time the fdt_init_reserved_mem() function is called.
Since the unflatten_devicetree APIs are a more efficient way of scanning
through the DT nodes, switch to using these APIs to facilitate the rest
of the reserved memory processing.
Signed-off-by: Oreoluwa Babatunde <[email protected]>
---
drivers/of/fdt.c | 56 -----------------------
drivers/of/of_private.h | 4 +-
drivers/of/of_reserved_mem.c | 81 ++++++++++++++++++++++++++-------
include/linux/of_fdt.h | 1 -
include/linux/of_reserved_mem.h | 2 +-
kernel/dma/coherent.c | 8 ++--
kernel/dma/contiguous.c | 8 ++--
kernel/dma/swiotlb.c | 10 ++--
8 files changed, 81 insertions(+), 89 deletions(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 2468360d6053..11630eba965c 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -558,62 +558,6 @@ static int __init __reserved_mem_check_root(unsigned long node)
return 0;
}
-/**
- * fdt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined
- * reserved memory regions.
- *
- * This function is used to scan through the DT and store the
- * information for the reserved memory regions that are defined using
- * the "reg" property. The region node number, name, base address, and
- * size are all stored in the reserved_mem array by calling the
- * fdt_reserved_mem_save_node() function.
- */
-void __init fdt_scan_reserved_mem_reg_nodes(void)
-
-{
- int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
- const void *fdt = initial_boot_params;
- phys_addr_t base, size;
- const __be32 *prop;
- int node, child;
- int len;
-
- node = fdt_path_offset(fdt, "/reserved-memory");
- if (node < 0) {
- pr_err("Reserved memory: Did not find reserved-memory node\n");
- return;
- }
-
- if (__reserved_mem_check_root(node)) {
- pr_err("Reserved memory: unsupported node format, ignoring\n");
- return;
- }
-
- fdt_for_each_subnode(child, fdt, node) {
- const char *uname;
-
- prop = of_get_flat_dt_prop(child, "reg", &len);
- if (!prop)
- continue;
-
- if (!of_fdt_device_is_available(fdt, child))
- continue;
-
- uname = fdt_get_name(fdt, child, NULL);
- if (len && len % t_len != 0) {
- pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
- uname);
- continue;
- }
-
- base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- size = dt_mem_next_cell(dt_root_size_cells, &prop);
-
- if (size)
- fdt_reserved_mem_save_node(child, uname, base, size);
- }
-}
-
int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
/*
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 447b63413b39..aa8844318257 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -177,7 +177,7 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node *
}
#endif
-void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
- phys_addr_t base, phys_addr_t size);
+void fdt_reserved_mem_save_node(struct device_node *node, const char *uname,
+ phys_addr_t base, phys_addr_t size);
#endif /* _LINUX_OF_PRIVATE_H */
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 3c4373b021be..8d50a5178440 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -97,11 +97,65 @@ static int __init alloc_reserved_mem_array(void)
return -1;
}
+/**
+ * dt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined
+ * reserved memory regions.
+ *
+ * This function is used to scan through the DT and store the
+ * information for the reserved memory regions that are defined using
+ * the "reg" property. The region node number, name, base address, and
+ * size are all stored in the reserved_mem array by calling the
+ * fdt_reserved_mem_save_node() function.
+ */
+static void __init dt_scan_reserved_mem_reg_nodes(void)
+{
+ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
+ struct device_node *node, *child;
+ phys_addr_t base, size;
+ const __be32 *prop;
+ int len;
+
+ node = of_find_node_by_path("/reserved-memory");
+ if (!node) {
+ pr_err("Reserved memory: Did not find reserved-memory node\n");
+ return;
+ }
+
+ for_each_child_of_node(node, child) {
+ const char *uname;
+ struct reserved_mem *rmem;
+
+ if (!of_device_is_available(child))
+ continue;
+
+ prop = of_get_property(child, "reg", &len);
+ if (!prop) {
+ rmem = of_reserved_mem_lookup(child);
+ if (rmem)
+ rmem->dev_node = child;
+ continue;
+ }
+
+ uname = of_node_full_name(child);
+ if (len && len % t_len != 0) {
+ pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
+ uname);
+ continue;
+ }
+
+ base = dt_mem_next_cell(dt_root_addr_cells, &prop);
+ size = dt_mem_next_cell(dt_root_size_cells, &prop);
+
+ if (size)
+ fdt_reserved_mem_save_node(child, uname, base, size);
+ }
+}
+
/*
* fdt_reserved_mem_save_node() - save fdt node for second pass initialization
*/
-void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
- phys_addr_t base, phys_addr_t size)
+void __init fdt_reserved_mem_save_node(struct device_node *node, const char *uname,
+ phys_addr_t base, phys_addr_t size)
{
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
@@ -110,7 +164,7 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
return;
}
- rmem->fdt_node = node;
+ rmem->dev_node = node;
rmem->name = uname;
rmem->base = base;
rmem->size = size;
@@ -253,7 +307,7 @@ int __init __reserved_mem_alloc_size(unsigned long node, const char *uname)
uname, (unsigned long)(size / SZ_1M));
return -ENOMEM;
}
- fdt_reserved_mem_save_node(node, uname, base, size);
+ fdt_reserved_mem_save_node(NULL, uname, base, size);
return 0;
}
@@ -273,7 +327,7 @@ static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
reservedmem_of_init_fn initfn = i->data;
const char *compat = i->compatible;
- if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
+ if (!of_device_is_compatible(rmem->dev_node, compat))
continue;
ret = initfn(rmem);
@@ -306,11 +360,6 @@ static int __init __rmem_cmp(const void *a, const void *b)
if (ra->size > rb->size)
return 1;
- if (ra->fdt_node < rb->fdt_node)
- return -1;
- if (ra->fdt_node > rb->fdt_node)
- return 1;
-
return 0;
}
@@ -352,23 +401,23 @@ void __init fdt_init_reserved_mem(void)
if (ret)
pr_err("Failed to allocate memory for reserved_mem array with err: %d", ret);
- fdt_scan_reserved_mem_reg_nodes();
+ dt_scan_reserved_mem_reg_nodes();
/* check for overlapping reserved regions */
__rmem_check_for_overlap();
for (i = 0; i < reserved_mem_count; i++) {
struct reserved_mem *rmem = &reserved_mem[i];
- unsigned long node = rmem->fdt_node;
+ struct device_node *node = rmem->dev_node;
int len;
const __be32 *prop;
int err = 0;
bool nomap;
- nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
- prop = of_get_flat_dt_prop(node, "phandle", &len);
+ nomap = of_get_property(node, "no-map", NULL) != NULL;
+ prop = of_get_property(node, "phandle", &len);
if (!prop)
- prop = of_get_flat_dt_prop(node, "linux,phandle", &len);
+ prop = of_get_property(node, "linux,phandle", &len);
if (prop)
rmem->phandle = of_read_number(prop, len/4);
@@ -384,7 +433,7 @@ void __init fdt_init_reserved_mem(void)
} else {
phys_addr_t end = rmem->base + rmem->size - 1;
bool reusable =
- (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
+ (of_get_property(node, "reusable", NULL)) != NULL;
pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
&rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
index 7b2a5d93d719..d69ad5bb1eb1 100644
--- a/include/linux/of_fdt.h
+++ b/include/linux/of_fdt.h
@@ -73,7 +73,6 @@ extern int early_init_dt_scan_root(void);
extern bool early_init_dt_scan(void *params);
extern bool early_init_dt_verify(void *params);
extern void early_init_dt_scan_nodes(void);
-extern void fdt_scan_reserved_mem_reg_nodes(void);
extern const char *of_flat_dt_get_machine_name(void);
extern const void *of_flat_dt_match_machine(const void *default_match,
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
index 2a3178920bae..e92babd669c2 100644
--- a/include/linux/of_reserved_mem.h
+++ b/include/linux/of_reserved_mem.h
@@ -10,7 +10,7 @@ struct reserved_mem_ops;
struct reserved_mem {
const char *name;
- unsigned long fdt_node;
+ struct device_node *dev_node;
unsigned long phandle;
const struct reserved_mem_ops *ops;
phys_addr_t base;
diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c
index ff5683a57f77..0db0aae83102 100644
--- a/kernel/dma/coherent.c
+++ b/kernel/dma/coherent.c
@@ -362,20 +362,20 @@ static const struct reserved_mem_ops rmem_dma_ops = {
static int __init rmem_dma_setup(struct reserved_mem *rmem)
{
- unsigned long node = rmem->fdt_node;
+ struct device_node *node = rmem->dev_node;
- if (of_get_flat_dt_prop(node, "reusable", NULL))
+ if (of_get_property(node, "reusable", NULL))
return -EINVAL;
#ifdef CONFIG_ARM
- if (!of_get_flat_dt_prop(node, "no-map", NULL)) {
+ if (!of_get_property(node, "no-map", NULL)) {
pr_err("Reserved memory: regions without no-map are not yet supported\n");
return -EINVAL;
}
#endif
#ifdef CONFIG_DMA_GLOBAL_POOL
- if (of_get_flat_dt_prop(node, "linux,dma-default", NULL)) {
+ if (of_get_property(node, "linux,dma-default", NULL)) {
WARN(dma_reserved_default_memory,
"Reserved memory: region for default DMA coherent area is redefined\n");
dma_reserved_default_memory = rmem;
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index f005c66f378c..b54cf128a9d9 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -462,8 +462,8 @@ static const struct reserved_mem_ops rmem_cma_ops = {
static int __init rmem_cma_setup(struct reserved_mem *rmem)
{
- unsigned long node = rmem->fdt_node;
- bool default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
+ struct device_node *node = rmem->dev_node;
+ bool default_cma = of_get_property(node, "linux,cma-default", NULL);
struct cma *cma;
int err;
@@ -473,8 +473,8 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem)
return -EBUSY;
}
- if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
- of_get_flat_dt_prop(node, "no-map", NULL))
+ if (!of_get_property(node, "reusable", NULL) ||
+ of_get_property(node, "no-map", NULL))
return -EINVAL;
if (!IS_ALIGNED(rmem->base | rmem->size, CMA_MIN_ALIGNMENT_BYTES)) {
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index b079a9a8e087..ea1f734c8c35 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -1732,12 +1732,12 @@ static const struct reserved_mem_ops rmem_swiotlb_ops = {
static int __init rmem_swiotlb_setup(struct reserved_mem *rmem)
{
- unsigned long node = rmem->fdt_node;
+ struct device_node *node = rmem->dev_node;
- if (of_get_flat_dt_prop(node, "reusable", NULL) ||
- of_get_flat_dt_prop(node, "linux,cma-default", NULL) ||
- of_get_flat_dt_prop(node, "linux,dma-default", NULL) ||
- of_get_flat_dt_prop(node, "no-map", NULL))
+ if (of_get_property(node, "reusable", NULL) ||
+ of_get_property(node, "linux,cma-default", NULL) ||
+ of_get_property(node, "linux,dma-default", NULL) ||
+ of_get_property(node, "no-map", NULL))
return -EINVAL;
rmem->ops = &rmem_swiotlb_ops;
--
2.34.1
On Fri, Mar 08, 2024 at 11:12:02AM -0800, Oreoluwa Babatunde wrote:
> The reserved_mem array is statically allocated with a size of
> MAX_RESERVED_REGIONS(64). Therefore, if the number of reserved_mem
> regions exceeds this size, there will not be enough space to store
> all the data.
>
> Hence, extend the use of the static array by introducing a
> dynamically allocated array based on the number of reserved memory
> regions specified in the DT.
>
> On architectures such as arm64, memblock allocated memory is not
> writable until after the page tables have been setup. Hence, the
> dynamic allocation of the reserved_mem array will need to be done only
> after the page tables have been setup.
>
> As a result, a temporary static array is still needed in the initial
> stages to store the information of the dynamically-placed reserved memory
> regions because the start address is selected only at run-time and is not
> stored anywhere else.
> It is not possible to wait until the reserved_mem array is allocated
> because this is done after the page tables are setup and the reserved
> memory regions need to be initialized before then.
>
> After the reserved_mem array is allocated, all entries from the static
> array is copied over to the new array, and the rest of the information
> for the statically-placed reserved memory regions are read in from the
> DT and stored in the new array as well.
>
> Once the init process is completed, the temporary static array is
> released back to the system because it is no longer needed. This is
> achieved by marking it as __initdata.
>
> Signed-off-by: Oreoluwa Babatunde <[email protected]>
> ---
> drivers/of/fdt.c | 15 +++++++---
> drivers/of/of_private.h | 1 +
> drivers/of/of_reserved_mem.c | 53 ++++++++++++++++++++++++++++++++++--
> 3 files changed, 62 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
> index fe6c75c5a8c0..2468360d6053 100644
> --- a/drivers/of/fdt.c
> +++ b/drivers/of/fdt.c
> @@ -614,13 +614,15 @@ void __init fdt_scan_reserved_mem_reg_nodes(void)
> }
> }
>
> +int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
> +
Put this in of_reserved_mem.c.
> /*
> * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
> */
> static int __init fdt_scan_reserved_mem(void)
> {
> int node, child;
> - int dynamic_nodes_cnt = 0;
> + int dynamic_nodes_cnt = 0, count = 0;
> int dynamic_nodes[MAX_RESERVED_REGIONS];
> const void *fdt = initial_boot_params;
>
> @@ -643,6 +645,8 @@ static int __init fdt_scan_reserved_mem(void)
> uname = fdt_get_name(fdt, child, NULL);
>
> err = __reserved_mem_reserve_reg(child, uname);
> + if (!err)
> + count++;
>
> /*
> * Delay allocation of the dynamically-placed regions
> @@ -657,12 +661,16 @@ static int __init fdt_scan_reserved_mem(void)
>
> for (int i = 0; i < dynamic_nodes_cnt; i++) {
> const char *uname;
> + int err;
>
> child = dynamic_nodes[i];
> uname = fdt_get_name(fdt, child, NULL);
>
> - __reserved_mem_alloc_size(child, uname);
> + err = __reserved_mem_alloc_size(child, uname);
> + if (!err)
> + count++;
> }
> + total_reserved_mem_cnt = count;
> return 0;
> }
>
> @@ -715,8 +723,6 @@ void __init early_init_fdt_scan_reserved_mem(void)
> break;
> memblock_reserve(base, size);
> }
> -
> - fdt_init_reserved_mem();
> }
>
> /**
> @@ -1405,6 +1411,7 @@ void __init unflatten_device_tree(void)
> of_alias_scan(early_init_dt_alloc_memory_arch);
>
> unittest_unflatten_overlay_base();
> + fdt_init_reserved_mem();
This change belongs in patch 1.
> }
>
> /**
> diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
> index 542e37a37a24..447b63413b39 100644
> --- a/drivers/of/of_private.h
> +++ b/drivers/of/of_private.h
> @@ -42,6 +42,7 @@ extern struct mutex of_mutex;
> extern raw_spinlock_t devtree_lock;
> extern struct list_head aliases_lookup;
> extern struct kset *of_kset;
> +extern int total_reserved_mem_cnt;
>
> #if defined(CONFIG_OF_DYNAMIC)
> extern int of_property_notify(int action, struct device_node *np,
> diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
> index d62f1956024c..3c4373b021be 100644
> --- a/drivers/of/of_reserved_mem.c
> +++ b/drivers/of/of_reserved_mem.c
> @@ -26,7 +26,8 @@
>
> #include "of_private.h"
>
> -static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
> +static struct reserved_mem reserved_mem_array[MAX_RESERVED_REGIONS] __initdata;
> +static struct reserved_mem *reserved_mem __refdata = reserved_mem_array;
> static int reserved_mem_count;
>
> static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
> @@ -54,6 +55,48 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
> return err;
> }
>
> +/**
> + * alloc_reserved_mem_array() - allocate memory for the reserved_mem
> + * array using memblock
> + *
> + * This function is used to allocate memory for the reserved_mem array
> + * according to the total number of reserved memory regions defined in
> + * the DT.
> + * After the new array is allocated, the information stored in the
> + * initial static array is copied over to this new array and the
> + * new array is used from this point on.
> + */
> +static int __init alloc_reserved_mem_array(void)
> +{
> + struct reserved_mem *new_array;
> + size_t alloc_size, copy_size, memset_size;
> +
> + alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array));
> + if (alloc_size == SIZE_MAX)
> + return -1;
Use EOVERFLOW
> +
> + new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES);
> + if (!new_array)
> + return -ENOMEM;
> +
> + copy_size = array_size(reserved_mem_count, sizeof(*new_array));
> + if (copy_size == SIZE_MAX)
> + goto overlow_err;
This is the only path for goto, so move the cleanup here.
> +
> + memset_size = alloc_size - copy_size;
> +
> + memcpy(new_array, reserved_mem, copy_size);
> + memset(new_array + reserved_mem_count, 0, memset_size);
> +
> + reserved_mem = new_array;
> + return 0;
> +
> +overlow_err:
> + memblock_free(new_array, alloc_size);
> + total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
> + return -1;
Use EOVERFLOW
> +}
> +
> /*
> * fdt_reserved_mem_save_node() - save fdt node for second pass initialization
> */
> @@ -62,7 +105,7 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
> {
> struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
>
> - if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
> + if (reserved_mem_count == total_reserved_mem_cnt) {
> pr_err("not enough space for all defined regions.\n");
> return;
> }
> @@ -303,7 +346,11 @@ static void __init __rmem_check_for_overlap(void)
> */
> void __init fdt_init_reserved_mem(void)
> {
> - int i;
> + int i, ret;
> +
> + ret = alloc_reserved_mem_array();
> + if (ret)
> + pr_err("Failed to allocate memory for reserved_mem array with err: %d", ret);
As printing a message is the only error handling, better to just print
something in alloc_reserved_mem_array() and return void.
>
> fdt_scan_reserved_mem_reg_nodes();
>
> --
> 2.34.1
>
On Fri, Mar 08, 2024 at 11:12:01AM -0800, Oreoluwa Babatunde wrote:
> The current implementation processes the reserved memory regions in two
> stages which are done with two separate functions within the
> early_init_fdt_scan_reserved_mem() function.
>
> Within the two stages of processing, the reserved memory regions are
> broken up into two groups which are processed differently:
> i) Statically-placed reserved memory regions
> i.e. regions defined with a static start address and size using the
> "reg" property in the DT.
> ii) Dynamically-placed reserved memory regions.
> i.e. regions defined by specifying a range of addresses where they can
> be placed in memory using the "alloc_ranges" and "size" properties
> in the DT.
>
> Stage 1: fdt_scan_reserved_mem()
> This stage of the reserved memory processing is used to scan through the
> reserved memory nodes defined in the devicetree and do the following on
> each of the nodes:
>
> 1) If the node represents a statically-placed reserved memory region,
> i.e. it is defined using the "reg" property:
> - Call memblock_reserve() or memblock_mark_nomap() as needed.
> - Add the information for the reserved region to the reserved_mem array.
> eg: fdt_reserved_mem_save_node(node, name, base, size);
>
> 2) If the node represents a dynamically-placed reserved memory region,
> i.e. it is defined using "alloc-ranges" and "size" properties:
> - Add the information for the region to the reserved_mem array with
> the starting address and size set to 0.
> eg: fdt_reserved_mem_save_node(node, name, 0, 0);
>
> Stage 2: fdt_init_reserved_mem()
> This stage of the reserved memory processing is used to iterate through
> the reserved_mem array which was populated in stage 1 and do the
> following on each of the entries:
>
> 1) If the entry represents a statically-placed reserved memory region:
> - Call the region specific init function.
> 2) If the entry represents a dynamically-placed reserved memory region:
> - Call __reserved_mem_alloc_size() which is used to allocate memory
> for the region using memblock_phys_alloc_range(), and call
> memblock_mark_nomap() on the allocated region if the region is
> specified as a no-map region.
> - Call the region specific init function.
>
> On architectures such as arm64, the dynamic allocation of the
> reserved_mem array needs to be done after the page tables have been
> setup because memblock allocated memory is not writable until then. This
> means that the reserved_mem array will not be available to store any
> reserved memory information until after the page tables have been setup.
>
> It is possible to call memblock_reserve() and memblock_mark_nomap() on
> the statically-placed reserved memory regions and not need to save them
> to the reserved_mem array until later. This is because all the
> information we need is present in the devicetree.
> Dynamically-placed reserved memory regions on the other hand get assigned
> a start address only at runtime, and since memblock_reserve() and
> memblock_mark_nomap() need to be called before the memory mappings are
> created, the allocation needs to happen before the page tables are setup.
>
> To make it easier to handle dynamically-placed reserved memory regions
> before the page tables are setup, this patch makes changes to the steps
> above to process the reserved memory regions in the following ways:
>
> Step 1: fdt_scan_reserved_mem()
> This stage of the reserved memory processing is used to scan through the
> reserved memory nodes defined in the devicetree and do the following on
> each of the nodes:
>
> 1) If the node represents a statically-placed reserved memory region,
> i.e. it is defined using the "reg" property:
> - Call memblock_reserve() or memblock_mark_nomap() as needed.
>
> 2) If the node represents a dynamically-placed reserved memory region,
> i.e. it is defined using "alloc-ranges" and "size" properties:
> - Call __reserved_mem_alloc_size() which will:
> i) Allocate memory for the reserved memory region.
> ii) Call memblock_mark_nomap() as needed.
> Note: There is no need to explicitly call memblock_reserve() here
> because it is already called by memblock when the memory for the
> region is being allocated.
> iii) Save the information for the region in the reserved_mem array.
>
> Step 2: fdt_init_reserved_mem()
> This stage of the reserved memory processing is used to:
>
> 1) Add the information for the statically-placed reserved memory into
> the reserved_mem array.
>
> 2) Iterate through all the entries in the array and call the region
> specific init function for each of them.
>
> Signed-off-by: Oreoluwa Babatunde <[email protected]>
> ---
> drivers/of/fdt.c | 84 ++++++++++++++++++++++++++++++---
> drivers/of/of_private.h | 2 +-
> drivers/of/of_reserved_mem.c | 54 +++++++++------------
> include/linux/of_fdt.h | 1 +
> include/linux/of_reserved_mem.h | 9 ++++
> 5 files changed, 111 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
> index bf502ba8da95..fe6c75c5a8c0 100644
> --- a/drivers/of/fdt.c
> +++ b/drivers/of/fdt.c
> @@ -504,7 +504,6 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
> phys_addr_t base, size;
> int len;
> const __be32 *prop;
> - int first = 1;
> bool nomap;
>
> prop = of_get_flat_dt_prop(node, "reg", &len);
> @@ -532,10 +531,6 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
> uname, &base, (unsigned long)(size / SZ_1M));
>
> len -= t_len;
> - if (first) {
> - fdt_reserved_mem_save_node(node, uname, base, size);
> - first = 0;
> - }
> }
> return 0;
> }
> @@ -563,12 +558,70 @@ static int __init __reserved_mem_check_root(unsigned long node)
> return 0;
> }
>
> +/**
> + * fdt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined
> + * reserved memory regions.
> + *
> + * This function is used to scan through the DT and store the
> + * information for the reserved memory regions that are defined using
> + * the "reg" property. The region node number, name, base address, and
> + * size are all stored in the reserved_mem array by calling the
> + * fdt_reserved_mem_save_node() function.
> + */
> +void __init fdt_scan_reserved_mem_reg_nodes(void)
> +
> +{
> + int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
> + const void *fdt = initial_boot_params;
> + phys_addr_t base, size;
> + const __be32 *prop;
> + int node, child;
> + int len;
> +
> + node = fdt_path_offset(fdt, "/reserved-memory");
> + if (node < 0) {
> + pr_err("Reserved memory: Did not find reserved-memory node\n");
No reserved regions is perfectly valid.
> + return;
> + }
> +
> + if (__reserved_mem_check_root(node)) {
> + pr_err("Reserved memory: unsupported node format, ignoring\n");
> + return;
> + }
> +
> + fdt_for_each_subnode(child, fdt, node) {
> + const char *uname;
> +
> + prop = of_get_flat_dt_prop(child, "reg", &len);
> + if (!prop)
> + continue;
> +
> + if (!of_fdt_device_is_available(fdt, child))
> + continue;
> +
> + uname = fdt_get_name(fdt, child, NULL);
> + if (len && len % t_len != 0) {
> + pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
> + uname);
> + continue;
> + }
> +
> + base = dt_mem_next_cell(dt_root_addr_cells, &prop);
> + size = dt_mem_next_cell(dt_root_size_cells, &prop);
> +
> + if (size)
> + fdt_reserved_mem_save_node(child, uname, base, size);
> + }
> +}
> +
> /*
> * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
> */
> static int __init fdt_scan_reserved_mem(void)
> {
> int node, child;
> + int dynamic_nodes_cnt = 0;
> + int dynamic_nodes[MAX_RESERVED_REGIONS];
> const void *fdt = initial_boot_params;
>
> node = fdt_path_offset(fdt, "/reserved-memory");
> @@ -590,8 +643,25 @@ static int __init fdt_scan_reserved_mem(void)
> uname = fdt_get_name(fdt, child, NULL);
>
> err = __reserved_mem_reserve_reg(child, uname);
> - if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL))
> - fdt_reserved_mem_save_node(child, uname, 0, 0);
> +
> + /*
> + * Delay allocation of the dynamically-placed regions
> + * until after all other statically-placed regions have
> + * been reserved or marked as nomap
> + */
> + if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) {
> + dynamic_nodes[dynamic_nodes_cnt] = child;
> + dynamic_nodes_cnt++;
Can't you just call __reserved_mem_alloc_size() here instead of looping
twice?
> + }
> + }
> +
> + for (int i = 0; i < dynamic_nodes_cnt; i++) {
> + const char *uname;
> +
> + child = dynamic_nodes[i];
> + uname = fdt_get_name(fdt, child, NULL);
> +
> + __reserved_mem_alloc_size(child, uname);
> }
> return 0;
> }
> diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
> index f38397c7b582..542e37a37a24 100644
> --- a/drivers/of/of_private.h
> +++ b/drivers/of/of_private.h
> @@ -36,6 +36,7 @@ struct alias_prop {
> #endif
>
> #define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1
> +#define MAX_RESERVED_REGIONS 64
>
> extern struct mutex of_mutex;
> extern raw_spinlock_t devtree_lock;
> @@ -175,7 +176,6 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node *
> }
> #endif
>
> -void fdt_init_reserved_mem(void);
I don't see why this is moved.
> void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
> phys_addr_t base, phys_addr_t size);
>
> diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
> index 7ec94cfcbddb..d62f1956024c 100644
> --- a/drivers/of/of_reserved_mem.c
> +++ b/drivers/of/of_reserved_mem.c
> @@ -26,7 +26,6 @@
>
> #include "of_private.h"
>
> -#define MAX_RESERVED_REGIONS 64
> static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
> static int reserved_mem_count;
>
> @@ -132,8 +131,7 @@ static int __init __reserved_mem_alloc_in_range(phys_addr_t size,
> * __reserved_mem_alloc_size() - allocate reserved memory described by
> * 'size', 'alignment' and 'alloc-ranges' properties.
> */
> -static int __init __reserved_mem_alloc_size(unsigned long node,
> - const char *uname, phys_addr_t *res_base, phys_addr_t *res_size)
> +int __init __reserved_mem_alloc_size(unsigned long node, const char *uname)
> {
> int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
> phys_addr_t start = 0, end = 0;
> @@ -212,10 +210,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
> uname, (unsigned long)(size / SZ_1M));
> return -ENOMEM;
> }
> -
> - *res_base = base;
> - *res_size = size;
> -
> + fdt_reserved_mem_save_node(node, uname, base, size);
> return 0;
> }
>
> @@ -310,6 +305,8 @@ void __init fdt_init_reserved_mem(void)
> {
> int i;
>
> + fdt_scan_reserved_mem_reg_nodes();
> +
> /* check for overlapping reserved regions */
> __rmem_check_for_overlap();
>
> @@ -328,30 +325,25 @@ void __init fdt_init_reserved_mem(void)
> if (prop)
> rmem->phandle = of_read_number(prop, len/4);
>
> - if (rmem->size == 0)
> - err = __reserved_mem_alloc_size(node, rmem->name,
> - &rmem->base, &rmem->size);
> - if (err == 0) {
> - err = __reserved_mem_init_node(rmem);
> - if (err != 0 && err != -ENOENT) {
> - pr_info("node %s compatible matching fail\n",
> - rmem->name);
> - if (nomap)
> - memblock_clear_nomap(rmem->base, rmem->size);
> - else
> - memblock_phys_free(rmem->base,
> - rmem->size);
> - } else {
> - phys_addr_t end = rmem->base + rmem->size - 1;
> - bool reusable =
> - (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
> -
> - pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
> - &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
> - nomap ? "nomap" : "map",
> - reusable ? "reusable" : "non-reusable",
> - rmem->name ? rmem->name : "unknown");
> - }
> + err = __reserved_mem_init_node(rmem);
> + if (err != 0 && err != -ENOENT) {
> + pr_info("node %s compatible matching fail\n",
> + rmem->name);
Can be 1 line now.
> + if (nomap)
> + memblock_clear_nomap(rmem->base, rmem->size);
> + else
> + memblock_phys_free(rmem->base,
> + rmem->size);
Can be 1 line now.
> + } else {
> + phys_addr_t end = rmem->base + rmem->size - 1;
> + bool reusable =
> + (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
> +
> + pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
> + &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
> + nomap ? "nomap" : "map",
> + reusable ? "reusable" : "non-reusable",
> + rmem->name ? rmem->name : "unknown");
> }
> }
> }
> diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
> index d69ad5bb1eb1..7b2a5d93d719 100644
> --- a/include/linux/of_fdt.h
> +++ b/include/linux/of_fdt.h
> @@ -73,6 +73,7 @@ extern int early_init_dt_scan_root(void);
> extern bool early_init_dt_scan(void *params);
> extern bool early_init_dt_verify(void *params);
> extern void early_init_dt_scan_nodes(void);
> +extern void fdt_scan_reserved_mem_reg_nodes(void);
This is internal to drivers/of/, so it goes in of_private.h
>
> extern const char *of_flat_dt_get_machine_name(void);
> extern const void *of_flat_dt_match_machine(const void *default_match,
> diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
> index 4de2a24cadc9..2a3178920bae 100644
> --- a/include/linux/of_reserved_mem.h
> +++ b/include/linux/of_reserved_mem.h
> @@ -32,12 +32,14 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem);
> #define RESERVEDMEM_OF_DECLARE(name, compat, init) \
> _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)
>
> +void fdt_init_reserved_mem(void);
> int of_reserved_mem_device_init_by_idx(struct device *dev,
> struct device_node *np, int idx);
> int of_reserved_mem_device_init_by_name(struct device *dev,
> struct device_node *np,
> const char *name);
> void of_reserved_mem_device_release(struct device *dev);
> +int __reserved_mem_alloc_size(unsigned long node, const char *uname);
This is internal to drivers/of/, so it goes in of_private.h
But really, I think fdt_scan_reserved_mem() should move to
of_reserved_mem.c first. Then everything you add to fdt.c goes there
too.
Rob
On 3/11/2024 9:55 AM, Rob Herring wrote:
> On Fri, Mar 08, 2024 at 11:12:02AM -0800, Oreoluwa Babatunde wrote:
>> The reserved_mem array is statically allocated with a size of
>> MAX_RESERVED_REGIONS(64). Therefore, if the number of reserved_mem
>> regions exceeds this size, there will not be enough space to store
>> all the data.
>>
>> Hence, extend the use of the static array by introducing a
>> dynamically allocated array based on the number of reserved memory
>> regions specified in the DT.
>>
>> On architectures such as arm64, memblock allocated memory is not
>> writable until after the page tables have been setup. Hence, the
>> dynamic allocation of the reserved_mem array will need to be done only
>> after the page tables have been setup.
>>
>> As a result, a temporary static array is still needed in the initial
>> stages to store the information of the dynamically-placed reserved memory
>> regions because the start address is selected only at run-time and is not
>> stored anywhere else.
>> It is not possible to wait until the reserved_mem array is allocated
>> because this is done after the page tables are setup and the reserved
>> memory regions need to be initialized before then.
>>
>> After the reserved_mem array is allocated, all entries from the static
>> array is copied over to the new array, and the rest of the information
>> for the statically-placed reserved memory regions are read in from the
>> DT and stored in the new array as well.
>>
>> Once the init process is completed, the temporary static array is
>> released back to the system because it is no longer needed. This is
>> achieved by marking it as __initdata.
>>
>> Signed-off-by: Oreoluwa Babatunde <[email protected]>
>> ---
>> drivers/of/fdt.c | 15 +++++++---
>> drivers/of/of_private.h | 1 +
>> drivers/of/of_reserved_mem.c | 53 ++++++++++++++++++++++++++++++++++--
>> 3 files changed, 62 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
>> index fe6c75c5a8c0..2468360d6053 100644
>> --- a/drivers/of/fdt.c
>> +++ b/drivers/of/fdt.c
>> @@ -614,13 +614,15 @@ void __init fdt_scan_reserved_mem_reg_nodes(void)
>> }
>> }
>>
>> +int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
>> +
> Put this in of_reserved_mem.c.
>
>> /*
>> * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
>> */
>> static int __init fdt_scan_reserved_mem(void)
>> {
>> int node, child;
>> - int dynamic_nodes_cnt = 0;
>> + int dynamic_nodes_cnt = 0, count = 0;
>> int dynamic_nodes[MAX_RESERVED_REGIONS];
>> const void *fdt = initial_boot_params;
>>
>> @@ -643,6 +645,8 @@ static int __init fdt_scan_reserved_mem(void)
>> uname = fdt_get_name(fdt, child, NULL);
>>
>> err = __reserved_mem_reserve_reg(child, uname);
>> + if (!err)
>> + count++;
>>
>> /*
>> * Delay allocation of the dynamically-placed regions
>> @@ -657,12 +661,16 @@ static int __init fdt_scan_reserved_mem(void)
>>
>> for (int i = 0; i < dynamic_nodes_cnt; i++) {
>> const char *uname;
>> + int err;
>>
>> child = dynamic_nodes[i];
>> uname = fdt_get_name(fdt, child, NULL);
>>
>> - __reserved_mem_alloc_size(child, uname);
>> + err = __reserved_mem_alloc_size(child, uname);
>> + if (!err)
>> + count++;
>> }
>> + total_reserved_mem_cnt = count;
>> return 0;
>> }
>>
>> @@ -715,8 +723,6 @@ void __init early_init_fdt_scan_reserved_mem(void)
>> break;
>> memblock_reserve(base, size);
>> }
>> -
>> - fdt_init_reserved_mem();
>> }
>>
>> /**
>> @@ -1405,6 +1411,7 @@ void __init unflatten_device_tree(void)
>> of_alias_scan(early_init_dt_alloc_memory_arch);
>>
>> unittest_unflatten_overlay_base();
>> + fdt_init_reserved_mem();
> This change belongs in patch 1.
>
>> }
>>
>> /**
>> diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
>> index 542e37a37a24..447b63413b39 100644
>> --- a/drivers/of/of_private.h
>> +++ b/drivers/of/of_private.h
>> @@ -42,6 +42,7 @@ extern struct mutex of_mutex;
>> extern raw_spinlock_t devtree_lock;
>> extern struct list_head aliases_lookup;
>> extern struct kset *of_kset;
>> +extern int total_reserved_mem_cnt;
>>
>> #if defined(CONFIG_OF_DYNAMIC)
>> extern int of_property_notify(int action, struct device_node *np,
>> diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
>> index d62f1956024c..3c4373b021be 100644
>> --- a/drivers/of/of_reserved_mem.c
>> +++ b/drivers/of/of_reserved_mem.c
>> @@ -26,7 +26,8 @@
>>
>> #include "of_private.h"
>>
>> -static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
>> +static struct reserved_mem reserved_mem_array[MAX_RESERVED_REGIONS] __initdata;
>> +static struct reserved_mem *reserved_mem __refdata = reserved_mem_array;
>> static int reserved_mem_count;
>>
>> static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
>> @@ -54,6 +55,48 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
>> return err;
>> }
>>
>> +/**
>> + * alloc_reserved_mem_array() - allocate memory for the reserved_mem
>> + * array using memblock
>> + *
>> + * This function is used to allocate memory for the reserved_mem array
>> + * according to the total number of reserved memory regions defined in
>> + * the DT.
>> + * After the new array is allocated, the information stored in the
>> + * initial static array is copied over to this new array and the
>> + * new array is used from this point on.
>> + */
>> +static int __init alloc_reserved_mem_array(void)
>> +{
>> + struct reserved_mem *new_array;
>> + size_t alloc_size, copy_size, memset_size;
>> +
>> + alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array));
>> + if (alloc_size == SIZE_MAX)
>> + return -1;
> Use EOVERFLOW
>
>> +
>> + new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES);
>> + if (!new_array)
>> + return -ENOMEM;
>> +
>> + copy_size = array_size(reserved_mem_count, sizeof(*new_array));
>> + if (copy_size == SIZE_MAX)
>> + goto overlow_err;
> This is the only path for goto, so move the cleanup here.
>
>> +
>> + memset_size = alloc_size - copy_size;
>> +
>> + memcpy(new_array, reserved_mem, copy_size);
>> + memset(new_array + reserved_mem_count, 0, memset_size);
>> +
>> + reserved_mem = new_array;
>> + return 0;
>> +
>> +overlow_err:
>> + memblock_free(new_array, alloc_size);
>> + total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
>> + return -1;
> Use EOVERFLOW
>
>> +}
>> +
>> /*
>> * fdt_reserved_mem_save_node() - save fdt node for second pass initialization
>> */
>> @@ -62,7 +105,7 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
>> {
>> struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
>>
>> - if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
>> + if (reserved_mem_count == total_reserved_mem_cnt) {
>> pr_err("not enough space for all defined regions.\n");
>> return;
>> }
>> @@ -303,7 +346,11 @@ static void __init __rmem_check_for_overlap(void)
>> */
>> void __init fdt_init_reserved_mem(void)
>> {
>> - int i;
>> + int i, ret;
>> +
>> + ret = alloc_reserved_mem_array();
>> + if (ret)
>> + pr_err("Failed to allocate memory for reserved_mem array with err: %d", ret);
> As printing a message is the only error handling, better to just print
> something in alloc_reserved_mem_array() and return void.
>
>>
>> fdt_scan_reserved_mem_reg_nodes();
>>
>> --
>> 2.34.1
Hi Rob,
Thank you for the feedback. I'll implement all of this in the next
revision.
Regards,
Oreoluwa
On 3/11/2024 10:13 AM, Rob Herring wrote:
> On Fri, Mar 08, 2024 at 11:12:01AM -0800, Oreoluwa Babatunde wrote:
>> The current implementation processes the reserved memory regions in two
>> stages which are done with two separate functions within the
>> early_init_fdt_scan_reserved_mem() function.
>>
>> Within the two stages of processing, the reserved memory regions are
>> broken up into two groups which are processed differently:
>> i) Statically-placed reserved memory regions
>> i.e. regions defined with a static start address and size using the
>> "reg" property in the DT.
>> ii) Dynamically-placed reserved memory regions.
>> i.e. regions defined by specifying a range of addresses where they can
>> be placed in memory using the "alloc_ranges" and "size" properties
>> in the DT.
>>
>> Stage 1: fdt_scan_reserved_mem()
>> This stage of the reserved memory processing is used to scan through the
>> reserved memory nodes defined in the devicetree and do the following on
>> each of the nodes:
>>
>> 1) If the node represents a statically-placed reserved memory region,
>> i.e. it is defined using the "reg" property:
>> - Call memblock_reserve() or memblock_mark_nomap() as needed.
>> - Add the information for the reserved region to the reserved_mem array.
>> eg: fdt_reserved_mem_save_node(node, name, base, size);
>>
>> 2) If the node represents a dynamically-placed reserved memory region,
>> i.e. it is defined using "alloc-ranges" and "size" properties:
>> - Add the information for the region to the reserved_mem array with
>> the starting address and size set to 0.
>> eg: fdt_reserved_mem_save_node(node, name, 0, 0);
>>
>> Stage 2: fdt_init_reserved_mem()
>> This stage of the reserved memory processing is used to iterate through
>> the reserved_mem array which was populated in stage 1 and do the
>> following on each of the entries:
>>
>> 1) If the entry represents a statically-placed reserved memory region:
>> - Call the region specific init function.
>> 2) If the entry represents a dynamically-placed reserved memory region:
>> - Call __reserved_mem_alloc_size() which is used to allocate memory
>> for the region using memblock_phys_alloc_range(), and call
>> memblock_mark_nomap() on the allocated region if the region is
>> specified as a no-map region.
>> - Call the region specific init function.
>>
>> On architectures such as arm64, the dynamic allocation of the
>> reserved_mem array needs to be done after the page tables have been
>> setup because memblock allocated memory is not writable until then. This
>> means that the reserved_mem array will not be available to store any
>> reserved memory information until after the page tables have been setup.
>>
>> It is possible to call memblock_reserve() and memblock_mark_nomap() on
>> the statically-placed reserved memory regions and not need to save them
>> to the reserved_mem array until later. This is because all the
>> information we need is present in the devicetree.
>> Dynamically-placed reserved memory regions on the other hand get assigned
>> a start address only at runtime, and since memblock_reserve() and
>> memblock_mark_nomap() need to be called before the memory mappings are
>> created, the allocation needs to happen before the page tables are setup.
>>
>> To make it easier to handle dynamically-placed reserved memory regions
>> before the page tables are setup, this patch makes changes to the steps
>> above to process the reserved memory regions in the following ways:
>>
>> Step 1: fdt_scan_reserved_mem()
>> This stage of the reserved memory processing is used to scan through the
>> reserved memory nodes defined in the devicetree and do the following on
>> each of the nodes:
>>
>> 1) If the node represents a statically-placed reserved memory region,
>> i.e. it is defined using the "reg" property:
>> - Call memblock_reserve() or memblock_mark_nomap() as needed.
>>
>> 2) If the node represents a dynamically-placed reserved memory region,
>> i.e. it is defined using "alloc-ranges" and "size" properties:
>> - Call __reserved_mem_alloc_size() which will:
>> i) Allocate memory for the reserved memory region.
>> ii) Call memblock_mark_nomap() as needed.
>> Note: There is no need to explicitly call memblock_reserve() here
>> because it is already called by memblock when the memory for the
>> region is being allocated.
>> iii) Save the information for the region in the reserved_mem array.
>>
>> Step 2: fdt_init_reserved_mem()
>> This stage of the reserved memory processing is used to:
>>
>> 1) Add the information for the statically-placed reserved memory into
>> the reserved_mem array.
>>
>> 2) Iterate through all the entries in the array and call the region
>> specific init function for each of them.
>>
>> Signed-off-by: Oreoluwa Babatunde <[email protected]>
>> ---
>> drivers/of/fdt.c | 84 ++++++++++++++++++++++++++++++---
>> drivers/of/of_private.h | 2 +-
>> drivers/of/of_reserved_mem.c | 54 +++++++++------------
>> include/linux/of_fdt.h | 1 +
>> include/linux/of_reserved_mem.h | 9 ++++
>> 5 files changed, 111 insertions(+), 39 deletions(-)
>>
>> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
>> index bf502ba8da95..fe6c75c5a8c0 100644
>> --- a/drivers/of/fdt.c
>> +++ b/drivers/of/fdt.c
>> @@ -504,7 +504,6 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
>> phys_addr_t base, size;
>> int len;
>> const __be32 *prop;
>> - int first = 1;
>> bool nomap;
>>
>> prop = of_get_flat_dt_prop(node, "reg", &len);
>> @@ -532,10 +531,6 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
>> uname, &base, (unsigned long)(size / SZ_1M));
>>
>> len -= t_len;
>> - if (first) {
>> - fdt_reserved_mem_save_node(node, uname, base, size);
>> - first = 0;
>> - }
>> }
>> return 0;
>> }
>> @@ -563,12 +558,70 @@ static int __init __reserved_mem_check_root(unsigned long node)
>> return 0;
>> }
>>
>> +/**
>> + * fdt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined
>> + * reserved memory regions.
>> + *
>> + * This function is used to scan through the DT and store the
>> + * information for the reserved memory regions that are defined using
>> + * the "reg" property. The region node number, name, base address, and
>> + * size are all stored in the reserved_mem array by calling the
>> + * fdt_reserved_mem_save_node() function.
>> + */
>> +void __init fdt_scan_reserved_mem_reg_nodes(void)
>> +
>> +{
>> + int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
>> + const void *fdt = initial_boot_params;
>> + phys_addr_t base, size;
>> + const __be32 *prop;
>> + int node, child;
>> + int len;
>> +
>> + node = fdt_path_offset(fdt, "/reserved-memory");
>> + if (node < 0) {
>> + pr_err("Reserved memory: Did not find reserved-memory node\n");
> No reserved regions is perfectly valid.
ack
>> + return;
>> + }
>> +
>> + if (__reserved_mem_check_root(node)) {
>> + pr_err("Reserved memory: unsupported node format, ignoring\n");
>> + return;
>> + }
>> +
>> + fdt_for_each_subnode(child, fdt, node) {
>> + const char *uname;
>> +
>> + prop = of_get_flat_dt_prop(child, "reg", &len);
>> + if (!prop)
>> + continue;
>> +
>> + if (!of_fdt_device_is_available(fdt, child))
>> + continue;
>> +
>> + uname = fdt_get_name(fdt, child, NULL);
>> + if (len && len % t_len != 0) {
>> + pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
>> + uname);
>> + continue;
>> + }
>> +
>> + base = dt_mem_next_cell(dt_root_addr_cells, &prop);
>> + size = dt_mem_next_cell(dt_root_size_cells, &prop);
>> +
>> + if (size)
>> + fdt_reserved_mem_save_node(child, uname, base, size);
>> + }
>> +}
>> +
>> /*
>> * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
>> */
>> static int __init fdt_scan_reserved_mem(void)
>> {
>> int node, child;
>> + int dynamic_nodes_cnt = 0;
>> + int dynamic_nodes[MAX_RESERVED_REGIONS];
>> const void *fdt = initial_boot_params;
>>
>> node = fdt_path_offset(fdt, "/reserved-memory");
>> @@ -590,8 +643,25 @@ static int __init fdt_scan_reserved_mem(void)
>> uname = fdt_get_name(fdt, child, NULL);
>>
>> err = __reserved_mem_reserve_reg(child, uname);
>> - if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL))
>> - fdt_reserved_mem_save_node(child, uname, 0, 0);
>> +
>> + /*
>> + * Delay allocation of the dynamically-placed regions
>> + * until after all other statically-placed regions have
>> + * been reserved or marked as nomap
>> + */
>> + if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) {
>> + dynamic_nodes[dynamic_nodes_cnt] = child;
>> + dynamic_nodes_cnt++;
> Can't you just call __reserved_mem_alloc_size() here instead of looping
> twice?
The reason for looping twice here is in the comment right above the
if-statement; "Delay allocation of the dynamically-placed regions until
after all other statically-placed regions have been reserved or marked
as nomap".
If we call "__reserved_mem_alloc_size()" at this point then there is a
possibility of allocating memory from one of the statically-placed
reserved memory regions since not all of them have been marked as
reserved or nomap yet.
>> + }
>> + }
>> +
>> + for (int i = 0; i < dynamic_nodes_cnt; i++) {
>> + const char *uname;
>> +
>> + child = dynamic_nodes[i];
>> + uname = fdt_get_name(fdt, child, NULL);
>> +
>> + __reserved_mem_alloc_size(child, uname);
>> }
>> return 0;
>> }
>> diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
>> index f38397c7b582..542e37a37a24 100644
>> --- a/drivers/of/of_private.h
>> +++ b/drivers/of/of_private.h
>> @@ -36,6 +36,7 @@ struct alias_prop {
>> #endif
>>
>> #define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1
>> +#define MAX_RESERVED_REGIONS 64
>>
>> extern struct mutex of_mutex;
>> extern raw_spinlock_t devtree_lock;
>> @@ -175,7 +176,6 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node *
>> }
>> #endif
>>
>> -void fdt_init_reserved_mem(void);
> I don't see why this is moved.
True, there is no need for this anymore.
Will revert it in the next revision.
>> [...]
>>
>> @@ -328,30 +325,25 @@ void __init fdt_init_reserved_mem(void)
>> if (prop)
>> rmem->phandle = of_read_number(prop, len/4);
>>
>> - if (rmem->size == 0)
>> - err = __reserved_mem_alloc_size(node, rmem->name,
>> - &rmem->base, &rmem->size);
>> - if (err == 0) {
>> - err = __reserved_mem_init_node(rmem);
>> - if (err != 0 && err != -ENOENT) {
>> - pr_info("node %s compatible matching fail\n",
>> - rmem->name);
>> - if (nomap)
>> - memblock_clear_nomap(rmem->base, rmem->size);
>> - else
>> - memblock_phys_free(rmem->base,
>> - rmem->size);
>> - } else {
>> - phys_addr_t end = rmem->base + rmem->size - 1;
>> - bool reusable =
>> - (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
>> -
>> - pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
>> - &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
>> - nomap ? "nomap" : "map",
>> - reusable ? "reusable" : "non-reusable",
>> - rmem->name ? rmem->name : "unknown");
>> - }
>> + err = __reserved_mem_init_node(rmem);
>> + if (err != 0 && err != -ENOENT) {
>> + pr_info("node %s compatible matching fail\n",
>> + rmem->name);
> Can be 1 line now.
ack.
>> + if (nomap)
>> + memblock_clear_nomap(rmem->base, rmem->size);
>> + else
>> + memblock_phys_free(rmem->base,
>> + rmem->size);
> Can be 1 line now.
ack.
>> + } else {
>> + phys_addr_t end = rmem->base + rmem->size - 1;
>> + bool reusable =
>> + (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
>> +
>> + pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
>> + &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
>> + nomap ? "nomap" : "map",
>> + reusable ? "reusable" : "non-reusable",
>> + rmem->name ? rmem->name : "unknown");
>> }
>> }
>> }
>> diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
>> index d69ad5bb1eb1..7b2a5d93d719 100644
>> --- a/include/linux/of_fdt.h
>> +++ b/include/linux/of_fdt.h
>> @@ -73,6 +73,7 @@ extern int early_init_dt_scan_root(void);
>> extern bool early_init_dt_scan(void *params);
>> extern bool early_init_dt_verify(void *params);
>> extern void early_init_dt_scan_nodes(void);
>> +extern void fdt_scan_reserved_mem_reg_nodes(void);
> This is internal to drivers/of/, so it goes in of_private.h
ack.
>>
>> extern const char *of_flat_dt_get_machine_name(void);
>> extern const void *of_flat_dt_match_machine(const void *default_match,
>> diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
>> index 4de2a24cadc9..2a3178920bae 100644
>> --- a/include/linux/of_reserved_mem.h
>> +++ b/include/linux/of_reserved_mem.h
>> @@ -32,12 +32,14 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem);
>> #define RESERVEDMEM_OF_DECLARE(name, compat, init) \
>> _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)
>>
>> +void fdt_init_reserved_mem(void);
>> int of_reserved_mem_device_init_by_idx(struct device *dev,
>> struct device_node *np, int idx);
>> int of_reserved_mem_device_init_by_name(struct device *dev,
>> struct device_node *np,
>> const char *name);
>> void of_reserved_mem_device_release(struct device *dev);
>> +int __reserved_mem_alloc_size(unsigned long node, const char *uname);
> This is internal to drivers/of/, so it goes in of_private.h
ack.
>
> But really, I think fdt_scan_reserved_mem() should move to
> of_reserved_mem.c first. Then everything you add to fdt.c goes there
> too.
ack.
Regards,
Oreoluwa