| From: Nathan Lynch <nathanl@linux.ibm.com> |
| Date: Fri, 2 Aug 2019 14:29:24 -0500 |
| Subject: powerpc/rtas: use device model APIs and serialization during LPM |
| |
| commit a6717c01ddc259f6f73364779df058e2c67309f8 upstream. |
| |
| The LPAR migration implementation and userspace-initiated cpu hotplug |
| can interleave their executions like so: |
| |
| 1. Set cpu 7 offline via sysfs. |
| |
| 2. Begin a partition migration, whose implementation requires the OS |
| to ensure all present cpus are online; cpu 7 is onlined: |
| |
| rtas_ibm_suspend_me -> rtas_online_cpus_mask -> cpu_up |
| |
| This sets cpu 7 online in all respects except for the cpu's |
| corresponding struct device; dev->offline remains true. |
| |
| 3. Set cpu 7 online via sysfs. _cpu_up() determines that cpu 7 is |
| already online and returns success. The driver core (device_online) |
| sets dev->offline = false. |
| |
| 4. The migration completes and restores cpu 7 to offline state: |
| |
| rtas_ibm_suspend_me -> rtas_offline_cpus_mask -> cpu_down |
| |
| This leaves cpu7 in a state where the driver core considers the cpu |
| device online, but in all other respects it is offline and |
| unused. Attempts to online the cpu via sysfs appear to succeed but the |
| driver core actually does not pass the request to the lower-level |
| cpuhp support code. This makes the cpu unusable until the cpu device |
| is manually set offline and then online again via sysfs. |
| |
| Instead of directly calling cpu_up/cpu_down, the migration code should |
| use the higher-level device core APIs to maintain consistent state and |
| serialize operations. |
| |
| Fixes: 120496ac2d2d ("powerpc: Bring all threads online prior to migration/hibernation") |
| Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com> |
| Reviewed-by: Gautham R. Shenoy <ego@linux.vnet.ibm.com> |
| Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> |
| Link: https://lore.kernel.org/r/20190802192926.19277-2-nathanl@linux.ibm.com |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| arch/powerpc/kernel/rtas.c | 11 ++++++++--- |
| 1 file changed, 8 insertions(+), 3 deletions(-) |
| |
| --- a/arch/powerpc/kernel/rtas.c |
| +++ b/arch/powerpc/kernel/rtas.c |
| @@ -855,15 +855,17 @@ static int rtas_cpu_state_change_mask(en |
| return 0; |
| |
| for_each_cpu(cpu, cpus) { |
| + struct device *dev = get_cpu_device(cpu); |
| + |
| switch (state) { |
| case DOWN: |
| - cpuret = cpu_down(cpu); |
| + cpuret = device_offline(dev); |
| break; |
| case UP: |
| - cpuret = cpu_up(cpu); |
| + cpuret = device_online(dev); |
| break; |
| } |
| - if (cpuret) { |
| + if (cpuret < 0) { |
| pr_debug("%s: cpu_%s for cpu#%d returned %d.\n", |
| __func__, |
| ((state == UP) ? "up" : "down"), |
| @@ -955,6 +957,8 @@ int rtas_ibm_suspend_me(struct rtas_args |
| data.token = rtas_token("ibm,suspend-me"); |
| data.complete = &done; |
| |
| + lock_device_hotplug(); |
| + |
| /* All present CPUs must be online */ |
| cpumask_andnot(offline_mask, cpu_present_mask, cpu_online_mask); |
| cpuret = rtas_online_cpus_mask(offline_mask); |
| @@ -986,6 +990,7 @@ int rtas_ibm_suspend_me(struct rtas_args |
| __func__); |
| |
| out: |
| + unlock_device_hotplug(); |
| free_cpumask_var(offline_mask); |
| return atomic_read(&data.error); |
| } |