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>

authored by Guy Martin and committed by Helge Deller 89206491 c90f0694

Changed files
+229 -4
arch
parisc
kernel
+229 -4
arch/parisc/kernel/syscall.S
··· 74 74 /* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */ 75 75 /* Light-weight-syscall entry must always be located at 0xb0 */ 76 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 79 lws_entry: 80 80 gate lws_start, %r0 /* increase privilege */ ··· 502 502 503 503 504 504 /*************************************************** 505 - Implementing CAS as an atomic operation: 505 + Implementing 32bit CAS as an atomic operation: 506 506 507 507 %r26 - Address to examine 508 508 %r25 - Old value to check (old) ··· 659 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 886 /* Make sure nothing else is placed on this page */ 663 887 .align PAGE_SIZE 664 888 END(linux_gateway_page) ··· 899 675 /* Light-weight-syscall table */ 900 676 /* Start of lws table. */ 901 677 ENTRY(lws_table) 902 - LWS_ENTRY(compare_and_swap32) /* 0 - ELF32 Atomic compare and swap */ 903 - LWS_ENTRY(compare_and_swap64) /* 1 - ELF64 Atomic compare and swap */ 678 + LWS_ENTRY(compare_and_swap32) /* 0 - ELF32 Atomic 32bit CAS */ 679 + LWS_ENTRY(compare_and_swap64) /* 1 - ELF64 Atomic 32bit CAS */ 680 + LWS_ENTRY(compare_and_swap_2) /* 2 - ELF32 Atomic 64bit CAS */ 904 681 END(lws_table) 905 682 /* End of lws table */ 906 683