While updating the cpus node, commit 40c753993e3a ("powerpc/kexec_file:
 Use current CPU info while setting up FDT") first deletes all subnodes
under the /cpus node. However, while adding sub-nodes back, it missed
adding cpus subnodes whose device_type != "cpu", such as l2-cache*,
l3-cache*, ibm,powerpc-cpu-features.

Fix this by only deleting cpus sub-nodes of device_type == "cpus" and
then adding all available nodes with device_type == "cpu".

Fixes: 40c753993e3a ("powerpc/kexec_file: Use current CPU info while setting up 
FDT")
Cc: Aditya Gupta <adit...@linux.ibm.com>
Cc: Hari Bathini <hbath...@linux.ibm.com>
Cc: Mahesh Salgaonkar <mah...@linux.ibm.com>
Cc: Michael Ellerman <m...@ellerman.id.au>
Cc: "Naveen N. Rao" <naveen.n....@linux.ibm.com>
Signed-off-by: Sourabh Jain <sourabhj...@linux.ibm.com>
---

* No changes in v2.

---

 arch/powerpc/kexec/core_64.c | 53 +++++++++++++++++++++++++-----------
 1 file changed, 37 insertions(+), 16 deletions(-)

diff --git a/arch/powerpc/kexec/core_64.c b/arch/powerpc/kexec/core_64.c
index 85050be08a23..2e625c2cb6b9 100644
--- a/arch/powerpc/kexec/core_64.c
+++ b/arch/powerpc/kexec/core_64.c
@@ -456,9 +456,15 @@ static int add_node_props(void *fdt, int node_offset, 
const struct device_node *
  * @fdt:              Flattened device tree of the kernel.
  *
  * Returns 0 on success, negative errno on error.
+ *
+ * Note: expecting no subnodes under /cpus/<node> with device_type == "cpu".
+ * If this changes, update this function to include them.
  */
 int update_cpus_node(void *fdt)
 {
+       int prev_node_offset;
+       const char *device_type;
+       const struct fdt_property *prop;
        struct device_node *cpus_node, *dn;
        int cpus_offset, cpus_subnode_offset, ret = 0;
 
@@ -469,30 +475,44 @@ int update_cpus_node(void *fdt)
                return cpus_offset;
        }
 
-       if (cpus_offset > 0) {
-               ret = fdt_del_node(fdt, cpus_offset);
+       prev_node_offset = cpus_offset;
+       /* Delete sub-nodes of /cpus node with device_type == "cpu" */
+       for (cpus_subnode_offset = fdt_first_subnode(fdt, cpus_offset); 
cpus_subnode_offset >= 0;) {
+               /* Ignore nodes that do not have a device_type property or 
device_type != "cpu" */
+               prop = fdt_get_property(fdt, cpus_subnode_offset, 
"device_type", NULL);
+               if (!prop || strcmp(prop->data, "cpu")) {
+                       prev_node_offset = cpus_subnode_offset;
+                       goto next_node;
+               }
+
+               ret = fdt_del_node(fdt, cpus_subnode_offset);
                if (ret < 0) {
-                       pr_err("Error deleting /cpus node: %s\n", 
fdt_strerror(ret));
-                       return -EINVAL;
+                       pr_err("Failed to delete a cpus sub-node: %s\n", 
fdt_strerror(ret));
+                       return ret;
                }
+next_node:
+               if (prev_node_offset == cpus_offset)
+                       cpus_subnode_offset = fdt_first_subnode(fdt, 
cpus_offset);
+               else
+                       cpus_subnode_offset = fdt_next_subnode(fdt, 
prev_node_offset);
        }
 
-       /* Add cpus node to fdt */
-       cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"), "cpus");
-       if (cpus_offset < 0) {
-               pr_err("Error creating /cpus node: %s\n", 
fdt_strerror(cpus_offset));
+       cpus_node = of_find_node_by_path("/cpus");
+       /* Fail here to avoid kexec/kdump kernel boot hung */
+       if (!cpus_node) {
+               pr_err("No /cpus node found\n");
                return -EINVAL;
        }
 
-       /* Add cpus node properties */
-       cpus_node = of_find_node_by_path("/cpus");
-       ret = add_node_props(fdt, cpus_offset, cpus_node);
-       of_node_put(cpus_node);
-       if (ret < 0)
-               return ret;
+       /* Add all /cpus sub-nodes of device_type == "cpu" to FDT */
+       for_each_child_of_node(cpus_node, dn) {
+               /* Ignore device nodes that do not have a device_type property
+                * or device_type != "cpu".
+                */
+               device_type = of_get_property(dn, "device_type", NULL);
+               if (!device_type || strcmp(device_type, "cpu"))
+                       continue;
 
-       /* Loop through all subnodes of cpus and add them to fdt */
-       for_each_node_by_type(dn, "cpu") {
                cpus_subnode_offset = fdt_add_subnode(fdt, cpus_offset, 
dn->full_name);
                if (cpus_subnode_offset < 0) {
                        pr_err("Unable to add %s subnode: %s\n", dn->full_name,
@@ -506,6 +526,7 @@ int update_cpus_node(void *fdt)
                        goto out;
        }
 out:
+       of_node_put(cpus_node);
        of_node_put(dn);
        return ret;
 }
-- 
2.44.0

Reply via email to