[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:
parent
91f62a2491
commit
91dc182ca6
@ -36,6 +36,11 @@ struct rtas_t rtas = {
|
|||||||
.lock = SPIN_LOCK_UNLOCKED
|
.lock = SPIN_LOCK_UNLOCKED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct rtas_suspend_me_data {
|
||||||
|
long waiting;
|
||||||
|
struct rtas_args *args;
|
||||||
|
};
|
||||||
|
|
||||||
EXPORT_SYMBOL(rtas);
|
EXPORT_SYMBOL(rtas);
|
||||||
|
|
||||||
DEFINE_SPINLOCK(rtas_data_buf_lock);
|
DEFINE_SPINLOCK(rtas_data_buf_lock);
|
||||||
@ -556,6 +561,80 @@ void rtas_os_term(char *str)
|
|||||||
} while (status == RTAS_BUSY);
|
} 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)
|
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;
|
unsigned long flags;
|
||||||
char *buff_copy, *errbuf = NULL;
|
char *buff_copy, *errbuf = NULL;
|
||||||
int nargs;
|
int nargs;
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (!capable(CAP_SYS_ADMIN))
|
if (!capable(CAP_SYS_ADMIN))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
@ -581,6 +661,17 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
|
|||||||
nargs * sizeof(rtas_arg_t)) != 0)
|
nargs * sizeof(rtas_arg_t)) != 0)
|
||||||
return -EFAULT;
|
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();
|
buff_copy = get_errorlog_buffer();
|
||||||
|
|
||||||
spin_lock_irqsave(&rtas.lock, flags);
|
spin_lock_irqsave(&rtas.lock, flags);
|
||||||
@ -604,6 +695,7 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
|
|||||||
kfree(buff_copy);
|
kfree(buff_copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copy_return:
|
||||||
/* Copy out args. */
|
/* Copy out args. */
|
||||||
if (copy_to_user(uargs->args + nargs,
|
if (copy_to_user(uargs->args + nargs,
|
||||||
args.args + nargs,
|
args.args + nargs,
|
||||||
@ -675,8 +767,10 @@ void __init rtas_initialize(void)
|
|||||||
* the stop-self token if any
|
* the stop-self token if any
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_PPC64
|
#ifdef CONFIG_PPC64
|
||||||
if (_machine == PLATFORM_PSERIES_LPAR)
|
if (_machine == PLATFORM_PSERIES_LPAR) {
|
||||||
rtas_region = min(lmb.rmo_size, RTAS_INSTANTIATE_MAX);
|
rtas_region = min(lmb.rmo_size, RTAS_INSTANTIATE_MAX);
|
||||||
|
ibm_suspend_me_token = rtas_token("ibm,suspend-me");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region);
|
rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user