aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/pseries/hotplug-cpu.c
diff options
context:
space:
mode:
authorNathan Fontenot <nfont@linux.vnet.ibm.com>2015-12-16 15:54:05 -0500
committerMichael Ellerman <mpe@ellerman.id.au>2015-12-17 06:41:02 -0500
commitac71380071d19d4ac7cd5f9fe4168d7109902cd5 (patch)
tree092123a664316847583f1af50483d42d788186c0 /arch/powerpc/platforms/pseries/hotplug-cpu.c
parente666ae0b10aaa1c961c928558bafc28bc049ac87 (diff)
powerpc/pseries: Add CPU dlpar remove functionality
Add the ability to dlpar remove CPUs via hotplug rtas events, either by specifying the drc-index of the CPU to remove or providing a count of cpus to remove. To remove multiple cpus in a single request we create a list of possible DR (Dynamic Reconfiguration) cpus and their drc indexes that can be removed. We can then traverse the list remove each cpu and easily clean up in any cases of failure. Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'arch/powerpc/platforms/pseries/hotplug-cpu.c')
-rw-r--r--arch/powerpc/platforms/pseries/hotplug-cpu.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
index a54aee982589..86c7ae3db50e 100644
--- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -26,6 +26,7 @@
26#include <linux/sched.h> /* for idle_task_exit */ 26#include <linux/sched.h> /* for idle_task_exit */
27#include <linux/cpu.h> 27#include <linux/cpu.h>
28#include <linux/of.h> 28#include <linux/of.h>
29#include <linux/slab.h>
29#include <asm/prom.h> 30#include <asm/prom.h>
30#include <asm/rtas.h> 31#include <asm/rtas.h>
31#include <asm/firmware.h> 32#include <asm/firmware.h>
@@ -565,6 +566,152 @@ static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index)
565 return 0; 566 return 0;
566} 567}
567 568
569static struct device_node *cpu_drc_index_to_dn(u32 drc_index)
570{
571 struct device_node *dn;
572 u32 my_index;
573 int rc;
574
575 for_each_node_by_type(dn, "cpu") {
576 rc = of_property_read_u32(dn, "ibm,my-drc-index", &my_index);
577 if (rc)
578 continue;
579
580 if (my_index == drc_index)
581 break;
582 }
583
584 return dn;
585}
586
587static int dlpar_cpu_remove_by_index(u32 drc_index)
588{
589 struct device_node *dn;
590 int rc;
591
592 dn = cpu_drc_index_to_dn(drc_index);
593 if (!dn) {
594 pr_warn("Cannot find CPU (drc index %x) to remove\n",
595 drc_index);
596 return -ENODEV;
597 }
598
599 rc = dlpar_cpu_remove(dn, drc_index);
600 of_node_put(dn);
601 return rc;
602}
603
604static int find_dlpar_cpus_to_remove(u32 *cpu_drcs, int cpus_to_remove)
605{
606 struct device_node *dn;
607 int cpus_found = 0;
608 int rc;
609
610 /* We want to find cpus_to_remove + 1 CPUs to ensure we do not
611 * remove the last CPU.
612 */
613 for_each_node_by_type(dn, "cpu") {
614 cpus_found++;
615
616 if (cpus_found > cpus_to_remove) {
617 of_node_put(dn);
618 break;
619 }
620
621 /* Note that cpus_found is always 1 ahead of the index
622 * into the cpu_drcs array, so we use cpus_found - 1
623 */
624 rc = of_property_read_u32(dn, "ibm,my-drc-index",
625 &cpu_drcs[cpus_found - 1]);
626 if (rc) {
627 pr_warn("Error occurred getting drc-index for %s\n",
628 dn->name);
629 of_node_put(dn);
630 return -1;
631 }
632 }
633
634 if (cpus_found < cpus_to_remove) {
635 pr_warn("Failed to find enough CPUs (%d of %d) to remove\n",
636 cpus_found, cpus_to_remove);
637 } else if (cpus_found == cpus_to_remove) {
638 pr_warn("Cannot remove all CPUs\n");
639 }
640
641 return cpus_found;
642}
643
644static int dlpar_cpu_remove_by_count(u32 cpus_to_remove)
645{
646 u32 *cpu_drcs;
647 int cpus_found;
648 int cpus_removed = 0;
649 int i, rc;
650
651 pr_debug("Attempting to hot-remove %d CPUs\n", cpus_to_remove);
652
653 cpu_drcs = kcalloc(cpus_to_remove, sizeof(*cpu_drcs), GFP_KERNEL);
654 if (!cpu_drcs)
655 return -EINVAL;
656
657 cpus_found = find_dlpar_cpus_to_remove(cpu_drcs, cpus_to_remove);
658 if (cpus_found <= cpus_to_remove) {
659 kfree(cpu_drcs);
660 return -EINVAL;
661 }
662
663 for (i = 0; i < cpus_to_remove; i++) {
664 rc = dlpar_cpu_remove_by_index(cpu_drcs[i]);
665 if (rc)
666 break;
667
668 cpus_removed++;
669 }
670
671 if (cpus_removed != cpus_to_remove) {
672 pr_warn("CPU hot-remove failed, adding back removed CPUs\n");
673
674 for (i = 0; i < cpus_removed; i++)
675 dlpar_cpu_add(cpu_drcs[i]);
676
677 rc = -EINVAL;
678 } else {
679 rc = 0;
680 }
681
682 kfree(cpu_drcs);
683 return rc;
684}
685
686int dlpar_cpu(struct pseries_hp_errorlog *hp_elog)
687{
688 u32 count, drc_index;
689 int rc;
690
691 count = hp_elog->_drc_u.drc_count;
692 drc_index = hp_elog->_drc_u.drc_index;
693
694 lock_device_hotplug();
695
696 switch (hp_elog->action) {
697 case PSERIES_HP_ELOG_ACTION_REMOVE:
698 if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT)
699 rc = dlpar_cpu_remove_by_count(count);
700 else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX)
701 rc = dlpar_cpu_remove_by_index(drc_index);
702 else
703 rc = -EINVAL;
704 break;
705 default:
706 pr_err("Invalid action (%d) specified\n", hp_elog->action);
707 rc = -EINVAL;
708 break;
709 }
710
711 unlock_device_hotplug();
712 return rc;
713}
714
568#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 715#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
569 716
570static ssize_t dlpar_cpu_probe(const char *buf, size_t count) 717static ssize_t dlpar_cpu_probe(const char *buf, size_t count)