[PATCH 3/4] Revert "of: reserved_mem: Use unflatten_devicetree APIs to scan reserved memory nodes"

From: Oreoluwa Babatunde
Date: Mon Jul 08 2024 - 19:08:01 EST


This reverts commit 59d2c3cbcb5a47483629d8d992886e2650d786df.

With recent changes made to initialize the cma regions before the page
tables are setup, commit f2a524d9ef5b ("of: reserved_mem: Restructure
code to call reserved mem init functions earlier"), an issue was
introduced where the initialization of the cma regions fail and are
initialized as "non-reusable" regions instead of "reusable".

This issue occurs because the device_node of the regions are not yet
created by the time the cma regions are being initialized.

The cma regions need to be initialized before the page tables are setup
for them to be configured correctly as was realized in [1].

Hence, since the unflatten_devicetree APIs are not available until after
the page tables have been setup, revert back to using the fdt APIs. This
makes it possible to store a reference to each cma node in the
reserved_mem array by the time it is needed in their init function.

[1] https://lore.kernel.org/all/20240610213403.GA1697364@thelio-3990X/

Signed-off-by: Oreoluwa Babatunde <quic_obabatun@xxxxxxxxxxx>
---
drivers/of/of_reserved_mem.c | 67 +++++++++++++++++++--------------
include/linux/of_reserved_mem.h | 2 +-
kernel/dma/coherent.c | 10 +++--
kernel/dma/contiguous.c | 8 ++--
kernel/dma/swiotlb.c | 10 ++---
5 files changed, 55 insertions(+), 42 deletions(-)

diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 784a49a27e7f..113d593ea031 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -14,7 +14,6 @@
#include <linux/err.h>
#include <linux/libfdt.h>
#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/of_platform.h>
#include <linux/mm.h>
@@ -100,7 +99,7 @@ static void __init alloc_reserved_mem_array(void)
/*
* fdt_reserved_mem_save_node() - save fdt node for second pass initialization
*/
-static void __init fdt_reserved_mem_save_node(struct device_node *node, const char *uname,
+static void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
phys_addr_t base, phys_addr_t size)
{
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
@@ -110,7 +109,7 @@ static void __init fdt_reserved_mem_save_node(struct device_node *node, const ch
return;
}

- rmem->dev_node = node;
+ rmem->fdt_node = node;
rmem->name = uname;
rmem->base = base;
rmem->size = size;
@@ -179,11 +178,11 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
}

/*
- * __fdt_reserved_mem_check_root() - check if #size-cells, #address-cells provided
+ * __reserved_mem_check_root() - check if #size-cells, #address-cells provided
* in /reserved-memory matches the values supported by the current implementation,
* also check if ranges property has been provided
*/
-static int __init __fdt_reserved_mem_check_root(unsigned long node)
+static int __init __reserved_mem_check_root(unsigned long node)
{
const __be32 *prop;

@@ -213,35 +212,41 @@ static int __init __fdt_reserved_mem_check_root(unsigned long node)
*/
static void __init fdt_scan_reserved_mem_reg_nodes(void)
{
- struct device_node *node, *child;
+ 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 = of_find_node_by_path("/reserved-memory");
- if (!node) {
+ node = fdt_path_offset(fdt, "/reserved-memory");
+ if (node < 0) {
pr_info("Reserved memory: No reserved-memory node in the DT\n");
return;
}

- for_each_child_of_node(node, child) {
- int ret = 0;
+ 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;
- struct resource res;
- struct reserved_mem *rmem;

- if (!of_device_is_available(child))
+ prop = of_get_flat_dt_prop(child, "reg", &len);
+ if (!prop)
+ continue;
+ if (!of_fdt_device_is_available(fdt, child))
continue;

- ret = of_address_to_resource(child, 0, &res);
- if (ret) {
- rmem = of_reserved_mem_lookup(child);
- if (rmem)
- rmem->dev_node = child;
+ 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;
}
- uname = of_node_full_name(child);
-
- base = res.start;
- size = res.end - res.start + 1;
+ 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);
@@ -264,7 +269,7 @@ int __init fdt_scan_reserved_mem(void)
if (node < 0)
return -ENODEV;

- if (__fdt_reserved_mem_check_root(node) != 0) {
+ if (__reserved_mem_check_root(node) != 0) {
pr_err("Reserved memory: unsupported node format, ignoring\n");
return -EINVAL;
}
@@ -442,7 +447,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
uname, (unsigned long)(size / SZ_1M));
return -ENOMEM;
}
- fdt_reserved_mem_save_node(NULL, uname, base, size);
+ fdt_reserved_mem_save_node(node, uname, base, size);
return 0;
}

@@ -462,7 +467,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_device_is_compatible(rmem->dev_node, compat))
+ if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
continue;

ret = initfn(rmem);
@@ -495,6 +500,11 @@ 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;
}

@@ -541,11 +551,11 @@ void __init fdt_init_reserved_mem(void)

for (i = 0; i < reserved_mem_count; i++) {
struct reserved_mem *rmem = &reserved_mem[i];
- struct device_node *node = rmem->dev_node;
+ unsigned long node = rmem->fdt_node;
int err = 0;
bool nomap;

- nomap = of_property_present(node, "no-map");
+ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;

err = __reserved_mem_init_node(rmem);
if (err != 0 && err != -ENOENT) {
@@ -556,7 +566,8 @@ void __init fdt_init_reserved_mem(void)
memblock_phys_free(rmem->base, rmem->size);
} else {
phys_addr_t end = rmem->base + rmem->size - 1;
- bool reusable = of_property_present(node, "reusable");
+ 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),
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
index 769b8f67c8d3..e338282da652 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;
- struct device_node *dev_node;
+ unsigned long fdt_node;
const struct reserved_mem_ops *ops;
phys_addr_t base;
phys_addr_t size;
diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c
index 8f99586204fb..ff5683a57f77 100644
--- a/kernel/dma/coherent.c
+++ b/kernel/dma/coherent.c
@@ -362,18 +362,20 @@ static const struct reserved_mem_ops rmem_dma_ops = {

static int __init rmem_dma_setup(struct reserved_mem *rmem)
{
- struct device_node *node = rmem->dev_node;
+ unsigned long node = rmem->fdt_node;

- if (of_property_present(node, "reusable"))
+ if (of_get_flat_dt_prop(node, "reusable", NULL))
return -EINVAL;

- if (IS_ENABLED(CONFIG_ARM) && !of_property_present(node, "no-map")) {
+#ifdef CONFIG_ARM
+ if (!of_get_flat_dt_prop(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_property_present(node, "linux,dma-default")) {
+ if (of_get_flat_dt_prop(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 450e9e4be79c..055da410ac71 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -456,8 +456,8 @@ static const struct reserved_mem_ops rmem_cma_ops = {

static int __init rmem_cma_setup(struct reserved_mem *rmem)
{
- struct device_node *node = rmem->dev_node;
- bool default_cma = of_property_read_bool(node, "linux,cma-default");
+ unsigned long node = rmem->fdt_node;
+ bool default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
struct cma *cma;
int err;

@@ -467,8 +467,8 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem)
return -EBUSY;
}

- if (!of_property_present(node, "reusable") ||
- of_property_present(node, "no-map"))
+ if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
+ of_get_flat_dt_prop(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 9949ddc14272..fe1ccb53596f 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -1859,12 +1859,12 @@ static const struct reserved_mem_ops rmem_swiotlb_ops = {

static int __init rmem_swiotlb_setup(struct reserved_mem *rmem)
{
- struct device_node *node = rmem->dev_node;
+ unsigned long node = rmem->fdt_node;

- if (of_property_present(node, "reusable") ||
- of_property_present(node, "linux,cma-default") ||
- of_property_present(node, "linux,dma-default") ||
- of_property_present(node, "no-map"))
+ 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))
return -EINVAL;

rmem->ops = &rmem_swiotlb_ops;
--
2.34.1