On pseries, cache nodes in the device tree can be added and removed by the
CPU DLPAR code as well as the partition migration (mobility) code. PowerVM
partitions in dedicated processor mode typically have L2 and L3 cache
nodes.
The CPU DLPAR code has the following shortcomings:
* Cache nodes returned as siblings of a new CPU node by
ibm,configure-connector are silently discarded; only the CPU node is
added to the device tree.
* Cache nodes which become unreferenced in the processor removal path are
not removed from the device tree. This can lead to duplicate nodes when
the post-migration device tree update code replaces cache nodes.
This is long-standing behavior. Presumably it has gone mostly unnoticed
because the two bugs have the property of obscuring each other in common
simple scenarios (e.g. remove a CPU and add it back). Likely you'd notice
only if you cared to inspect the device tree or the sysfs cacheinfo
information.
Booted with two processors:
$ pwd
/sys/firmware/devicetree/base/cpus
$ ls -1d */
l2-cache@2010/
l2-cache@2011/
l3-cache@3110/
l3-cache@3111/
PowerPC,POWER9@0/
PowerPC,POWER9@8/
$ lsprop */l2-cache
l2-cache@2010/l2-cache
00003110 (12560)
l2-cache@2011/l2-cache
00003111 (12561)
PowerPC,POWER9@0/l2-cache
00002010 (8208)
PowerPC,POWER9@8/l2-cache
00002011 (8209)
$ ls /sys/devices/system/cpu/cpu0/cache/
index0 index1 index2 index3
After DLPAR-adding PowerPC,POWER9@10, we see that its associated cache
nodes are absent, its threads' L2+L3 cacheinfo is unpopulated, and it is
missing a cache level in its sched domain hierarchy:
$ ls -1d */
l2-cache@2010/
l2-cache@2011/
l3-cache@3110/
l3-cache@3111/
PowerPC,POWER9@0/
PowerPC,POWER9@10/
PowerPC,POWER9@8/
$ lsprop PowerPC\,POWER9@10/l2-cache
PowerPC,POWER9@10/l2-cache
00002012 (8210)
$ ls /sys/devices/system/cpu/cpu16/cache/
index0 index1
$ grep . /sys/kernel/debug/sched/domains/cpu{0,8,16}/domain*/name
/sys/kernel/debug/sched/domains/cpu0/domain0/name:SMT
/sys/kernel/debug/sched/domains/cpu0/domain1/name:CACHE
/sys/kernel/debug/sched/domains/cpu0/domain2/name:DIE
/sys/kernel/debug/sched/domains/cpu8/domain0/name:SMT
/sys/kernel/debug/sched/domains/cpu8/domain1/name:CACHE
/sys/kernel/debug/sched/domains/cpu8/domain2/name:DIE
/sys/kernel/debug/sched/domains/cpu16/domain0/name:SMT
/sys/kernel/debug/sched/domains/cpu16/domain1/name:DIE
When removing PowerPC,POWER9@8, we see that its cache nodes are left
behind:
$ ls -1d */
l2-cache@2010/
l2-cache@2011/
l3-cache@3110/
l3-cache@3111/
PowerPC,POWER9@0/
When DLPAR is combined with VM migration, we can get duplicate nodes. E.g.
removing one processor, then migrating, adding a processor, and then
migrating again can result in warnings from the OF core during
post-migration device tree updates:
Duplicate name in cpus, renamed to "l2-cache@2011#1"
Duplicate name in cpus, renamed to "l3-cache@3111#1"
and nodes with duplicated phandles in the tree, making lookup behavior
unpredictable:
$ lsprop l[23]-cache@*/ibm,phandle
l2-cache@2010/ibm,phandle
00002010 (8208)
l2-cache@2011#1/ibm,phandle
00002011 (8209)
l2-cache@2011/ibm,phandle
00002011 (8209)
l3-cache@3110/ibm,phandle
00003110 (12560)
l3-cache@3111#1/ibm,phandle
00003111 (12561)
l3-cache@3111/ibm,phandle
00003111 (12561)
Address these issues by:
* Correctly processing siblings of the node returned from
dlpar_configure_connector().
* Removing cache nodes in the CPU remove path when it can be determined
that they are not associated with other CPUs or caches.
Use the of_changeset API in both cases, which allows us to keep the error
handling in this code from becoming more complex while ensuring that the
device tree cannot become inconsistent.
Signed-off-by: Nathan Lynch <redacted>
Fixes: ac71380 ("powerpc/pseries: Add CPU dlpar remove functionality")
Fixes: 90edf18 ("powerpc/pseries: Add CPU dlpar add functionality")
---
arch/powerpc/platforms/pseries/hotplug-cpu.c | 72 +++++++++++++++++++-
1 file changed, 70 insertions(+), 2 deletions(-)
@@ -563,7 +584,7 @@ static ssize_t dlpar_cpu_add(u32 drc_index)return-EINVAL;}-rc=dlpar_attach_node(dn,parent);+rc=pseries_cpuhp_attach_nodes(dn);/* Regardless we are done with parent now */of_node_put(parent);
If, due to bugs elsewhere, we get into unregister_cpu_online() with a CPU
that isn't marked hotpluggable, we can emit a warning and return an
appropriate error instead of crashing.
Signed-off-by: Nathan Lynch <redacted>
---
arch/powerpc/kernel/sysfs.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
@@ -928,7 +928,8 @@ static int unregister_cpu_online(unsigned int cpu)structdevice_attribute*attrs,*pmc_attrs;inti,nattrs;-BUG_ON(!c->hotpluggable);+if(WARN_RATELIMIT(!c->hotpluggable,"cpu %d can't be offlined\n",cpu))+return-EBUSY;#ifdef CONFIG_PPC64if(cpu_has_feature(CPU_FTR_SMT))
The core DLPAR code supports two actions (add and remove) and three
subtypes of action:
* By DRC index: the action is attempted on a single specified resource.
This is the usual case for processors.
* By indexed count: the action is attempted on a range of resources
beginning at the specified index. This is implemented only by the memory
DLPAR code.
* By count: the lower layer (CPU or memory) is responsible for locating the
specified number of resources to which the action can be applied.
I cannot find any evidence of the "by count" subtype being used by drmgr or
qemu for processors. And when I try to exercise this code, the add case
does not work:
$ ppc64_cpu --smt ; nproc
SMT=8
24
$ printf "cpu remove count 2" > /sys/kernel/dlpar
$ nproc
8
$ printf "cpu add count 2" > /sys/kernel/dlpar
-bash: printf: write error: Invalid argument
$ dmesg | tail -2
pseries-hotplug-cpu: Failed to find enough CPUs (1 of 2) to add
dlpar: Could not handle DLPAR request "cpu add count 2"
$ nproc
8
$ drmgr -c cpu -a -q 2 # this uses the by-index method
Validating CPU DLPAR capability...yes.
CPU 1
CPU 17
$ nproc
24
This is because find_drc_info_cpus_to_add() does not increment drc_index
appropriately during its search.
This is not hard to fix. But the _by_count() functions also have the
property that they attempt to roll back all prior operations if the entire
request cannot be satisfied, even though the rollback itself can encounter
errors. It's not possible to provide transaction-like behavior at this
level, and it's undesirable to have code that can only pretend to do that.
Any users of these functions cannot know what the state of the system is in
the error case. And the error paths are, to my knowledge, impossible to
test without adding custom error injection code.
Summary:
* This code has not worked reliably since its introduction.
* There is no evidence that it is used.
* It contains questionable rollback behaviors in error paths which are
difficult to test.
So let's remove it.
Signed-off-by: Nathan Lynch <redacted>
Fixes: ac71380071d1 ("powerpc/pseries: Add CPU dlpar remove functionality")
Fixes: 90edf184b9b7 ("powerpc/pseries: Add CPU dlpar add functionality")
Fixes: b015f6bc9547 ("powerpc/pseries: Add cpu DLPAR support for drc-info property")
---
arch/powerpc/platforms/pseries/hotplug-cpu.c | 218 +------------------
1 file changed, 2 insertions(+), 216 deletions(-)
@@ -741,216 +741,6 @@ static int dlpar_cpu_remove_by_index(u32 drc_index)returnrc;}-staticintfind_dlpar_cpus_to_remove(u32*cpu_drcs,intcpus_to_remove)-{-structdevice_node*dn;-intcpus_found=0;-intrc;--/* We want to find cpus_to_remove + 1 CPUs to ensure we do not-*removethelastCPU.-*/-for_each_node_by_type(dn,"cpu"){-cpus_found++;--if(cpus_found>cpus_to_remove){-of_node_put(dn);-break;-}--/* Note that cpus_found is always 1 ahead of the index-*intothecpu_drcsarray,soweusecpus_found-1-*/-rc=of_property_read_u32(dn,"ibm,my-drc-index",-&cpu_drcs[cpus_found-1]);-if(rc){-pr_warn("Error occurred getting drc-index for %pOFn\n",-dn);-of_node_put(dn);-return-1;-}-}--if(cpus_found<cpus_to_remove){-pr_warn("Failed to find enough CPUs (%d of %d) to remove\n",-cpus_found,cpus_to_remove);-}elseif(cpus_found==cpus_to_remove){-pr_warn("Cannot remove all CPUs\n");-}--returncpus_found;-}--staticintdlpar_cpu_remove_by_count(u32cpus_to_remove)-{-u32*cpu_drcs;-intcpus_found;-intcpus_removed=0;-inti,rc;--pr_debug("Attempting to hot-remove %d CPUs\n",cpus_to_remove);--cpu_drcs=kcalloc(cpus_to_remove,sizeof(*cpu_drcs),GFP_KERNEL);-if(!cpu_drcs)-return-EINVAL;--cpus_found=find_dlpar_cpus_to_remove(cpu_drcs,cpus_to_remove);-if(cpus_found<=cpus_to_remove){-kfree(cpu_drcs);-return-EINVAL;-}--for(i=0;i<cpus_to_remove;i++){-rc=dlpar_cpu_remove_by_index(cpu_drcs[i]);-if(rc)-break;--cpus_removed++;-}--if(cpus_removed!=cpus_to_remove){-pr_warn("CPU hot-remove failed, adding back removed CPUs\n");--for(i=0;i<cpus_removed;i++)-dlpar_cpu_add(cpu_drcs[i]);--rc=-EINVAL;-}else{-rc=0;-}--kfree(cpu_drcs);-returnrc;-}--staticintfind_drc_info_cpus_to_add(structdevice_node*cpus,-structproperty*info,-u32*cpu_drcs,u32cpus_to_add)-{-structof_drc_infodrc;-const__be32*value;-u32count,drc_index;-intcpus_found=0;-inti,j;--if(!info)-return-1;--value=of_prop_next_u32(info,NULL,&count);-if(value)-value++;--for(i=0;i<count;i++){-of_read_drc_info_cell(&info,&value,&drc);-if(strncmp(drc.drc_type,"CPU",3))-break;--drc_index=drc.drc_index_start;-for(j=0;j<drc.num_sequential_elems;j++){-if(dlpar_cpu_exists(cpus,drc_index))-continue;--cpu_drcs[cpus_found++]=drc_index;--if(cpus_found==cpus_to_add)-returncpus_found;--drc_index+=drc.sequential_inc;-}-}--returncpus_found;-}--staticintfind_drc_index_cpus_to_add(structdevice_node*cpus,-u32*cpu_drcs,u32cpus_to_add)-{-intcpus_found=0;-intindex,rc;-u32drc_index;--/* Search the ibm,drc-indexes array for possible CPU drcs to-*add.Notethattheformatoftheibm,drc-indexesarrayis-*thenumberofentriesinthearrayfollowedbythearray-*ofdrcvaluessowestartlookingatindex=1.-*/-index=1;-while(cpus_found<cpus_to_add){-rc=of_property_read_u32_index(cpus,"ibm,drc-indexes",-index++,&drc_index);--if(rc)-break;--if(dlpar_cpu_exists(cpus,drc_index))-continue;--cpu_drcs[cpus_found++]=drc_index;-}--returncpus_found;-}--staticintdlpar_cpu_add_by_count(u32cpus_to_add)-{-structdevice_node*parent;-structproperty*info;-u32*cpu_drcs;-intcpus_added=0;-intcpus_found;-inti,rc;--pr_debug("Attempting to hot-add %d CPUs\n",cpus_to_add);--cpu_drcs=kcalloc(cpus_to_add,sizeof(*cpu_drcs),GFP_KERNEL);-if(!cpu_drcs)-return-EINVAL;--parent=of_find_node_by_path("/cpus");-if(!parent){-pr_warn("Could not find CPU root node in device tree\n");-kfree(cpu_drcs);-return-1;-}--info=of_find_property(parent,"ibm,drc-info",NULL);-if(info)-cpus_found=find_drc_info_cpus_to_add(parent,info,cpu_drcs,cpus_to_add);-else-cpus_found=find_drc_index_cpus_to_add(parent,cpu_drcs,cpus_to_add);--of_node_put(parent);--if(cpus_found<cpus_to_add){-pr_warn("Failed to find enough CPUs (%d of %d) to add\n",-cpus_found,cpus_to_add);-kfree(cpu_drcs);-return-EINVAL;-}--for(i=0;i<cpus_to_add;i++){-rc=dlpar_cpu_add(cpu_drcs[i]);-if(rc)-break;--cpus_added++;-}--if(cpus_added<cpus_to_add){-pr_warn("CPU hot-add failed, removing any added CPUs\n");--for(i=0;i<cpus_added;i++)-dlpar_cpu_remove_by_index(cpu_drcs[i]);--rc=-EINVAL;-}else{-rc=0;-}--kfree(cpu_drcs);-returnrc;-}-intdlpar_cpu(structpseries_hp_errorlog*hp_elog){u32count,drc_index;
@@ -963,9 +753,7 @@ int dlpar_cpu(struct pseries_hp_errorlog *hp_elog)switch(hp_elog->action){casePSERIES_HP_ELOG_ACTION_REMOVE:-if(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_COUNT)-rc=dlpar_cpu_remove_by_count(count);-elseif(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_INDEX){+if(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_INDEX){rc=dlpar_cpu_remove_by_index(drc_index);/**SettingtheisolationstateofanUNISOLATED/CONFIGURED
@@ -979,9 +767,7 @@ int dlpar_cpu(struct pseries_hp_errorlog *hp_elog)rc=-EINVAL;break;casePSERIES_HP_ELOG_ACTION_ADD:-if(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_COUNT)-rc=dlpar_cpu_add_by_count(count);-elseif(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_INDEX)+if(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_INDEX)rc=dlpar_cpu_add(drc_index);elserc=-EINVAL;
If, due to bugs elsewhere, we get into unregister_cpu_online() with a CPU
that isn't marked hotpluggable, we can emit a warning and return an
appropriate error instead of crashing.
Is it only a bug situation, or is it something that can happen in real
life ?
If it can happen in real life, kernels with panic_on_warn will still be
impacted.
@@ -928,7 +928,8 @@ static int unregister_cpu_online(unsigned int cpu)structdevice_attribute*attrs,*pmc_attrs;inti,nattrs;-BUG_ON(!c->hotpluggable);+if(WARN_RATELIMIT(!c->hotpluggable,"cpu %d can't be offlined\n",cpu))+return-EBUSY;#ifdef CONFIG_PPC64if(cpu_has_feature(CPU_FTR_SMT))
If, due to bugs elsewhere, we get into unregister_cpu_online() with a CPU
that isn't marked hotpluggable, we can emit a warning and return an
appropriate error instead of crashing.
Is it only a bug situation, or is it something that can happen in real
life ?
If it can happen in real life, kernels with panic_on_warn will still be
impacted.
I only found this by inspection, and it can happen only due to a bug in
CPU device registration at boot. The flag must not be set if the
platform or CPU can't support going offline.
From: Daniel Henrique Barboza <hidden> Date: 2021-09-21 00:00:06
On 9/20/21 10:55, Nathan Lynch wrote:
On pseries, cache nodes in the device tree can be added and removed by the
CPU DLPAR code as well as the partition migration (mobility) code. PowerVM
partitions in dedicated processor mode typically have L2 and L3 cache
nodes.
The CPU DLPAR code has the following shortcomings:
* Cache nodes returned as siblings of a new CPU node by
ibm,configure-connector are silently discarded; only the CPU node is
added to the device tree.
* Cache nodes which become unreferenced in the processor removal path are
not removed from the device tree. This can lead to duplicate nodes when
the post-migration device tree update code replaces cache nodes.
This is long-standing behavior. Presumably it has gone mostly unnoticed
because the two bugs have the property of obscuring each other in common
simple scenarios (e.g. remove a CPU and add it back). Likely you'd notice
only if you cared to inspect the device tree or the sysfs cacheinfo
information.
Booted with two processors:
$ pwd
/sys/firmware/devicetree/base/cpus
$ ls -1d */
l2-cache@2010/
l2-cache@2011/
l3-cache@3110/
l3-cache@3111/
PowerPC,POWER9@0/
PowerPC,POWER9@8/
$ lsprop */l2-cache
l2-cache@2010/l2-cache
00003110 (12560)
l2-cache@2011/l2-cache
00003111 (12561)
PowerPC,POWER9@0/l2-cache
00002010 (8208)
PowerPC,POWER9@8/l2-cache
00002011 (8209)
$ ls /sys/devices/system/cpu/cpu0/cache/
index0 index1 index2 index3
After DLPAR-adding PowerPC,POWER9@10, we see that its associated cache
nodes are absent, its threads' L2+L3 cacheinfo is unpopulated, and it is
missing a cache level in its sched domain hierarchy:
$ ls -1d */
l2-cache@2010/
l2-cache@2011/
l3-cache@3110/
l3-cache@3111/
PowerPC,POWER9@0/
PowerPC,POWER9@10/
PowerPC,POWER9@8/
$ lsprop PowerPC\,POWER9@10/l2-cache
PowerPC,POWER9@10/l2-cache
00002012 (8210)
$ ls /sys/devices/system/cpu/cpu16/cache/
index0 index1
$ grep . /sys/kernel/debug/sched/domains/cpu{0,8,16}/domain*/name
/sys/kernel/debug/sched/domains/cpu0/domain0/name:SMT
/sys/kernel/debug/sched/domains/cpu0/domain1/name:CACHE
/sys/kernel/debug/sched/domains/cpu0/domain2/name:DIE
/sys/kernel/debug/sched/domains/cpu8/domain0/name:SMT
/sys/kernel/debug/sched/domains/cpu8/domain1/name:CACHE
/sys/kernel/debug/sched/domains/cpu8/domain2/name:DIE
/sys/kernel/debug/sched/domains/cpu16/domain0/name:SMT
/sys/kernel/debug/sched/domains/cpu16/domain1/name:DIE
When removing PowerPC,POWER9@8, we see that its cache nodes are left
behind:
$ ls -1d */
l2-cache@2010/
l2-cache@2011/
l3-cache@3110/
l3-cache@3111/
PowerPC,POWER9@0/
When DLPAR is combined with VM migration, we can get duplicate nodes. E.g.
removing one processor, then migrating, adding a processor, and then
migrating again can result in warnings from the OF core during
post-migration device tree updates:
Duplicate name in cpus, renamed to "l2-cache@2011#1"
Duplicate name in cpus, renamed to "l3-cache@3111#1"
and nodes with duplicated phandles in the tree, making lookup behavior
unpredictable:
$ lsprop l[23]-cache@*/ibm,phandle
l2-cache@2010/ibm,phandle
00002010 (8208)
l2-cache@2011#1/ibm,phandle
00002011 (8209)
l2-cache@2011/ibm,phandle
00002011 (8209)
l3-cache@3110/ibm,phandle
00003110 (12560)
l3-cache@3111#1/ibm,phandle
00003111 (12561)
l3-cache@3111/ibm,phandle
00003111 (12561)
Address these issues by:
* Correctly processing siblings of the node returned from
dlpar_configure_connector().
* Removing cache nodes in the CPU remove path when it can be determined
that they are not associated with other CPUs or caches.
Use the of_changeset API in both cases, which allows us to keep the error
handling in this code from becoming more complex while ensuring that the
device tree cannot become inconsistent.
Signed-off-by: Nathan Lynch <redacted>
Fixes: ac71380 ("powerpc/pseries: Add CPU dlpar remove functionality")
Fixes: 90edf18 ("powerpc/pseries: Add CPU dlpar add functionality")
---
arch/powerpc/platforms/pseries/hotplug-cpu.c | 72 +++++++++++++++++++-
1 file changed, 70 insertions(+), 2 deletions(-)
Tested with a QEMU pseries guest, multiple CPU add/removals of the same CPU,
and no issues found with these new pseries_cpuhp* functions.
Code LGTM as well.
Reviewed-by: Daniel Henrique Barboza <redacted>
Tested-by: Daniel Henrique Barboza <redacted>
@@ -563,7 +584,7 @@ static ssize_t dlpar_cpu_add(u32 drc_index)return-EINVAL;}-rc=dlpar_attach_node(dn,parent);+rc=pseries_cpuhp_attach_nodes(dn);/* Regardless we are done with parent now */of_node_put(parent);
From: Daniel Henrique Barboza <hidden> Date: 2021-09-21 00:06:27
On 9/20/21 10:55, Nathan Lynch wrote:
If, due to bugs elsewhere, we get into unregister_cpu_online() with a CPU
that isn't marked hotpluggable, we can emit a warning and return an
appropriate error instead of crashing.
Signed-off-by: Nathan Lynch <redacted>
---
As mentioned by Christophe this will not solve the crash for kernels with
panic_on_warn, but at least it will panic with a clearer message on those
and will not panic for everyone else.
Reviewed-by: Daniel Henrique Barboza <redacted>
@@ -928,7 +928,8 @@ static int unregister_cpu_online(unsigned int cpu)structdevice_attribute*attrs,*pmc_attrs;inti,nattrs;-BUG_ON(!c->hotpluggable);+if(WARN_RATELIMIT(!c->hotpluggable,"cpu %d can't be offlined\n",cpu))+return-EBUSY;#ifdef CONFIG_PPC64if(cpu_has_feature(CPU_FTR_SMT))
From: Daniel Henrique Barboza <hidden> Date: 2021-09-21 00:19:43
On 9/20/21 10:55, Nathan Lynch wrote:
The core DLPAR code supports two actions (add and remove) and three
subtypes of action:
* By DRC index: the action is attempted on a single specified resource.
This is the usual case for processors.
* By indexed count: the action is attempted on a range of resources
beginning at the specified index. This is implemented only by the memory
DLPAR code.
* By count: the lower layer (CPU or memory) is responsible for locating the
specified number of resources to which the action can be applied.
I cannot find any evidence of the "by count" subtype being used by drmgr or
qemu for processors. And when I try to exercise this code, the add case
does not work:
Just to clarify: did you check both CPU and memory cases and found out that the
'by count' subtype isn't used with CPUs, but drmgr has some cases in which
'by count' is used with LMBs?
I'm asking because I worked with a part of the LMB removal code a few months ago,
and got stuck in a situation in which the 'by count' and 'by indexed count' are
similar enough to feel repetitive, but distinct enough to not be easily reduced
into a single function. If drmgr wasn't using the 'by count' subtypes for LMBs
that would be a good chance for more code redux.
$ ppc64_cpu --smt ; nproc
SMT=8
24
$ printf "cpu remove count 2" > /sys/kernel/dlpar
$ nproc
8
$ printf "cpu add count 2" > /sys/kernel/dlpar
-bash: printf: write error: Invalid argument
$ dmesg | tail -2
pseries-hotplug-cpu: Failed to find enough CPUs (1 of 2) to add
dlpar: Could not handle DLPAR request "cpu add count 2"
$ nproc
8
$ drmgr -c cpu -a -q 2 # this uses the by-index method
Validating CPU DLPAR capability...yes.
CPU 1
CPU 17
$ nproc
24
This is because find_drc_info_cpus_to_add() does not increment drc_index
appropriately during its search.
This is not hard to fix. But the _by_count() functions also have the
property that they attempt to roll back all prior operations if the entire
request cannot be satisfied, even though the rollback itself can encounter
errors. It's not possible to provide transaction-like behavior at this
level, and it's undesirable to have code that can only pretend to do that.
Any users of these functions cannot know what the state of the system is in
the error case. And the error paths are, to my knowledge, impossible to
test without adding custom error injection code.
Summary:
* This code has not worked reliably since its introduction.
* There is no evidence that it is used.
* It contains questionable rollback behaviors in error paths which are
difficult to test.
So let's remove it.
Signed-off-by: Nathan Lynch <redacted>
Fixes: ac71380071d1 ("powerpc/pseries: Add CPU dlpar remove functionality")
Fixes: 90edf184b9b7 ("powerpc/pseries: Add CPU dlpar add functionality")
Fixes: b015f6bc9547 ("powerpc/pseries: Add cpu DLPAR support for drc-info property")
---
Tested with a QEMU pseries guest, no issues found.
Reviewed-by: Daniel Henrique Barboza <redacted>
Tested-by: Daniel Henrique Barboza <redacted>
@@ -741,216 +741,6 @@ static int dlpar_cpu_remove_by_index(u32 drc_index)returnrc;}-staticintfind_dlpar_cpus_to_remove(u32*cpu_drcs,intcpus_to_remove)-{-structdevice_node*dn;-intcpus_found=0;-intrc;--/* We want to find cpus_to_remove + 1 CPUs to ensure we do not-*removethelastCPU.-*/-for_each_node_by_type(dn,"cpu"){-cpus_found++;--if(cpus_found>cpus_to_remove){-of_node_put(dn);-break;-}--/* Note that cpus_found is always 1 ahead of the index-*intothecpu_drcsarray,soweusecpus_found-1-*/-rc=of_property_read_u32(dn,"ibm,my-drc-index",-&cpu_drcs[cpus_found-1]);-if(rc){-pr_warn("Error occurred getting drc-index for %pOFn\n",-dn);-of_node_put(dn);-return-1;-}-}--if(cpus_found<cpus_to_remove){-pr_warn("Failed to find enough CPUs (%d of %d) to remove\n",-cpus_found,cpus_to_remove);-}elseif(cpus_found==cpus_to_remove){-pr_warn("Cannot remove all CPUs\n");-}--returncpus_found;-}--staticintdlpar_cpu_remove_by_count(u32cpus_to_remove)-{-u32*cpu_drcs;-intcpus_found;-intcpus_removed=0;-inti,rc;--pr_debug("Attempting to hot-remove %d CPUs\n",cpus_to_remove);--cpu_drcs=kcalloc(cpus_to_remove,sizeof(*cpu_drcs),GFP_KERNEL);-if(!cpu_drcs)-return-EINVAL;--cpus_found=find_dlpar_cpus_to_remove(cpu_drcs,cpus_to_remove);-if(cpus_found<=cpus_to_remove){-kfree(cpu_drcs);-return-EINVAL;-}--for(i=0;i<cpus_to_remove;i++){-rc=dlpar_cpu_remove_by_index(cpu_drcs[i]);-if(rc)-break;--cpus_removed++;-}--if(cpus_removed!=cpus_to_remove){-pr_warn("CPU hot-remove failed, adding back removed CPUs\n");--for(i=0;i<cpus_removed;i++)-dlpar_cpu_add(cpu_drcs[i]);--rc=-EINVAL;-}else{-rc=0;-}--kfree(cpu_drcs);-returnrc;-}--staticintfind_drc_info_cpus_to_add(structdevice_node*cpus,-structproperty*info,-u32*cpu_drcs,u32cpus_to_add)-{-structof_drc_infodrc;-const__be32*value;-u32count,drc_index;-intcpus_found=0;-inti,j;--if(!info)-return-1;--value=of_prop_next_u32(info,NULL,&count);-if(value)-value++;--for(i=0;i<count;i++){-of_read_drc_info_cell(&info,&value,&drc);-if(strncmp(drc.drc_type,"CPU",3))-break;--drc_index=drc.drc_index_start;-for(j=0;j<drc.num_sequential_elems;j++){-if(dlpar_cpu_exists(cpus,drc_index))-continue;--cpu_drcs[cpus_found++]=drc_index;--if(cpus_found==cpus_to_add)-returncpus_found;--drc_index+=drc.sequential_inc;-}-}--returncpus_found;-}--staticintfind_drc_index_cpus_to_add(structdevice_node*cpus,-u32*cpu_drcs,u32cpus_to_add)-{-intcpus_found=0;-intindex,rc;-u32drc_index;--/* Search the ibm,drc-indexes array for possible CPU drcs to-*add.Notethattheformatoftheibm,drc-indexesarrayis-*thenumberofentriesinthearrayfollowedbythearray-*ofdrcvaluessowestartlookingatindex=1.-*/-index=1;-while(cpus_found<cpus_to_add){-rc=of_property_read_u32_index(cpus,"ibm,drc-indexes",-index++,&drc_index);--if(rc)-break;--if(dlpar_cpu_exists(cpus,drc_index))-continue;--cpu_drcs[cpus_found++]=drc_index;-}--returncpus_found;-}--staticintdlpar_cpu_add_by_count(u32cpus_to_add)-{-structdevice_node*parent;-structproperty*info;-u32*cpu_drcs;-intcpus_added=0;-intcpus_found;-inti,rc;--pr_debug("Attempting to hot-add %d CPUs\n",cpus_to_add);--cpu_drcs=kcalloc(cpus_to_add,sizeof(*cpu_drcs),GFP_KERNEL);-if(!cpu_drcs)-return-EINVAL;--parent=of_find_node_by_path("/cpus");-if(!parent){-pr_warn("Could not find CPU root node in device tree\n");-kfree(cpu_drcs);-return-1;-}--info=of_find_property(parent,"ibm,drc-info",NULL);-if(info)-cpus_found=find_drc_info_cpus_to_add(parent,info,cpu_drcs,cpus_to_add);-else-cpus_found=find_drc_index_cpus_to_add(parent,cpu_drcs,cpus_to_add);--of_node_put(parent);--if(cpus_found<cpus_to_add){-pr_warn("Failed to find enough CPUs (%d of %d) to add\n",-cpus_found,cpus_to_add);-kfree(cpu_drcs);-return-EINVAL;-}--for(i=0;i<cpus_to_add;i++){-rc=dlpar_cpu_add(cpu_drcs[i]);-if(rc)-break;--cpus_added++;-}--if(cpus_added<cpus_to_add){-pr_warn("CPU hot-add failed, removing any added CPUs\n");--for(i=0;i<cpus_added;i++)-dlpar_cpu_remove_by_index(cpu_drcs[i]);--rc=-EINVAL;-}else{-rc=0;-}--kfree(cpu_drcs);-returnrc;-}-intdlpar_cpu(structpseries_hp_errorlog*hp_elog){u32count,drc_index;
@@ -963,9 +753,7 @@ int dlpar_cpu(struct pseries_hp_errorlog *hp_elog)switch(hp_elog->action){casePSERIES_HP_ELOG_ACTION_REMOVE:-if(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_COUNT)-rc=dlpar_cpu_remove_by_count(count);-elseif(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_INDEX){+if(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_INDEX){rc=dlpar_cpu_remove_by_index(drc_index);/**SettingtheisolationstateofanUNISOLATED/CONFIGURED
@@ -979,9 +767,7 @@ int dlpar_cpu(struct pseries_hp_errorlog *hp_elog)rc=-EINVAL;break;casePSERIES_HP_ELOG_ACTION_ADD:-if(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_COUNT)-rc=dlpar_cpu_add_by_count(count);-elseif(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_INDEX)+if(hp_elog->id_type==PSERIES_HP_ELOG_ID_DRC_INDEX)rc=dlpar_cpu_add(drc_index);elserc=-EINVAL;
The core DLPAR code supports two actions (add and remove) and three
subtypes of action:
* By DRC index: the action is attempted on a single specified resource.
This is the usual case for processors.
* By indexed count: the action is attempted on a range of resources
beginning at the specified index. This is implemented only by the memory
DLPAR code.
* By count: the lower layer (CPU or memory) is responsible for locating the
specified number of resources to which the action can be applied.
I cannot find any evidence of the "by count" subtype being used by drmgr or
qemu for processors. And when I try to exercise this code, the add case
does not work:
Just to clarify: did you check both CPU and memory cases and found out that the
'by count' subtype isn't used with CPUs, but drmgr has some cases in which
'by count' is used with LMBs?
Yes, drmgr uses both the 'by count' and the 'by index' methods for
memory currently on PowerVM.
I'm asking because I worked with a part of the LMB removal code a few months ago,
and got stuck in a situation in which the 'by count' and 'by indexed count' are
similar enough to feel repetitive, but distinct enough to not be easily reduced
into a single function. If drmgr wasn't using the 'by count' subtypes for LMBs
that would be a good chance for more code redux.
The 'by count' method is definitely used for memory on PowerVM. I was
under the impression that the 'by indexed count' method was used by qemu
for memory sometimes; I'm pretty sure it's not used on PowerVM.
quoted
Summary:
* This code has not worked reliably since its introduction.
* There is no evidence that it is used.
* It contains questionable rollback behaviors in error paths which are
difficult to test.
So let's remove it.
Signed-off-by: Nathan Lynch <redacted>
Fixes: ac71380071d1 ("powerpc/pseries: Add CPU dlpar remove functionality")
Fixes: 90edf184b9b7 ("powerpc/pseries: Add CPU dlpar add functionality")
Fixes: b015f6bc9547 ("powerpc/pseries: Add cpu DLPAR support for drc-info property")
---
Tested with a QEMU pseries guest, no issues found.
Reviewed-by: Daniel Henrique Barboza <redacted>
Tested-by: Daniel Henrique Barboza <redacted>