This patch fixes a bug matching drc-indexes of CPUs that are
being hot-added to a system either individually or by count.
This patch inserts a couple of missing checks and parsing code
for the new representation of device-tree information provided
by the property "ibm,drc-info".

Signed-off-by: Michael Bringmann <[email protected]>
Fixes: 3f38000eda48 ("powerpc/firmware: Add definitions for new drc-info firmwar
e feature" -- end of patch series applied to powerpc next)
---
 arch/powerpc/platforms/pseries/hotplug-cpu.c |  117 +++++++++++++++++++++-----
 1 file changed, 96 insertions(+), 21 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c 
b/arch/powerpc/platforms/pseries/hotplug-cpu.c
index a7d14aa7..4cc6b70 100644
--- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -413,19 +413,52 @@ static bool valid_cpu_drc_index(struct device_node 
*parent, u32 drc_index)
        bool found = false;
        int rc, index;
 
-       index = 0;
-       while (!found) {
-               u32 drc;
+       if (firmware_has_feature(FW_FEATURE_DRC_INFO)) {
+               struct property *info = NULL;
+               struct of_drc_info drc;
+               int j;
+               u32 num_set_entries;
+               const __be32 *value;
+
+               info = of_find_property(parent, "ibm,drc-info", NULL);
+               if (info == NULL)
+                       goto end_out;
+
+               value = of_prop_next_u32(info, NULL, &num_set_entries);
+               if (!value)
+                       goto end_out;
+               value++;
+
+               for (j = 0; j < num_set_entries; j++) {
+
+                       of_read_drc_info_cell(&info, &value, &drc);
+                       if (strncmp(drc.drc_type, "CPU", 3))
+                               goto end_out;
+
+                       if ((drc.drc_index_start <= drc_index) &&
+                               (drc_index <= drc.last_drc_index)) {
+                               found = true;
+                               break;
+                       }
+               }
 
-               rc = of_property_read_u32_index(parent, "ibm,drc-indexes",
+       } else {
+               index = 0;
+               while (!found) {
+                       u32 drc;
+
+                       rc = of_property_read_u32_index(parent,
+                                               "ibm,drc-indexes",
                                                index++, &drc);
-               if (rc)
-                       break;
+                       if (rc)
+                               break;
 
-               if (drc == drc_index)
-                       found = true;
+                       if (drc == drc_index)
+                               found = true;
+               }
        }
 
+end_out:
        return found;
 }
 
@@ -731,26 +764,68 @@ static int find_dlpar_cpus_to_add(u32 *cpu_drcs, u32 
cpus_to_add)
                return -1;
        }
 
-       /* Search the ibm,drc-indexes array for possible CPU drcs to
-        * add. Note that the format of the ibm,drc-indexes array is
-        * the number of entries in the array followed by the array
-        * of drc values so we start looking at index = 1.
+       /* Search the ibm,drc-indexes or ibm,drc-info array for
+        * possible CPU drcs to add. Note that the format of the
+        * ibm,drc-indexes array is the number of entries in the
+        * array followed by the array of drc values so we start
+        * looking at index = 1.  The ibm,drc-info array is a more
+        * compact format for large numbers of CPUs, and the format
+        * is correspondingly more complex.
         */
-       index = 1;
-       while (cpus_found < cpus_to_add) {
-               u32 drc;
+       if (firmware_has_feature(FW_FEATURE_DRC_INFO)) {
+               struct property *info = NULL;
+               struct of_drc_info drc;
+               int j;
+               u32 num_set_entries;
+               const __be32 *value;
+
+               info = of_find_property(parent, "ibm,drc-info", NULL);
+               if (info == NULL)
+                       goto err_out;
+
+               value = of_prop_next_u32(info, NULL, &num_set_entries);
+               if (!value)
+                       goto err_out;
+               value++;
+
+               for (j = 0; j < num_set_entries; j++) {
+                       int k;
+
+                       of_read_drc_info_cell(&info, &value, &drc);
+                       if (strncmp(drc.drc_type, "CPU", 3))
+                               goto err_out;
+
+                       for (k = 0; (k < drc.num_sequential_elems) &&
+                                       (cpus_found < cpus_to_add); k++) {
+                               u32 idrc = drc.drc_index_start +
+                                       (k * drc.sequential_inc);
+
+                               if (dlpar_cpu_exists(parent, idrc))
+                                       continue;
+
+                               cpu_drcs[cpus_found++] = idrc;
+                       }
+               }
+
+       } else {
+               index = 1;
+               while (cpus_found < cpus_to_add) {
+                       u32 drc;
 
-               rc = of_property_read_u32_index(parent, "ibm,drc-indexes",
+                       rc = of_property_read_u32_index(parent,
+                                               "ibm,drc-indexes",
                                                index++, &drc);
-               if (rc)
-                       break;
+                       if (rc)
+                               break;
 
-               if (dlpar_cpu_exists(parent, drc))
-                       continue;
+                       if (dlpar_cpu_exists(parent, drc))
+                               continue;
 
-               cpu_drcs[cpus_found++] = drc;
+                       cpu_drcs[cpus_found++] = drc;
+               }
        }
 
+err_out:
        of_node_put(parent);
        return cpus_found;
 }

Reply via email to