diff options
author | Dave C Boutcher <sleddog@us.ibm.com> | 2006-01-13 19:39:24 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-01-13 20:04:25 -0500 |
commit | 91dc182ca6e25ee8b648ed2fb2a41859ead34903 (patch) | |
tree | d668133a2a3abd8345a5a2509c2da185dd180af8 /arch/powerpc/kernel/rtas.c | |
parent | 91f62a2491f45b5d46ebe5aca1fac3477355c9c1 (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.c | 96 |
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 | ||
39 | struct rtas_suspend_me_data { | ||
40 | long waiting; | ||
41 | struct rtas_args *args; | ||
42 | }; | ||
43 | |||
39 | EXPORT_SYMBOL(rtas); | 44 | EXPORT_SYMBOL(rtas); |
40 | 45 | ||
41 | DEFINE_SPINLOCK(rtas_data_buf_lock); | 46 | DEFINE_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 | ||
564 | static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE; | ||
565 | #ifdef CONFIG_PPC_PSERIES | ||
566 | static 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 | |||
597 | out: | ||
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 | |||
606 | static 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 */ | ||
633 | static int rtas_ibm_suspend_me(struct rtas_args *args) | ||
634 | { | ||
635 | return -ENOSYS; | ||
636 | } | ||
637 | #endif | ||
559 | 638 | ||
560 | asmlinkage int ppc_rtas(struct rtas_args __user *uargs) | 639 | asmlinkage 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 | ||