[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