sys_rtas() cannot call ibm,suspend-me directly in the same way it handles other inputs. Instead it must dispatch the request to code that can first perform the H_JOIN sequence before any call to ibm,suspend-me can succeed. Over time kernel/rtas.c has accreted a fair amount of platform-specific code to implement this.
Since a different, more robust implementation of the suspend sequence is now in the pseries platform code, we want to dispatch the request there while minimizing additional dependence on pseries. Use a weak function that only pseries overrides. Note that invoking ibm,suspend-me via the RTAS syscall is all but deprecated; this change preserves ABI compatibility for old programs while providing to them the benefit of the new partition suspend implementation. This is a behavior change in that the kernel performs the device tree update and firmware activation before returning, but experimentation indicates this is tolerated fine by legacy user space. Signed-off-by: Nathan Lynch <nath...@linux.ibm.com> --- arch/powerpc/include/asm/rtas.h | 1 + arch/powerpc/kernel/rtas.c | 8 +++++++- arch/powerpc/platforms/pseries/mobility.c | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index fdefe6a974eb..be0fc2536673 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h @@ -260,6 +260,7 @@ extern int rtas_suspend_cpu(struct rtas_suspend_me_data *data); extern int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data); int rtas_ibm_suspend_me_unsafe(u64 handle); int rtas_ibm_suspend_me(int *fw_status); +int rtas_syscall_dispatch_ibm_suspend_me(u64 handle); struct rtc_time; extern time64_t rtas_get_boot_time(void); diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 58bbd69a233f..52fb394f15d6 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -1221,6 +1221,12 @@ static bool block_rtas_call(int token, int nargs, #endif /* CONFIG_PPC_RTAS_FILTER */ +/* Only pseries should need to override this. */ +int __weak rtas_syscall_dispatch_ibm_suspend_me(u64 handle) +{ + return -EINVAL; +} + /* We assume to be passed big endian arguments */ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs) { @@ -1271,7 +1277,7 @@ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs) int rc = 0; u64 handle = ((u64)be32_to_cpu(args.args[0]) << 32) | be32_to_cpu(args.args[1]); - rc = rtas_ibm_suspend_me_unsafe(handle); + rc = rtas_syscall_dispatch_ibm_suspend_me(handle); if (rc == -EAGAIN) args.rets[0] = cpu_to_be32(RTAS_NOT_SUSPENDABLE); else if (rc == -EIO) diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index e459cdb8286f..d6417c9db201 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -622,6 +622,11 @@ static int pseries_migrate_partition(u64 handle) return ret; } +int rtas_syscall_dispatch_ibm_suspend_me(u64 handle) +{ + return pseries_migrate_partition(handle); +} + static ssize_t migration_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) -- 2.25.4