diff options
| author | Guy Martin <gmsoft@tuxicoman.be> | 2014-09-12 12:02:34 -0400 |
|---|---|---|
| committer | Helge Deller <deller@gmx.de> | 2014-09-13 16:40:48 -0400 |
| commit | 89206491201cbd1571009b36292af781cef74c1b (patch) | |
| tree | 62bd03def7041ca8afc67c1d732d93ec7e2d87e2 /arch/parisc/kernel/syscall.S | |
| parent | c90f06943e05519a87140dc407cf589c220aeedf (diff) | |
parisc: Implement new LWS CAS supporting 64 bit operations.
The current LWS cas only works correctly for 32bit. The new LWS allows
for CAS operations of variable size.
Signed-off-by: Guy Martin <gmsoft@tuxicoman.be>
Cc: <stable@vger.kernel.org> # 3.13+
Signed-off-by: Helge Deller <deller@gmx.de>
Diffstat (limited to 'arch/parisc/kernel/syscall.S')
| -rw-r--r-- | arch/parisc/kernel/syscall.S | 233 |
1 files changed, 229 insertions, 4 deletions
diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S index 838786011037..7ef22e3387e0 100644 --- a/arch/parisc/kernel/syscall.S +++ b/arch/parisc/kernel/syscall.S | |||
| @@ -74,7 +74,7 @@ ENTRY(linux_gateway_page) | |||
| 74 | /* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */ | 74 | /* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */ |
| 75 | /* Light-weight-syscall entry must always be located at 0xb0 */ | 75 | /* Light-weight-syscall entry must always be located at 0xb0 */ |
| 76 | /* WARNING: Keep this number updated with table size changes */ | 76 | /* WARNING: Keep this number updated with table size changes */ |
| 77 | #define __NR_lws_entries (2) | 77 | #define __NR_lws_entries (3) |
| 78 | 78 | ||
| 79 | lws_entry: | 79 | lws_entry: |
| 80 | gate lws_start, %r0 /* increase privilege */ | 80 | gate lws_start, %r0 /* increase privilege */ |
| @@ -502,7 +502,7 @@ lws_exit: | |||
| 502 | 502 | ||
| 503 | 503 | ||
| 504 | /*************************************************** | 504 | /*************************************************** |
| 505 | Implementing CAS as an atomic operation: | 505 | Implementing 32bit CAS as an atomic operation: |
| 506 | 506 | ||
| 507 | %r26 - Address to examine | 507 | %r26 - Address to examine |
| 508 | %r25 - Old value to check (old) | 508 | %r25 - Old value to check (old) |
| @@ -659,6 +659,230 @@ cas_action: | |||
| 659 | ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 3b-linux_gateway_page) | 659 | ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 3b-linux_gateway_page) |
| 660 | 660 | ||
| 661 | 661 | ||
| 662 | /*************************************************** | ||
| 663 | New CAS implementation which uses pointers and variable size | ||
| 664 | information. The value pointed by old and new MUST NOT change | ||
| 665 | while performing CAS. The lock only protect the value at %r26. | ||
| 666 | |||
| 667 | %r26 - Address to examine | ||
| 668 | %r25 - Pointer to the value to check (old) | ||
| 669 | %r24 - Pointer to the value to set (new) | ||
| 670 | %r23 - Size of the variable (0/1/2/3 for 8/16/32/64 bit) | ||
| 671 | %r28 - Return non-zero on failure | ||
| 672 | %r21 - Kernel error code | ||
| 673 | |||
| 674 | %r21 has the following meanings: | ||
| 675 | |||
| 676 | EAGAIN - CAS is busy, ldcw failed, try again. | ||
| 677 | EFAULT - Read or write failed. | ||
| 678 | |||
| 679 | Scratch: r20, r22, r28, r29, r1, fr4 (32bit for 64bit CAS only) | ||
| 680 | |||
| 681 | ****************************************************/ | ||
| 682 | |||
| 683 | /* ELF32 Process entry path */ | ||
| 684 | lws_compare_and_swap_2: | ||
| 685 | #ifdef CONFIG_64BIT | ||
| 686 | /* Clip the input registers */ | ||
| 687 | depdi 0, 31, 32, %r26 | ||
| 688 | depdi 0, 31, 32, %r25 | ||
| 689 | depdi 0, 31, 32, %r24 | ||
| 690 | depdi 0, 31, 32, %r23 | ||
| 691 | #endif | ||
| 692 | |||
| 693 | /* Check the validity of the size pointer */ | ||
| 694 | subi,>>= 4, %r23, %r0 | ||
| 695 | b,n lws_exit_nosys | ||
| 696 | |||
| 697 | /* Jump to the functions which will load the old and new values into | ||
| 698 | registers depending on the their size */ | ||
| 699 | shlw %r23, 2, %r29 | ||
| 700 | blr %r29, %r0 | ||
| 701 | nop | ||
| 702 | |||
| 703 | /* 8bit load */ | ||
| 704 | 4: ldb 0(%sr3,%r25), %r25 | ||
| 705 | b cas2_lock_start | ||
| 706 | 5: ldb 0(%sr3,%r24), %r24 | ||
| 707 | nop | ||
| 708 | nop | ||
| 709 | nop | ||
| 710 | nop | ||
| 711 | nop | ||
| 712 | |||
| 713 | /* 16bit load */ | ||
| 714 | 6: ldh 0(%sr3,%r25), %r25 | ||
| 715 | b cas2_lock_start | ||
| 716 | 7: ldh 0(%sr3,%r24), %r24 | ||
| 717 | nop | ||
| 718 | nop | ||
| 719 | nop | ||
| 720 | nop | ||
| 721 | nop | ||
| 722 | |||
| 723 | /* 32bit load */ | ||
| 724 | 8: ldw 0(%sr3,%r25), %r25 | ||
| 725 | b cas2_lock_start | ||
| 726 | 9: ldw 0(%sr3,%r24), %r24 | ||
| 727 | nop | ||
| 728 | nop | ||
| 729 | nop | ||
| 730 | nop | ||
| 731 | nop | ||
| 732 | |||
| 733 | /* 64bit load */ | ||
| 734 | #ifdef CONFIG_64BIT | ||
| 735 | 10: ldd 0(%sr3,%r25), %r25 | ||
| 736 | 11: ldd 0(%sr3,%r24), %r24 | ||
| 737 | #else | ||
| 738 | /* Load new value into r22/r23 - high/low */ | ||
| 739 | 10: ldw 0(%sr3,%r25), %r22 | ||
| 740 | 11: ldw 4(%sr3,%r25), %r23 | ||
| 741 | /* Load new value into fr4 for atomic store later */ | ||
| 742 | 12: flddx 0(%sr3,%r24), %fr4 | ||
| 743 | #endif | ||
| 744 | |||
| 745 | cas2_lock_start: | ||
| 746 | /* Load start of lock table */ | ||
| 747 | ldil L%lws_lock_start, %r20 | ||
| 748 | ldo R%lws_lock_start(%r20), %r28 | ||
| 749 | |||
| 750 | /* Extract four bits from r26 and hash lock (Bits 4-7) */ | ||
| 751 | extru %r26, 27, 4, %r20 | ||
| 752 | |||
| 753 | /* Find lock to use, the hash is either one of 0 to | ||
| 754 | 15, multiplied by 16 (keep it 16-byte aligned) | ||
| 755 | and add to the lock table offset. */ | ||
| 756 | shlw %r20, 4, %r20 | ||
| 757 | add %r20, %r28, %r20 | ||
| 758 | |||
| 759 | rsm PSW_SM_I, %r0 /* Disable interrupts */ | ||
| 760 | /* COW breaks can cause contention on UP systems */ | ||
| 761 | LDCW 0(%sr2,%r20), %r28 /* Try to acquire the lock */ | ||
| 762 | cmpb,<>,n %r0, %r28, cas2_action /* Did we get it? */ | ||
| 763 | cas2_wouldblock: | ||
| 764 | ldo 2(%r0), %r28 /* 2nd case */ | ||
| 765 | ssm PSW_SM_I, %r0 | ||
| 766 | b lws_exit /* Contended... */ | ||
| 767 | ldo -EAGAIN(%r0), %r21 /* Spin in userspace */ | ||
| 768 | |||
| 769 | /* | ||
| 770 | prev = *addr; | ||
| 771 | if ( prev == old ) | ||
| 772 | *addr = new; | ||
| 773 | return prev; | ||
| 774 | */ | ||
| 775 | |||
| 776 | /* NOTES: | ||
| 777 | This all works becuse intr_do_signal | ||
| 778 | and schedule both check the return iasq | ||
| 779 | and see that we are on the kernel page | ||
| 780 | so this process is never scheduled off | ||
| 781 | or is ever sent any signal of any sort, | ||
| 782 | thus it is wholly atomic from usrspaces | ||
| 783 | perspective | ||
| 784 | */ | ||
| 785 | cas2_action: | ||
| 786 | /* Jump to the correct function */ | ||
| 787 | blr %r29, %r0 | ||
| 788 | /* Set %r28 as non-zero for now */ | ||
| 789 | ldo 1(%r0),%r28 | ||
| 790 | |||
| 791 | /* 8bit CAS */ | ||
| 792 | 13: ldb,ma 0(%sr3,%r26), %r29 | ||
| 793 | sub,= %r29, %r25, %r0 | ||
| 794 | b,n cas2_end | ||
| 795 | 14: stb,ma %r24, 0(%sr3,%r26) | ||
| 796 | b cas2_end | ||
| 797 | copy %r0, %r28 | ||
| 798 | nop | ||
| 799 | nop | ||
| 800 | |||
| 801 | /* 16bit CAS */ | ||
| 802 | 15: ldh,ma 0(%sr3,%r26), %r29 | ||
| 803 | sub,= %r29, %r25, %r0 | ||
| 804 | b,n cas2_end | ||
| 805 | 16: sth,ma %r24, 0(%sr3,%r26) | ||
| 806 | b cas2_end | ||
| 807 | copy %r0, %r28 | ||
| 808 | nop | ||
| 809 | nop | ||
| 810 | |||
| 811 | /* 32bit CAS */ | ||
| 812 | 17: ldw,ma 0(%sr3,%r26), %r29 | ||
| 813 | sub,= %r29, %r25, %r0 | ||
| 814 | b,n cas2_end | ||
| 815 | 18: stw,ma %r24, 0(%sr3,%r26) | ||
| 816 | b cas2_end | ||
| 817 | copy %r0, %r28 | ||
| 818 | nop | ||
| 819 | nop | ||
| 820 | |||
| 821 | /* 64bit CAS */ | ||
| 822 | #ifdef CONFIG_64BIT | ||
| 823 | 19: ldd,ma 0(%sr3,%r26), %r29 | ||
| 824 | sub,= %r29, %r25, %r0 | ||
| 825 | b,n cas2_end | ||
| 826 | 20: std,ma %r24, 0(%sr3,%r26) | ||
| 827 | copy %r0, %r28 | ||
| 828 | #else | ||
| 829 | /* Compare first word */ | ||
| 830 | 19: ldw,ma 0(%sr3,%r26), %r29 | ||
| 831 | sub,= %r29, %r22, %r0 | ||
| 832 | b,n cas2_end | ||
| 833 | /* Compare second word */ | ||
| 834 | 20: ldw,ma 4(%sr3,%r26), %r29 | ||
| 835 | sub,= %r29, %r23, %r0 | ||
| 836 | b,n cas2_end | ||
| 837 | /* Perform the store */ | ||
| 838 | 21: fstdx %fr4, 0(%sr3,%r26) | ||
| 839 | copy %r0, %r28 | ||
| 840 | #endif | ||
| 841 | |||
| 842 | cas2_end: | ||
| 843 | /* Free lock */ | ||
| 844 | stw,ma %r20, 0(%sr2,%r20) | ||
| 845 | /* Enable interrupts */ | ||
| 846 | ssm PSW_SM_I, %r0 | ||
| 847 | /* Return to userspace, set no error */ | ||
| 848 | b lws_exit | ||
| 849 | copy %r0, %r21 | ||
| 850 | |||
| 851 | 22: | ||
| 852 | /* Error occurred on load or store */ | ||
| 853 | /* Free lock */ | ||
| 854 | stw %r20, 0(%sr2,%r20) | ||
| 855 | ssm PSW_SM_I, %r0 | ||
| 856 | ldo 1(%r0),%r28 | ||
| 857 | b lws_exit | ||
| 858 | ldo -EFAULT(%r0),%r21 /* set errno */ | ||
| 859 | nop | ||
| 860 | nop | ||
| 861 | nop | ||
| 862 | |||
| 863 | /* Exception table entries, for the load and store, return EFAULT. | ||
| 864 | Each of the entries must be relocated. */ | ||
| 865 | ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 866 | ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 867 | ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 868 | ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 869 | ASM_EXCEPTIONTABLE_ENTRY(8b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 870 | ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 871 | ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 872 | ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 873 | ASM_EXCEPTIONTABLE_ENTRY(13b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 874 | ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 875 | ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 876 | ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 877 | ASM_EXCEPTIONTABLE_ENTRY(17b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 878 | ASM_EXCEPTIONTABLE_ENTRY(18b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 879 | ASM_EXCEPTIONTABLE_ENTRY(19b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 880 | ASM_EXCEPTIONTABLE_ENTRY(20b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 881 | #ifndef CONFIG_64BIT | ||
| 882 | ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 883 | ASM_EXCEPTIONTABLE_ENTRY(21b-linux_gateway_page, 22b-linux_gateway_page) | ||
| 884 | #endif | ||
| 885 | |||
| 662 | /* Make sure nothing else is placed on this page */ | 886 | /* Make sure nothing else is placed on this page */ |
| 663 | .align PAGE_SIZE | 887 | .align PAGE_SIZE |
| 664 | END(linux_gateway_page) | 888 | END(linux_gateway_page) |
| @@ -675,8 +899,9 @@ ENTRY(end_linux_gateway_page) | |||
| 675 | /* Light-weight-syscall table */ | 899 | /* Light-weight-syscall table */ |
| 676 | /* Start of lws table. */ | 900 | /* Start of lws table. */ |
| 677 | ENTRY(lws_table) | 901 | ENTRY(lws_table) |
| 678 | LWS_ENTRY(compare_and_swap32) /* 0 - ELF32 Atomic compare and swap */ | 902 | LWS_ENTRY(compare_and_swap32) /* 0 - ELF32 Atomic 32bit CAS */ |
| 679 | LWS_ENTRY(compare_and_swap64) /* 1 - ELF64 Atomic compare and swap */ | 903 | LWS_ENTRY(compare_and_swap64) /* 1 - ELF64 Atomic 32bit CAS */ |
| 904 | LWS_ENTRY(compare_and_swap_2) /* 2 - ELF32 Atomic 64bit CAS */ | ||
| 680 | END(lws_table) | 905 | END(lws_table) |
| 681 | /* End of lws table */ | 906 | /* End of lws table */ |
| 682 | 907 | ||
