[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>

authored by Dave C Boutcher and committed by Paul Mackerras 91dc182c 91f62a24

+95 -1
+95 -1
arch/powerpc/kernel/rtas.c
··· 36 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 44 EXPORT_SYMBOL(rtas); 40 45 41 46 DEFINE_SPINLOCK(rtas_data_buf_lock); ··· 561 556 } while (status == RTAS_BUSY); 562 557 } 563 558 559 + static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE; 560 + #ifdef CONFIG_PPC_PSERIES 561 + static void rtas_percpu_suspend_me(void *info) 562 + { 563 + long rc; 564 + long flags; 565 + struct rtas_suspend_me_data *data = 566 + (struct rtas_suspend_me_data *)info; 567 + 568 + /* 569 + * We use "waiting" to indicate our state. As long 570 + * as it is >0, we are still trying to all join up. 571 + * If it goes to 0, we have successfully joined up and 572 + * one thread got H_Continue. If any error happens, 573 + * we set it to <0. 574 + */ 575 + local_irq_save(flags); 576 + do { 577 + rc = plpar_hcall_norets(H_JOIN); 578 + smp_rmb(); 579 + } while (rc == H_Success && data->waiting > 0); 580 + if (rc == H_Success) 581 + goto out; 582 + 583 + if (rc == H_Continue) { 584 + data->waiting = 0; 585 + rtas_call(ibm_suspend_me_token, 0, 1, 586 + data->args->args); 587 + } else { 588 + data->waiting = -EBUSY; 589 + printk(KERN_ERR "Error on H_Join hypervisor call\n"); 590 + } 591 + 592 + out: 593 + /* before we restore interrupts, make sure we don't 594 + * generate a spurious soft lockup errors 595 + */ 596 + touch_softlockup_watchdog(); 597 + local_irq_restore(flags); 598 + return; 599 + } 600 + 601 + static int rtas_ibm_suspend_me(struct rtas_args *args) 602 + { 603 + int i; 604 + 605 + struct rtas_suspend_me_data data; 606 + 607 + data.waiting = 1; 608 + data.args = args; 609 + 610 + /* Call function on all CPUs. One of us will make the 611 + * rtas call 612 + */ 613 + if (on_each_cpu(rtas_percpu_suspend_me, &data, 1, 0)) 614 + data.waiting = -EINVAL; 615 + 616 + if (data.waiting != 0) 617 + printk(KERN_ERR "Error doing global join\n"); 618 + 619 + /* Prod each CPU. This won't hurt, and will wake 620 + * anyone we successfully put to sleep with H_Join 621 + */ 622 + for_each_cpu(i) 623 + plpar_hcall_norets(H_PROD, i); 624 + 625 + return data.waiting; 626 + } 627 + #else /* CONFIG_PPC_PSERIES */ 628 + static int rtas_ibm_suspend_me(struct rtas_args *args) 629 + { 630 + return -ENOSYS; 631 + } 632 + #endif 564 633 565 634 asmlinkage int ppc_rtas(struct rtas_args __user *uargs) 566 635 { ··· 642 563 unsigned long flags; 643 564 char *buff_copy, *errbuf = NULL; 644 565 int nargs; 566 + int rc; 645 567 646 568 if (!capable(CAP_SYS_ADMIN)) 647 569 return -EPERM; ··· 660 580 if (copy_from_user(args.args, uargs->args, 661 581 nargs * sizeof(rtas_arg_t)) != 0) 662 582 return -EFAULT; 583 + 584 + if (args.token == RTAS_UNKNOWN_SERVICE) 585 + return -EINVAL; 586 + 587 + /* Need to handle ibm,suspend_me call specially */ 588 + if (args.token == ibm_suspend_me_token) { 589 + rc = rtas_ibm_suspend_me(&args); 590 + if (rc) 591 + return rc; 592 + goto copy_return; 593 + } 663 594 664 595 buff_copy = get_errorlog_buffer(); 665 596 ··· 695 604 kfree(buff_copy); 696 605 } 697 606 607 + copy_return: 698 608 /* Copy out args. */ 699 609 if (copy_to_user(uargs->args + nargs, 700 610 args.args + nargs, ··· 767 675 * the stop-self token if any 768 676 */ 769 677 #ifdef CONFIG_PPC64 770 - if (_machine == PLATFORM_PSERIES_LPAR) 678 + if (_machine == PLATFORM_PSERIES_LPAR) { 771 679 rtas_region = min(lmb.rmo_size, RTAS_INSTANTIATE_MAX); 680 + ibm_suspend_me_token = rtas_token("ibm,suspend-me"); 681 + } 772 682 #endif 773 683 rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region); 774 684