diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2016-02-26 13:43:32 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2016-03-01 14:36:55 -0500 |
commit | 757c989b9994f51b42d6be1bd33c7c12d16a3ac7 (patch) | |
tree | 2cc7cbece632e883aa76b03fe38417f843a56ab3 | |
parent | 98f8cdce1db580b99fce823a48eea2cb2bdb261e (diff) |
cpu/hotplug: Make target state writeable
Make it possible to write a target state to the per cpu state file, so we can
switch between states.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-arch@vger.kernel.org
Cc: Rik van Riel <riel@redhat.com>
Cc: Rafael Wysocki <rafael.j.wysocki@intel.com>
Cc: "Srivatsa S. Bhat" <srivatsa@mit.edu>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Arjan van de Ven <arjan@linux.intel.com>
Cc: Sebastian Siewior <bigeasy@linutronix.de>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Paul Turner <pjt@google.com>
Link: http://lkml.kernel.org/r/20160226182341.022814799@linutronix.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | kernel/cpu.c | 73 | ||||
-rw-r--r-- | lib/Kconfig.debug | 13 |
2 files changed, 78 insertions, 8 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index 1979b8927b86..be9335da82f1 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
@@ -48,12 +48,14 @@ static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state); | |||
48 | * @teardown: Teardown function of the step | 48 | * @teardown: Teardown function of the step |
49 | * @skip_onerr: Do not invoke the functions on error rollback | 49 | * @skip_onerr: Do not invoke the functions on error rollback |
50 | * Will go away once the notifiers are gone | 50 | * Will go away once the notifiers are gone |
51 | * @cant_stop: Bringup/teardown can't be stopped at this step | ||
51 | */ | 52 | */ |
52 | struct cpuhp_step { | 53 | struct cpuhp_step { |
53 | const char *name; | 54 | const char *name; |
54 | int (*startup)(unsigned int cpu); | 55 | int (*startup)(unsigned int cpu); |
55 | int (*teardown)(unsigned int cpu); | 56 | int (*teardown)(unsigned int cpu); |
56 | bool skip_onerr; | 57 | bool skip_onerr; |
58 | bool cant_stop; | ||
57 | }; | 59 | }; |
58 | 60 | ||
59 | static DEFINE_MUTEX(cpuhp_state_mutex); | 61 | static DEFINE_MUTEX(cpuhp_state_mutex); |
@@ -558,7 +560,7 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen, | |||
558 | if (num_online_cpus() == 1) | 560 | if (num_online_cpus() == 1) |
559 | return -EBUSY; | 561 | return -EBUSY; |
560 | 562 | ||
561 | if (!cpu_online(cpu)) | 563 | if (!cpu_present(cpu)) |
562 | return -EINVAL; | 564 | return -EINVAL; |
563 | 565 | ||
564 | cpu_hotplug_begin(); | 566 | cpu_hotplug_begin(); |
@@ -683,16 +685,25 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen, enum cpuhp_state target) | |||
683 | 685 | ||
684 | cpu_hotplug_begin(); | 686 | cpu_hotplug_begin(); |
685 | 687 | ||
686 | if (cpu_online(cpu) || !cpu_present(cpu)) { | 688 | if (!cpu_present(cpu)) { |
687 | ret = -EINVAL; | 689 | ret = -EINVAL; |
688 | goto out; | 690 | goto out; |
689 | } | 691 | } |
690 | 692 | ||
691 | /* Let it fail before we try to bring the cpu up */ | 693 | /* |
692 | idle = idle_thread_get(cpu); | 694 | * The caller of do_cpu_up might have raced with another |
693 | if (IS_ERR(idle)) { | 695 | * caller. Ignore it for now. |
694 | ret = PTR_ERR(idle); | 696 | */ |
697 | if (st->state >= target) | ||
695 | goto out; | 698 | goto out; |
699 | |||
700 | if (st->state == CPUHP_OFFLINE) { | ||
701 | /* Let it fail before we try to bring the cpu up */ | ||
702 | idle = idle_thread_get(cpu); | ||
703 | if (IS_ERR(idle)) { | ||
704 | ret = PTR_ERR(idle); | ||
705 | goto out; | ||
706 | } | ||
696 | } | 707 | } |
697 | 708 | ||
698 | cpuhp_tasks_frozen = tasks_frozen; | 709 | cpuhp_tasks_frozen = tasks_frozen; |
@@ -909,27 +920,32 @@ static struct cpuhp_step cpuhp_bp_states[] = { | |||
909 | .name = "threads:create", | 920 | .name = "threads:create", |
910 | .startup = smpboot_create_threads, | 921 | .startup = smpboot_create_threads, |
911 | .teardown = NULL, | 922 | .teardown = NULL, |
923 | .cant_stop = true, | ||
912 | }, | 924 | }, |
913 | [CPUHP_NOTIFY_PREPARE] = { | 925 | [CPUHP_NOTIFY_PREPARE] = { |
914 | .name = "notify:prepare", | 926 | .name = "notify:prepare", |
915 | .startup = notify_prepare, | 927 | .startup = notify_prepare, |
916 | .teardown = notify_dead, | 928 | .teardown = notify_dead, |
917 | .skip_onerr = true, | 929 | .skip_onerr = true, |
930 | .cant_stop = true, | ||
918 | }, | 931 | }, |
919 | [CPUHP_BRINGUP_CPU] = { | 932 | [CPUHP_BRINGUP_CPU] = { |
920 | .name = "cpu:bringup", | 933 | .name = "cpu:bringup", |
921 | .startup = bringup_cpu, | 934 | .startup = bringup_cpu, |
922 | .teardown = NULL, | 935 | .teardown = NULL, |
936 | .cant_stop = true, | ||
923 | }, | 937 | }, |
924 | [CPUHP_TEARDOWN_CPU] = { | 938 | [CPUHP_TEARDOWN_CPU] = { |
925 | .name = "cpu:teardown", | 939 | .name = "cpu:teardown", |
926 | .startup = NULL, | 940 | .startup = NULL, |
927 | .teardown = takedown_cpu, | 941 | .teardown = takedown_cpu, |
942 | .cant_stop = true, | ||
928 | }, | 943 | }, |
929 | [CPUHP_NOTIFY_ONLINE] = { | 944 | [CPUHP_NOTIFY_ONLINE] = { |
930 | .name = "notify:online", | 945 | .name = "notify:online", |
931 | .startup = notify_online, | 946 | .startup = notify_online, |
932 | .teardown = notify_down_prepare, | 947 | .teardown = notify_down_prepare, |
948 | .cant_stop = true, | ||
933 | }, | 949 | }, |
934 | #endif | 950 | #endif |
935 | [CPUHP_ONLINE] = { | 951 | [CPUHP_ONLINE] = { |
@@ -947,6 +963,7 @@ static struct cpuhp_step cpuhp_ap_states[] = { | |||
947 | .startup = notify_starting, | 963 | .startup = notify_starting, |
948 | .teardown = notify_dying, | 964 | .teardown = notify_dying, |
949 | .skip_onerr = true, | 965 | .skip_onerr = true, |
966 | .cant_stop = true, | ||
950 | }, | 967 | }, |
951 | #endif | 968 | #endif |
952 | [CPUHP_ONLINE] = { | 969 | [CPUHP_ONLINE] = { |
@@ -979,6 +996,46 @@ static ssize_t show_cpuhp_state(struct device *dev, | |||
979 | } | 996 | } |
980 | static DEVICE_ATTR(state, 0444, show_cpuhp_state, NULL); | 997 | static DEVICE_ATTR(state, 0444, show_cpuhp_state, NULL); |
981 | 998 | ||
999 | static ssize_t write_cpuhp_target(struct device *dev, | ||
1000 | struct device_attribute *attr, | ||
1001 | const char *buf, size_t count) | ||
1002 | { | ||
1003 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, dev->id); | ||
1004 | struct cpuhp_step *sp; | ||
1005 | int target, ret; | ||
1006 | |||
1007 | ret = kstrtoint(buf, 10, &target); | ||
1008 | if (ret) | ||
1009 | return ret; | ||
1010 | |||
1011 | #ifdef CONFIG_CPU_HOTPLUG_STATE_CONTROL | ||
1012 | if (target < CPUHP_OFFLINE || target > CPUHP_ONLINE) | ||
1013 | return -EINVAL; | ||
1014 | #else | ||
1015 | if (target != CPUHP_OFFLINE && target != CPUHP_ONLINE) | ||
1016 | return -EINVAL; | ||
1017 | #endif | ||
1018 | |||
1019 | ret = lock_device_hotplug_sysfs(); | ||
1020 | if (ret) | ||
1021 | return ret; | ||
1022 | |||
1023 | mutex_lock(&cpuhp_state_mutex); | ||
1024 | sp = cpuhp_get_step(target); | ||
1025 | ret = !sp->name || sp->cant_stop ? -EINVAL : 0; | ||
1026 | mutex_unlock(&cpuhp_state_mutex); | ||
1027 | if (ret) | ||
1028 | return ret; | ||
1029 | |||
1030 | if (st->state < target) | ||
1031 | ret = do_cpu_up(dev->id, target); | ||
1032 | else | ||
1033 | ret = do_cpu_down(dev->id, target); | ||
1034 | |||
1035 | unlock_device_hotplug(); | ||
1036 | return ret ? ret : count; | ||
1037 | } | ||
1038 | |||
982 | static ssize_t show_cpuhp_target(struct device *dev, | 1039 | static ssize_t show_cpuhp_target(struct device *dev, |
983 | struct device_attribute *attr, char *buf) | 1040 | struct device_attribute *attr, char *buf) |
984 | { | 1041 | { |
@@ -986,7 +1043,7 @@ static ssize_t show_cpuhp_target(struct device *dev, | |||
986 | 1043 | ||
987 | return sprintf(buf, "%d\n", st->target); | 1044 | return sprintf(buf, "%d\n", st->target); |
988 | } | 1045 | } |
989 | static DEVICE_ATTR(target, 0444, show_cpuhp_target, NULL); | 1046 | static DEVICE_ATTR(target, 0644, show_cpuhp_target, write_cpuhp_target); |
990 | 1047 | ||
991 | static struct attribute *cpuhp_cpu_attrs[] = { | 1048 | static struct attribute *cpuhp_cpu_attrs[] = { |
992 | &dev_attr_state.attr, | 1049 | &dev_attr_state.attr, |
@@ -1007,7 +1064,7 @@ static ssize_t show_cpuhp_states(struct device *dev, | |||
1007 | int i; | 1064 | int i; |
1008 | 1065 | ||
1009 | mutex_lock(&cpuhp_state_mutex); | 1066 | mutex_lock(&cpuhp_state_mutex); |
1010 | for (i = 0; i <= CPUHP_ONLINE; i++) { | 1067 | for (i = CPUHP_OFFLINE; i <= CPUHP_ONLINE; i++) { |
1011 | struct cpuhp_step *sp = cpuhp_get_step(i); | 1068 | struct cpuhp_step *sp = cpuhp_get_step(i); |
1012 | 1069 | ||
1013 | if (sp->name) { | 1070 | if (sp->name) { |
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 8bfd1aca7a3d..f28f7fad452f 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
@@ -1442,6 +1442,19 @@ config DEBUG_BLOCK_EXT_DEVT | |||
1442 | 1442 | ||
1443 | Say N if you are unsure. | 1443 | Say N if you are unsure. |
1444 | 1444 | ||
1445 | config CPU_HOTPLUG_STATE_CONTROL | ||
1446 | bool "Enable CPU hotplug state control" | ||
1447 | depends on DEBUG_KERNEL | ||
1448 | depends on HOTPLUG_CPU | ||
1449 | default n | ||
1450 | help | ||
1451 | Allows to write steps between "offline" and "online" to the CPUs | ||
1452 | sysfs target file so states can be stepped granular. This is a debug | ||
1453 | option for now as the hotplug machinery cannot be stopped and | ||
1454 | restarted at arbitrary points yet. | ||
1455 | |||
1456 | Say N if your are unsure. | ||
1457 | |||
1445 | config NOTIFIER_ERROR_INJECTION | 1458 | config NOTIFIER_ERROR_INJECTION |
1446 | tristate "Notifier error injection" | 1459 | tristate "Notifier error injection" |
1447 | depends on DEBUG_KERNEL | 1460 | depends on DEBUG_KERNEL |