[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 .lock = SPIN_LOCK_UNLOCKED 37 }; 38 39 EXPORT_SYMBOL(rtas); 40 41 DEFINE_SPINLOCK(rtas_data_buf_lock); ··· 561 } while (status == RTAS_BUSY); 562 } 563 564 565 asmlinkage int ppc_rtas(struct rtas_args __user *uargs) 566 { ··· 642 unsigned long flags; 643 char *buff_copy, *errbuf = NULL; 644 int nargs; 645 646 if (!capable(CAP_SYS_ADMIN)) 647 return -EPERM; ··· 660 if (copy_from_user(args.args, uargs->args, 661 nargs * sizeof(rtas_arg_t)) != 0) 662 return -EFAULT; 663 664 buff_copy = get_errorlog_buffer(); 665 ··· 695 kfree(buff_copy); 696 } 697 698 /* Copy out args. */ 699 if (copy_to_user(uargs->args + nargs, 700 args.args + nargs, ··· 767 * the stop-self token if any 768 */ 769 #ifdef CONFIG_PPC64 770 - if (_machine == PLATFORM_PSERIES_LPAR) 771 rtas_region = min(lmb.rmo_size, RTAS_INSTANTIATE_MAX); 772 #endif 773 rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region); 774
··· 36 .lock = SPIN_LOCK_UNLOCKED 37 }; 38 39 + struct rtas_suspend_me_data { 40 + long waiting; 41 + struct rtas_args *args; 42 + }; 43 + 44 EXPORT_SYMBOL(rtas); 45 46 DEFINE_SPINLOCK(rtas_data_buf_lock); ··· 556 } while (status == RTAS_BUSY); 557 } 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 633 634 asmlinkage int ppc_rtas(struct rtas_args __user *uargs) 635 { ··· 563 unsigned long flags; 564 char *buff_copy, *errbuf = NULL; 565 int nargs; 566 + int rc; 567 568 if (!capable(CAP_SYS_ADMIN)) 569 return -EPERM; ··· 580 if (copy_from_user(args.args, uargs->args, 581 nargs * sizeof(rtas_arg_t)) != 0) 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 + } 594 595 buff_copy = get_errorlog_buffer(); 596 ··· 604 kfree(buff_copy); 605 } 606 607 + copy_return: 608 /* Copy out args. */ 609 if (copy_to_user(uargs->args + nargs, 610 args.args + nargs, ··· 675 * the stop-self token if any 676 */ 677 #ifdef CONFIG_PPC64 678 + if (_machine == PLATFORM_PSERIES_LPAR) { 679 rtas_region = min(lmb.rmo_size, RTAS_INSTANTIATE_MAX); 680 + ibm_suspend_me_token = rtas_token("ibm,suspend-me"); 681 + } 682 #endif 683 rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region); 684