aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/rtas.c
diff options
context:
space:
mode:
authorDave C Boutcher <sleddog@us.ibm.com>2006-01-13 19:39:24 -0500
committerPaul Mackerras <paulus@samba.org>2006-01-13 20:04:25 -0500
commit91dc182ca6e25ee8b648ed2fb2a41859ead34903 (patch)
treed668133a2a3abd8345a5a2509c2da185dd180af8 /arch/powerpc/kernel/rtas.c
parent91f62a2491f45b5d46ebe5aca1fac3477355c9c1 (diff)
[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>
Diffstat (limited to 'arch/powerpc/kernel/rtas.c')
-rw-r--r--arch/powerpc/kernel/rtas.c96
1 files changed, 95 insertions, 1 deletions
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 4b9cfe4637b1..7fe4a5c944c9 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -36,6 +36,11 @@ struct rtas_t rtas = {
36 .lock = SPIN_LOCK_UNLOCKED 36 .lock = SPIN_LOCK_UNLOCKED
37}; 37};
38 38
39struct rtas_suspend_me_data {
40 long waiting;
41 struct rtas_args *args;
42};
43
39EXPORT_SYMBOL(rtas); 44EXPORT_SYMBOL(rtas);
40 45
41DEFINE_SPINLOCK(rtas_data_buf_lock); 46DEFINE_SPINLOCK(rtas_data_buf_lock);
@@ -556,6 +561,80 @@ void rtas_os_term(char *str)
556 } while (status == RTAS_BUSY); 561 } while (status == RTAS_BUSY);
557} 562}
558 563
564static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE;
565#ifdef CONFIG_PPC_PSERIES
566static void rtas_percpu_suspend_me(void *info)
567{
568 long rc;
569 long flags;
570 struct rtas_suspend_me_data *data =
571 (struct rtas_suspend_me_data *)info;
572
573 /*
574 * We use "waiting" to indicate our state. As long
575 * as it is >0, we are still trying to all join up.
576 * If it goes to 0, we have successfully joined up and
577 * one thread got H_Continue. If any error happens,
578 * we set it to <0.
579 */
580 local_irq_save(flags);
581 do {
582 rc = plpar_hcall_norets(H_JOIN);
583 smp_rmb();
584 } while (rc == H_Success && data->waiting > 0);
585 if (rc == H_Success)
586 goto out;
587
588 if (rc == H_Continue) {
589 data->waiting = 0;
590 rtas_call(ibm_suspend_me_token, 0, 1,
591 data->args->args);
592 } else {
593 data->waiting = -EBUSY;
594 printk(KERN_ERR "Error on H_Join hypervisor call\n");
595 }
596
597out:
598 /* before we restore interrupts, make sure we don't
599 * generate a spurious soft lockup errors
600 */
601 touch_softlockup_watchdog();
602 local_irq_restore(flags);
603 return;
604}
605
606static int rtas_ibm_suspend_me(struct rtas_args *args)
607{
608 int i;
609
610 struct rtas_suspend_me_data data;
611
612 data.waiting = 1;
613 data.args = args;
614
615 /* Call function on all CPUs. One of us will make the
616 * rtas call
617 */
618 if (on_each_cpu(rtas_percpu_suspend_me, &data, 1, 0))
619 data.waiting = -EINVAL;
620
621 if (data.waiting != 0)
622 printk(KERN_ERR "Error doing global join\n");
623
624 /* Prod each CPU. This won't hurt, and will wake
625 * anyone we successfully put to sleep with H_Join
626 */
627 for_each_cpu(i)
628 plpar_hcall_norets(H_PROD, i);
629
630 return data.waiting;
631}
632#else /* CONFIG_PPC_PSERIES */
633static int rtas_ibm_suspend_me(struct rtas_args *args)
634{
635 return -ENOSYS;
636}
637#endif
559 638
560asmlinkage int ppc_rtas(struct rtas_args __user *uargs) 639asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
561{ 640{
@@ -563,6 +642,7 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
563 unsigned long flags; 642 unsigned long flags;
564 char *buff_copy, *errbuf = NULL; 643 char *buff_copy, *errbuf = NULL;
565 int nargs; 644 int nargs;
645 int rc;
566 646
567 if (!capable(CAP_SYS_ADMIN)) 647 if (!capable(CAP_SYS_ADMIN))
568 return -EPERM; 648 return -EPERM;
@@ -581,6 +661,17 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
581 nargs * sizeof(rtas_arg_t)) != 0) 661 nargs * sizeof(rtas_arg_t)) != 0)
582 return -EFAULT; 662 return -EFAULT;
583 663
664 if (args.token == RTAS_UNKNOWN_SERVICE)
665 return -EINVAL;
666
667 /* Need to handle ibm,suspend_me call specially */
668 if (args.token == ibm_suspend_me_token) {
669 rc = rtas_ibm_suspend_me(&args);
670 if (rc)
671 return rc;
672 goto copy_return;
673 }
674
584 buff_copy = get_errorlog_buffer(); 675 buff_copy = get_errorlog_buffer();
585 676
586 spin_lock_irqsave(&rtas.lock, flags); 677 spin_lock_irqsave(&rtas.lock, flags);
@@ -604,6 +695,7 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
604 kfree(buff_copy); 695 kfree(buff_copy);
605 } 696 }
606 697
698 copy_return:
607 /* Copy out args. */ 699 /* Copy out args. */
608 if (copy_to_user(uargs->args + nargs, 700 if (copy_to_user(uargs->args + nargs,
609 args.args + nargs, 701 args.args + nargs,
@@ -675,8 +767,10 @@ void __init rtas_initialize(void)
675 * the stop-self token if any 767 * the stop-self token if any
676 */ 768 */
677#ifdef CONFIG_PPC64 769#ifdef CONFIG_PPC64
678 if (_machine == PLATFORM_PSERIES_LPAR) 770 if (_machine == PLATFORM_PSERIES_LPAR) {
679 rtas_region = min(lmb.rmo_size, RTAS_INSTANTIATE_MAX); 771 rtas_region = min(lmb.rmo_size, RTAS_INSTANTIATE_MAX);
772 ibm_suspend_me_token = rtas_token("ibm,suspend-me");
773 }
680#endif 774#endif
681 rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region); 775 rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region);
682 776