[PATCH] powerpc: special case ibm,suspend-me rtas call
Dave C Boutcher
sleddog at us.ibm.com
Sat Jan 14 09:03:31 EST 2006
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 syscall.
This is an alternative to a previous patch I submitted which issues the
rtas call through a new (and thus evil) /proc file. I don't especially
like adding special-case code to the rtas call path, but I don't like
adding a /proc file either...
Signed-off-by: Dave Boutcher <sleddog at us.ibm.com>
diff -uNr -X exclude-files powerpc.git.patched2/arch/powerpc/kernel/rtas.c powerpc.git.vpm/arch/powerpc/kernel/rtas.c
--- powerpc.git.patched2/arch/powerpc/kernel/rtas.c 2006-01-12 15:12:17.000000000 -0600
+++ powerpc.git.vpm/arch/powerpc/kernel/rtas.c 2006-01-13 15:53:37.000000000 -0600
@@ -34,6 +34,11 @@
.lock = SPIN_LOCK_UNLOCKED
};
+struct rtas_suspend_me_data {
+ long waiting;
+ struct rtas_args *args;
+};
+
EXPORT_SYMBOL(rtas);
DEFINE_SPINLOCK(rtas_data_buf_lock);
@@ -549,6 +554,80 @@
} 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:
+#ifdef CONFIG_DETECT_SOFTLOCKUP
+ /* before we restore interrupts, make sure we don't
+ * generate a spurious soft lockup errors
+ */
+ touch_softlockup_watchdog();
+#endif
+ 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 other CPUs */
+ 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 -EINVAL;
+}
+#endif
asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
{
@@ -556,6 +635,7 @@
unsigned long flags;
char *buff_copy, *errbuf = NULL;
int nargs;
+ int rc;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -574,6 +654,18 @@
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;
+ else
+ goto copy_return;
+ }
+
buff_copy = get_errorlog_buffer();
spin_lock_irqsave(&rtas.lock, flags);
@@ -597,6 +689,7 @@
kfree(buff_copy);
}
+copy_return:
/* Copy out args. */
if (copy_to_user(uargs->args + nargs,
args.args + nargs,
@@ -668,8 +761,10 @@
* 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);
--
Dave Boutcher
More information about the Linuxppc64-dev
mailing list