| From 69fa6eb7d6a64801ea261025cce9723d9442d773 Mon Sep 17 00:00:00 2001 |
| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Thu, 6 Sep 2018 15:21:38 +0200 |
| Subject: cpu/hotplug: Prevent state corruption on error rollback |
| |
| From: Thomas Gleixner <tglx@linutronix.de> |
| |
| commit 69fa6eb7d6a64801ea261025cce9723d9442d773 upstream. |
| |
| When a teardown callback fails, the CPU hotplug code brings the CPU back to |
| the previous state. The previous state becomes the new target state. The |
| rollback happens in undo_cpu_down() which increments the state |
| unconditionally even if the state is already the same as the target. |
| |
| As a consequence the next CPU hotplug operation will start at the wrong |
| state. This is easily to observe when __cpu_disable() fails. |
| |
| Prevent the unconditional undo by checking the state vs. target before |
| incrementing state and fix up the consequently wrong conditional in the |
| unplug code which handles the failure of the final CPU take down on the |
| control CPU side. |
| |
| Fixes: 4dddfb5faa61 ("smp/hotplug: Rewrite AP state machine core") |
| Reported-by: Neeraj Upadhyay <neeraju@codeaurora.org> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Tested-by: Geert Uytterhoeven <geert+renesas@glider.be> |
| Tested-by: Sudeep Holla <sudeep.holla@arm.com> |
| Tested-by: Neeraj Upadhyay <neeraju@codeaurora.org> |
| Cc: josh@joshtriplett.org |
| Cc: peterz@infradead.org |
| Cc: jiangshanlai@gmail.com |
| Cc: dzickus@redhat.com |
| Cc: brendan.jackman@arm.com |
| Cc: malat@debian.org |
| Cc: sramana@codeaurora.org |
| Cc: linux-arm-msm@vger.kernel.org |
| Cc: stable@vger.kernel.org |
| Link: https://lkml.kernel.org/r/alpine.DEB.2.21.1809051419580.1416@nanos.tec.linutronix.de |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| ---- |
| |
| --- |
| kernel/cpu.c | 5 +++-- |
| 1 file changed, 3 insertions(+), 2 deletions(-) |
| |
| --- a/kernel/cpu.c |
| +++ b/kernel/cpu.c |
| @@ -932,7 +932,8 @@ static int cpuhp_down_callbacks(unsigned |
| ret = cpuhp_invoke_callback(cpu, st->state, false, NULL, NULL); |
| if (ret) { |
| st->target = prev_state; |
| - undo_cpu_down(cpu, st); |
| + if (st->state < prev_state) |
| + undo_cpu_down(cpu, st); |
| break; |
| } |
| } |
| @@ -985,7 +986,7 @@ static int __ref _cpu_down(unsigned int |
| * to do the further cleanups. |
| */ |
| ret = cpuhp_down_callbacks(cpu, st, target); |
| - if (ret && st->state > CPUHP_TEARDOWN_CPU && st->state < prev_state) { |
| + if (ret && st->state == CPUHP_TEARDOWN_CPU && st->state < prev_state) { |
| cpuhp_reset_state(st, prev_state); |
| __cpuhp_kick_ap(st); |
| } |