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