[PATCH] powerpc: special-case ibm,suspend-me RTAS call

Handle the ibm,suspend-me RTAS call specially.  It needs
to be wrapped in a set of synchronization hypervisor calls
(H_Join).  When the H_Join calls are made on all CPUs, the
intent is that only one will return with H_Continue, meaning
that he is the "last man standing".  That CPU then issues the
ibm,suspend-me call.  What is interesting, of course, is that
the CPU running when the rtas syscall is made, may NOT be the
CPU that ultimately executes the ibm,suspend-me rtas call.

Signed-off-by: Dave Boutcher <sleddog@us.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
Dave C Boutcher 2006-01-13 18:39:24 -06:00 committed by Paul Mackerras
parent 91f62a2491
commit 91dc182ca6

View file

@ -36,6 +36,11 @@ struct rtas_t rtas = {
.lock = SPIN_LOCK_UNLOCKED
};
struct rtas_suspend_me_data {
long waiting;
struct rtas_args *args;
};
EXPORT_SYMBOL(rtas);
DEFINE_SPINLOCK(rtas_data_buf_lock);
@ -556,6 +561,80 @@ void rtas_os_term(char *str)
} while (status == RTAS_BUSY);
}
static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE;
#ifdef CONFIG_PPC_PSERIES
static void rtas_percpu_suspend_me(void *info)
{
long rc;
long flags;
struct rtas_suspend_me_data *data =
(struct rtas_suspend_me_data *)info;
/*
* We use "waiting" to indicate our state. As long
* as it is >0, we are still trying to all join up.
* If it goes to 0, we have successfully joined up and
* one thread got H_Continue. If any error happens,
* we set it to <0.
*/
local_irq_save(flags);
do {
rc = plpar_hcall_norets(H_JOIN);
smp_rmb();
} while (rc == H_Success && data->waiting > 0);
if (rc == H_Success)
goto out;
if (rc == H_Continue) {
data->waiting = 0;
rtas_call(ibm_suspend_me_token, 0, 1,
data->args->args);
} else {
data->waiting = -EBUSY;
printk(KERN_ERR "Error on H_Join hypervisor call\n");
}
out:
/* before we restore interrupts, make sure we don't
* generate a spurious soft lockup errors
*/
touch_softlockup_watchdog();
local_irq_restore(flags);
return;
}
static int rtas_ibm_suspend_me(struct rtas_args *args)
{
int i;
struct rtas_suspend_me_data data;
data.waiting = 1;
data.args = args;
/* Call function on all CPUs. One of us will make the
* rtas call
*/
if (on_each_cpu(rtas_percpu_suspend_me, &data, 1, 0))
data.waiting = -EINVAL;
if (data.waiting != 0)
printk(KERN_ERR "Error doing global join\n");
/* Prod each CPU. This won't hurt, and will wake
* anyone we successfully put to sleep with H_Join
*/
for_each_cpu(i)
plpar_hcall_norets(H_PROD, i);
return data.waiting;
}
#else /* CONFIG_PPC_PSERIES */
static int rtas_ibm_suspend_me(struct rtas_args *args)
{
return -ENOSYS;
}
#endif
asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
{
@ -563,6 +642,7 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
unsigned long flags;
char *buff_copy, *errbuf = NULL;
int nargs;
int rc;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@ -581,6 +661,17 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
nargs * sizeof(rtas_arg_t)) != 0)
return -EFAULT;
if (args.token == RTAS_UNKNOWN_SERVICE)
return -EINVAL;
/* Need to handle ibm,suspend_me call specially */
if (args.token == ibm_suspend_me_token) {
rc = rtas_ibm_suspend_me(&args);
if (rc)
return rc;
goto copy_return;
}
buff_copy = get_errorlog_buffer();
spin_lock_irqsave(&rtas.lock, flags);
@ -604,6 +695,7 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
kfree(buff_copy);
}
copy_return:
/* Copy out args. */
if (copy_to_user(uargs->args + nargs,
args.args + nargs,
@ -675,8 +767,10 @@ void __init rtas_initialize(void)
* the stop-self token if any
*/
#ifdef CONFIG_PPC64
if (_machine == PLATFORM_PSERIES_LPAR)
if (_machine == PLATFORM_PSERIES_LPAR) {
rtas_region = min(lmb.rmo_size, RTAS_INSTANTIATE_MAX);
ibm_suspend_me_token = rtas_token("ibm,suspend-me");
}
#endif
rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region);