The reserved_mem array is used to store the data of the different
reserved memory regions specified in the DT of a device.
The array stores information such as the name, node, starting address,
and size of a reserved memory region.
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.
Therefore, this series changes the reserved_mem array from a static
array with a fixed size of MAX_RESERVED_REGIONS(64), to a dynamically
allocated array, using memblock_alloc(), based on the number of reserved
memory regions specified in the DT.
Memory gotten from memblock_alloc() is only writable after paging_init()
is called, but the reserved memory regions need to be reserved before
then so that the system does not create page table mappings for them.
It is possible to call memblock_resverve() and memblock_mark_nomap() on
the reserved memory regions and not need to save them to the array until
we dynamically allocate the array after paging_init(), but this becomes
an issue with reserved memory regions that do not have their starting
address specified in the DT.
i.e. regions specified with the "alloc_ranges" and "size" properties.
Therefore, this series achieves the allocation and population of the
reserved_mem array in two steps:
1. Before paging_init()
Before paging_init() is called, iterate through the reserved_mem
nodes in the DT and do the following:
- Allocate memory for dynamically placed reserved memory regions and
store their starting address in the static allocated reserved_mem
array.
- Call memblock_reserve() and memblock_mark_nomap() on all the
reserved memory regions.
- Count the total number of reserved_mem nodes in the DT.
2. After paging_init()
After paging_init() is called:
- Allocate new memory for the reserved_mem array based on the number
of reserved memory nodes in the DT.
- Transfer all the information that was stored in the static array
into the new array.
- Store the rest of the reserved_mem regions in the new array.
i.e. reserved memory nodes specified with the "reg" property.
The static array is no longer needed after this point, but there is
currently no obvious way to free the memory. Therefore, the size of the
initial static array is now defined using a config option.
Since its size would vary depending on the user, scaling it can help get
some memory savings.
Oreoluwa Babatunde (3):
of: reserved_mem: Change the order that reserved_mem regions are
stored
of: reserved_mem: Add code to dynamically allocate reserved_mem array
of: reserved_mem: Make MAX_RESERVED_REGIONS a config option
arch/arm64/kernel/setup.c | 4 ++
drivers/of/Kconfig | 13 ++++
drivers/of/fdt.c | 63 +++++++++++++++----
drivers/of/of_private.h | 3 +-
drivers/of/of_reserved_mem.c | 108 ++++++++++++++++++++++----------
include/linux/of_fdt.h | 1 +
include/linux/of_reserved_mem.h | 9 +++
7 files changed, 154 insertions(+), 47 deletions(-)
--
2.17.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.
To fix this, dynamically allocate memory for the reserved_mem array
based on the number of reserved memory regions speicified in the DT.
Signed-off-by: Oreoluwa Babatunde <[email protected]>
---
drivers/of/fdt.c | 9 ++++--
drivers/of/of_private.h | 2 ++
drivers/of/of_reserved_mem.c | 53 ++++++++++++++++++++++++++++++++++--
3 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index d51a1176a7b9..408fd15b7db0 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -598,7 +598,7 @@ static void save_reserved_mem_reg_nodes(unsigned long node, const char *uname)
*/
int __init fdt_scan_reserved_mem(bool save_only)
{
- int node, child;
+ int node, child, count = 0;
const void *fdt = initial_boot_params;
node = fdt_path_offset(fdt, "/reserved-memory");
@@ -626,8 +626,13 @@ int __init fdt_scan_reserved_mem(bool save_only)
err = __reserved_mem_reserve_reg(child, uname);
if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL))
- __reserved_mem_alloc_size(child, uname);
+ err = __reserved_mem_alloc_size(child, uname);
+
+ if (err == 0)
+ count++;
}
+ if (!save_only)
+ update_reserved_mem_max_cnt(count);
return 0;
}
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index e52b27b8392d..ca1ba9f06660 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -178,4 +178,6 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node *
void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
phys_addr_t base, phys_addr_t size);
+void update_reserved_mem_max_cnt(int max_count);
+
#endif /* _LINUX_OF_PRIVATE_H */
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 13e694f5e316..203828ca118e 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -27,7 +27,10 @@
#include "of_private.h"
#define MAX_RESERVED_REGIONS 64
-static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
+static struct reserved_mem reserved_mem_array[MAX_RESERVED_REGIONS];
+static struct reserved_mem *reserved_mem __refdata = reserved_mem_array;
+
+static int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
static int reserved_mem_count;
static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
@@ -55,6 +58,45 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
return err;
}
+void __init update_reserved_mem_max_cnt(int max_count)
+{
+ total_reserved_mem_cnt = max_count;
+}
+
+static int 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;
+
+ if (total_reserved_mem_cnt >= reserved_mem_count)
+ memset_size = alloc_size - copy_size;
+ else
+ memset_size = copy_size - alloc_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
*/
@@ -63,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;
}
@@ -304,9 +346,14 @@ static void __init __rmem_check_for_overlap(void)
*/
void __init fdt_init_reserved_mem(void)
{
- int i;
+ int i, ret = 0;
bool save_only = true;
+ ret = alloc_reserved_mem_array();
+ if (ret) {
+ pr_err("Failed to store reserved_mem nodes in array with err: %d", ret);
+ return;
+ }
fdt_scan_reserved_mem(save_only);
/* check for overlapping reserved regions */
--
2.17.1
Add code to make the size of reserved_mem_array a config option which
can be modified based on user requirements.
The reserved_mem_array is required during device bootup to store the
information of the reserved memory regions that need to be dynamically
allocated. After some time, this information is transferred to another
array which is used to store all reserved memory regions, after which
the reserved_mem_array will no longer be needed.
Since the size required for the reserved_mem_array can vary and there
is currently no way to free the memory afterwards, make the size of the
array configurable in an attempt to save some memory.
Signed-off-by: Oreoluwa Babatunde <[email protected]>
---
drivers/of/Kconfig | 13 +++++++++++++
drivers/of/of_reserved_mem.c | 9 ++++-----
2 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index da9826accb1b..3cb2ec4993b5 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -102,4 +102,17 @@ config OF_OVERLAY
config OF_NUMA
bool
+config OF_MAX_RESERVED_REGIONS
+ int "OF resvered_mem array size"
+ default "64"
+ range 1 64
+ help
+ The reserved_mem_array is used to store information about the dynamically
+ placed reserved memory regions before we are able to allocate the memory
+ needed to store all the reserved memory regions defined in the DT.
+ Because the amount of memory needed initially for this array could vary,
+ make the size of the reserved_mem_array configurable in an attempt to
+ save some memory when possible.
+ if unsure, leave as default value.
+
endif # OF
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 203828ca118e..f408dce762e1 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -26,11 +26,10 @@
#include "of_private.h"
-#define MAX_RESERVED_REGIONS 64
-static struct reserved_mem reserved_mem_array[MAX_RESERVED_REGIONS];
-static struct reserved_mem *reserved_mem __refdata = reserved_mem_array;
+static struct reserved_mem reserved_mem_array[CONFIG_OF_MAX_RESERVED_REGIONS];
+static struct reserved_mem *reserved_mem = reserved_mem_array;
-static int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
+static int total_reserved_mem_cnt = CONFIG_OF_MAX_RESERVED_REGIONS;
static int reserved_mem_count;
static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
@@ -93,7 +92,7 @@ static int alloc_reserved_mem_array(void)
overlow_err:
memblock_free(new_array, alloc_size);
- total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
+ total_reserved_mem_cnt = CONFIG_OF_MAX_RESERVED_REGIONS;
return -1;
}
--
2.17.1