aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Pitre <nicolas.pitre@linaro.org>2011-06-19 23:36:03 -0400
committerNicolas Pitre <nico@fluxnic.net>2011-06-28 15:47:47 -0400
commit40fb79c8a88625504857d44de1bc89dc0341e618 (patch)
tree1f0e417a1f1c80fcaa79729f1a4b29e96f01fed2
parent37b8304642c7f91df54888955c373ae89b577fcc (diff)
ARM: add a kuser_cmpxchg64 user space helper
Some user space applications are designed around the ability to perform atomic operations on 64 bit values. Since this is natively possible only with ARMv6k and above, let's provide a new kuser helper to perform the operation with kernel supervision on pre ARMv6k hardware. Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org> Tested-by: Dave Martin <dave.martin@linaro.org>
-rw-r--r--Documentation/arm/kernel_user_helpers.txt64
-rw-r--r--arch/arm/kernel/entry-armv.S99
2 files changed, 160 insertions, 3 deletions
diff --git a/Documentation/arm/kernel_user_helpers.txt b/Documentation/arm/kernel_user_helpers.txt
index 0c33f72d1873..a17df9f91d16 100644
--- a/Documentation/arm/kernel_user_helpers.txt
+++ b/Documentation/arm/kernel_user_helpers.txt
@@ -201,3 +201,67 @@ typedef void (__kuser_dmb_t)(void);
201Notes: 201Notes:
202 202
203 - Valid only if __kuser_helper_version >= 3 (from kernel version 2.6.15). 203 - Valid only if __kuser_helper_version >= 3 (from kernel version 2.6.15).
204
205kuser_cmpxchg64
206---------------
207
208Location: 0xffff0f60
209
210Reference prototype:
211
212 int __kuser_cmpxchg64(const int64_t *oldval,
213 const int64_t *newval,
214 volatile int64_t *ptr);
215
216Input:
217
218 r0 = pointer to oldval
219 r1 = pointer to newval
220 r2 = pointer to target value
221 lr = return address
222
223Output:
224
225 r0 = success code (zero or non-zero)
226 C flag = set if r0 == 0, clear if r0 != 0
227
228Clobbered registers:
229
230 r3, lr, flags
231
232Definition:
233
234 Atomically store the 64-bit value pointed by *newval in *ptr only if *ptr
235 is equal to the 64-bit value pointed by *oldval. Return zero if *ptr was
236 changed or non-zero if no exchange happened.
237
238 The C flag is also set if *ptr was changed to allow for assembly
239 optimization in the calling code.
240
241Usage example:
242
243typedef int (__kuser_cmpxchg64_t)(const int64_t *oldval,
244 const int64_t *newval,
245 volatile int64_t *ptr);
246#define __kuser_cmpxchg64 (*(__kuser_cmpxchg64_t *)0xffff0f60)
247
248int64_t atomic_add64(volatile int64_t *ptr, int64_t val)
249{
250 int64_t old, new;
251
252 do {
253 old = *ptr;
254 new = old + val;
255 } while(__kuser_cmpxchg64(&old, &new, ptr));
256
257 return new;
258}
259
260Notes:
261
262 - This routine already includes memory barriers as needed.
263
264 - Due to the length of this sequence, this spans 2 conventional kuser
265 "slots", therefore 0xffff0f80 is not used as a valid entry point.
266
267 - Valid only if __kuser_helper_version >= 5 (from kernel version 3.1).
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 63f7907c4c3c..9be97deca215 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -383,7 +383,7 @@ ENDPROC(__pabt_svc)
383 .endm 383 .endm
384 384
385 .macro kuser_cmpxchg_check 385 .macro kuser_cmpxchg_check
386#if __LINUX_ARM_ARCH__ < 6 && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG) 386#if !defined(CONFIG_CPU_32v6K) && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
387#ifndef CONFIG_MMU 387#ifndef CONFIG_MMU
388#warning "NPTL on non MMU needs fixing" 388#warning "NPTL on non MMU needs fixing"
389#else 389#else
@@ -392,7 +392,7 @@ ENDPROC(__pabt_svc)
392 @ perform a quick test inline since it should be false 392 @ perform a quick test inline since it should be false
393 @ 99.9999% of the time. The rest is done out of line. 393 @ 99.9999% of the time. The rest is done out of line.
394 cmp r2, #TASK_SIZE 394 cmp r2, #TASK_SIZE
395 blhs kuser_cmpxchg_fixup 395 blhs kuser_cmpxchg64_fixup
396#endif 396#endif
397#endif 397#endif
398 .endm 398 .endm
@@ -775,6 +775,99 @@ ENDPROC(__switch_to)
775 .globl __kuser_helper_start 775 .globl __kuser_helper_start
776__kuser_helper_start: 776__kuser_helper_start:
777 777
778/*
779 * Due to the length of some sequences, __kuser_cmpxchg64 spans 2 regular
780 * kuser "slots", therefore 0xffff0f80 is not used as a valid entry point.
781 */
782
783__kuser_cmpxchg64: @ 0xffff0f60
784
785#if defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
786
787 /*
788 * Poor you. No fast solution possible...
789 * The kernel itself must perform the operation.
790 * A special ghost syscall is used for that (see traps.c).
791 */
792 stmfd sp!, {r7, lr}
793 ldr r7, 1f @ it's 20 bits
794 swi __ARM_NR_cmpxchg64
795 ldmfd sp!, {r7, pc}
7961: .word __ARM_NR_cmpxchg64
797
798#elif defined(CONFIG_CPU_32v6K)
799
800 stmfd sp!, {r4, r5, r6, r7}
801 ldrd r4, r5, [r0] @ load old val
802 ldrd r6, r7, [r1] @ load new val
803 smp_dmb arm
8041: ldrexd r0, r1, [r2] @ load current val
805 eors r3, r0, r4 @ compare with oldval (1)
806 eoreqs r3, r1, r5 @ compare with oldval (2)
807 strexdeq r3, r6, r7, [r2] @ store newval if eq
808 teqeq r3, #1 @ success?
809 beq 1b @ if no then retry
810 smp_dmb arm
811 rsbs r0, r3, #0 @ set returned val and C flag
812 ldmfd sp!, {r4, r5, r6, r7}
813 bx lr
814
815#elif !defined(CONFIG_SMP)
816
817#ifdef CONFIG_MMU
818
819 /*
820 * The only thing that can break atomicity in this cmpxchg64
821 * implementation is either an IRQ or a data abort exception
822 * causing another process/thread to be scheduled in the middle of
823 * the critical sequence. The same strategy as for cmpxchg is used.
824 */
825 stmfd sp!, {r4, r5, r6, lr}
826 ldmia r0, {r4, r5} @ load old val
827 ldmia r1, {r6, lr} @ load new val
8281: ldmia r2, {r0, r1} @ load current val
829 eors r3, r0, r4 @ compare with oldval (1)
830 eoreqs r3, r1, r5 @ compare with oldval (2)
8312: stmeqia r2, {r6, lr} @ store newval if eq
832 rsbs r0, r3, #0 @ set return val and C flag
833 ldmfd sp!, {r4, r5, r6, pc}
834
835 .text
836kuser_cmpxchg64_fixup:
837 @ Called from kuser_cmpxchg_fixup.
838 @ r2 = address of interrupted insn (must be preserved).
839 @ sp = saved regs. r7 and r8 are clobbered.
840 @ 1b = first critical insn, 2b = last critical insn.
841 @ If r2 >= 1b and r2 <= 2b then saved pc_usr is set to 1b.
842 mov r7, #0xffff0fff
843 sub r7, r7, #(0xffff0fff - (0xffff0f60 + (1b - __kuser_cmpxchg64)))
844 subs r8, r2, r7
845 rsbcss r8, r8, #(2b - 1b)
846 strcs r7, [sp, #S_PC]
847#if __LINUX_ARM_ARCH__ < 6
848 bcc kuser_cmpxchg32_fixup
849#endif
850 mov pc, lr
851 .previous
852
853#else
854#warning "NPTL on non MMU needs fixing"
855 mov r0, #-1
856 adds r0, r0, #0
857 usr_ret lr
858#endif
859
860#else
861#error "incoherent kernel configuration"
862#endif
863
864 /* pad to next slot */
865 .rept (16 - (. - __kuser_cmpxchg64)/4)
866 .word 0
867 .endr
868
869 .align 5
870
778__kuser_memory_barrier: @ 0xffff0fa0 871__kuser_memory_barrier: @ 0xffff0fa0
779 smp_dmb arm 872 smp_dmb arm
780 usr_ret lr 873 usr_ret lr
@@ -816,7 +909,7 @@ __kuser_cmpxchg: @ 0xffff0fc0
816 usr_ret lr 909 usr_ret lr
817 910
818 .text 911 .text
819kuser_cmpxchg_fixup: 912kuser_cmpxchg32_fixup:
820 @ Called from kuser_cmpxchg_check macro. 913 @ Called from kuser_cmpxchg_check macro.
821 @ r2 = address of interrupted insn (must be preserved). 914 @ r2 = address of interrupted insn (must be preserved).
822 @ sp = saved regs. r7 and r8 are clobbered. 915 @ sp = saved regs. r7 and r8 are clobbered.