diff options
author | Rich Felker <dalias@libc.org> | 2016-04-25 17:58:03 -0400 |
---|---|---|
committer | Rich Felker <dalias@libc.org> | 2016-08-04 23:29:36 -0400 |
commit | 00b73d8d1b7131da03aec73011a7286f566fe87f (patch) | |
tree | 5a557b49eeaa1717bba015c730c5a56e8d084985 | |
parent | 2b47d54ed41c33baf5825185168b493317c5572f (diff) |
sh: add working futex atomic ops on userspace addresses for smp
The version of futex.h in asm-generic should really be adapted to do
the same thing so that this hideous code does not have to be
duplicated per-arch.
Signed-off-by: Rich Felker <dalias@libc.org>
-rw-r--r-- | arch/sh/include/asm/futex-cas.h | 34 | ||||
-rw-r--r-- | arch/sh/include/asm/futex-irq.h | 86 | ||||
-rw-r--r-- | arch/sh/include/asm/futex-llsc.h | 41 | ||||
-rw-r--r-- | arch/sh/include/asm/futex.h | 97 |
4 files changed, 134 insertions, 124 deletions
diff --git a/arch/sh/include/asm/futex-cas.h b/arch/sh/include/asm/futex-cas.h new file mode 100644 index 000000000000..267cb7a5f101 --- /dev/null +++ b/arch/sh/include/asm/futex-cas.h | |||
@@ -0,0 +1,34 @@ | |||
1 | #ifndef __ASM_SH_FUTEX_CAS_H | ||
2 | #define __ASM_SH_FUTEX_CAS_H | ||
3 | |||
4 | static inline int atomic_futex_op_cmpxchg_inatomic(u32 *uval, | ||
5 | u32 __user *uaddr, | ||
6 | u32 oldval, u32 newval) | ||
7 | { | ||
8 | int err = 0; | ||
9 | __asm__ __volatile__( | ||
10 | "1:\n\t" | ||
11 | "cas.l %2, %1, @r0\n" | ||
12 | "2:\n\t" | ||
13 | #ifdef CONFIG_MMU | ||
14 | ".section .fixup,\"ax\"\n" | ||
15 | "3:\n\t" | ||
16 | "mov.l 4f, %0\n\t" | ||
17 | "jmp @%0\n\t" | ||
18 | " mov %3, %0\n\t" | ||
19 | ".balign 4\n" | ||
20 | "4: .long 2b\n\t" | ||
21 | ".previous\n" | ||
22 | ".section __ex_table,\"a\"\n\t" | ||
23 | ".long 1b, 3b\n\t" | ||
24 | ".previous" | ||
25 | #endif | ||
26 | :"+r" (err), "+r" (newval) | ||
27 | :"r" (oldval), "i" (-EFAULT), "z" (uaddr) | ||
28 | :"t", "memory"); | ||
29 | if (err) return err; | ||
30 | *uval = newval; | ||
31 | return 0; | ||
32 | } | ||
33 | |||
34 | #endif /* __ASM_SH_FUTEX_CAS_H */ | ||
diff --git a/arch/sh/include/asm/futex-irq.h b/arch/sh/include/asm/futex-irq.h index 63d33129ea23..ab01dbee0a82 100644 --- a/arch/sh/include/asm/futex-irq.h +++ b/arch/sh/include/asm/futex-irq.h | |||
@@ -1,92 +1,6 @@ | |||
1 | #ifndef __ASM_SH_FUTEX_IRQ_H | 1 | #ifndef __ASM_SH_FUTEX_IRQ_H |
2 | #define __ASM_SH_FUTEX_IRQ_H | 2 | #define __ASM_SH_FUTEX_IRQ_H |
3 | 3 | ||
4 | |||
5 | static inline int atomic_futex_op_xchg_set(int oparg, u32 __user *uaddr, | ||
6 | int *oldval) | ||
7 | { | ||
8 | unsigned long flags; | ||
9 | int ret; | ||
10 | |||
11 | local_irq_save(flags); | ||
12 | |||
13 | ret = get_user(*oldval, uaddr); | ||
14 | if (!ret) | ||
15 | ret = put_user(oparg, uaddr); | ||
16 | |||
17 | local_irq_restore(flags); | ||
18 | |||
19 | return ret; | ||
20 | } | ||
21 | |||
22 | static inline int atomic_futex_op_xchg_add(int oparg, u32 __user *uaddr, | ||
23 | int *oldval) | ||
24 | { | ||
25 | unsigned long flags; | ||
26 | int ret; | ||
27 | |||
28 | local_irq_save(flags); | ||
29 | |||
30 | ret = get_user(*oldval, uaddr); | ||
31 | if (!ret) | ||
32 | ret = put_user(*oldval + oparg, uaddr); | ||
33 | |||
34 | local_irq_restore(flags); | ||
35 | |||
36 | return ret; | ||
37 | } | ||
38 | |||
39 | static inline int atomic_futex_op_xchg_or(int oparg, u32 __user *uaddr, | ||
40 | int *oldval) | ||
41 | { | ||
42 | unsigned long flags; | ||
43 | int ret; | ||
44 | |||
45 | local_irq_save(flags); | ||
46 | |||
47 | ret = get_user(*oldval, uaddr); | ||
48 | if (!ret) | ||
49 | ret = put_user(*oldval | oparg, uaddr); | ||
50 | |||
51 | local_irq_restore(flags); | ||
52 | |||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | static inline int atomic_futex_op_xchg_and(int oparg, u32 __user *uaddr, | ||
57 | int *oldval) | ||
58 | { | ||
59 | unsigned long flags; | ||
60 | int ret; | ||
61 | |||
62 | local_irq_save(flags); | ||
63 | |||
64 | ret = get_user(*oldval, uaddr); | ||
65 | if (!ret) | ||
66 | ret = put_user(*oldval & oparg, uaddr); | ||
67 | |||
68 | local_irq_restore(flags); | ||
69 | |||
70 | return ret; | ||
71 | } | ||
72 | |||
73 | static inline int atomic_futex_op_xchg_xor(int oparg, u32 __user *uaddr, | ||
74 | int *oldval) | ||
75 | { | ||
76 | unsigned long flags; | ||
77 | int ret; | ||
78 | |||
79 | local_irq_save(flags); | ||
80 | |||
81 | ret = get_user(*oldval, uaddr); | ||
82 | if (!ret) | ||
83 | ret = put_user(*oldval ^ oparg, uaddr); | ||
84 | |||
85 | local_irq_restore(flags); | ||
86 | |||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | static inline int atomic_futex_op_cmpxchg_inatomic(u32 *uval, | 4 | static inline int atomic_futex_op_cmpxchg_inatomic(u32 *uval, |
91 | u32 __user *uaddr, | 5 | u32 __user *uaddr, |
92 | u32 oldval, u32 newval) | 6 | u32 oldval, u32 newval) |
diff --git a/arch/sh/include/asm/futex-llsc.h b/arch/sh/include/asm/futex-llsc.h new file mode 100644 index 000000000000..23591703bec0 --- /dev/null +++ b/arch/sh/include/asm/futex-llsc.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef __ASM_SH_FUTEX_LLSC_H | ||
2 | #define __ASM_SH_FUTEX_LLSC_H | ||
3 | |||
4 | static inline int atomic_futex_op_cmpxchg_inatomic(u32 *uval, | ||
5 | u32 __user *uaddr, | ||
6 | u32 oldval, u32 newval) | ||
7 | { | ||
8 | int err = 0; | ||
9 | __asm__ __volatile__( | ||
10 | "synco\n" | ||
11 | "1:\n\t" | ||
12 | "movli.l @%2, r0\n\t" | ||
13 | "mov r0, %1\n\t" | ||
14 | "cmp/eq %1, %4\n\t" | ||
15 | "bf 2f\n\t" | ||
16 | "mov %5, r0\n\t" | ||
17 | "movco.l r0, @%2\n\t" | ||
18 | "bf 1b\n" | ||
19 | "2:\n\t" | ||
20 | "synco\n\t" | ||
21 | #ifdef CONFIG_MMU | ||
22 | ".section .fixup,\"ax\"\n" | ||
23 | "3:\n\t" | ||
24 | "mov.l 4f, %0\n\t" | ||
25 | "jmp @%0\n\t" | ||
26 | " mov %3, %0\n\t" | ||
27 | ".balign 4\n" | ||
28 | "4: .long 2b\n\t" | ||
29 | ".previous\n" | ||
30 | ".section __ex_table,\"a\"\n\t" | ||
31 | ".long 1b, 3b\n\t" | ||
32 | ".previous" | ||
33 | #endif | ||
34 | :"+r" (err), "=&r" (*uval) | ||
35 | :"r" (uaddr), "i" (-EFAULT), "r" (oldval), "r" (newval) | ||
36 | :"t", "memory", "r0"); | ||
37 | if (err) return err; | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | #endif /* __ASM_SH_FUTEX_LLSC_H */ | ||
diff --git a/arch/sh/include/asm/futex.h b/arch/sh/include/asm/futex.h index 7be39a646fbd..d0078747d308 100644 --- a/arch/sh/include/asm/futex.h +++ b/arch/sh/include/asm/futex.h | |||
@@ -7,16 +7,34 @@ | |||
7 | #include <linux/uaccess.h> | 7 | #include <linux/uaccess.h> |
8 | #include <asm/errno.h> | 8 | #include <asm/errno.h> |
9 | 9 | ||
10 | /* XXX: UP variants, fix for SH-4A and SMP.. */ | 10 | #if !defined(CONFIG_SMP) |
11 | #include <asm/futex-irq.h> | 11 | #include <asm/futex-irq.h> |
12 | #elif defined(CONFIG_CPU_J2) | ||
13 | #include <asm/futex-cas.h> | ||
14 | #elif defined(CONFIG_CPU_SH4A) | ||
15 | #include <asm/futex-llsc.h> | ||
16 | #else | ||
17 | #error SMP not supported on this configuration. | ||
18 | #endif | ||
19 | |||
20 | static inline int | ||
21 | futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, | ||
22 | u32 oldval, u32 newval) | ||
23 | { | ||
24 | if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) | ||
25 | return -EFAULT; | ||
26 | |||
27 | return atomic_futex_op_cmpxchg_inatomic(uval, uaddr, oldval, newval); | ||
28 | } | ||
12 | 29 | ||
13 | static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) | 30 | static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) |
14 | { | 31 | { |
15 | int op = (encoded_op >> 28) & 7; | 32 | int op = (encoded_op >> 28) & 7; |
16 | int cmp = (encoded_op >> 24) & 15; | 33 | int cmp = (encoded_op >> 24) & 15; |
17 | int oparg = (encoded_op << 8) >> 20; | 34 | u32 oparg = (encoded_op << 8) >> 20; |
18 | int cmparg = (encoded_op << 20) >> 20; | 35 | u32 cmparg = (encoded_op << 20) >> 20; |
19 | int oldval = 0, ret; | 36 | u32 oldval, newval, prev; |
37 | int ret; | ||
20 | 38 | ||
21 | if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) | 39 | if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) |
22 | oparg = 1 << oparg; | 40 | oparg = 1 << oparg; |
@@ -26,26 +44,39 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) | |||
26 | 44 | ||
27 | pagefault_disable(); | 45 | pagefault_disable(); |
28 | 46 | ||
29 | switch (op) { | 47 | do { |
30 | case FUTEX_OP_SET: | 48 | if (op == FUTEX_OP_SET) |
31 | ret = atomic_futex_op_xchg_set(oparg, uaddr, &oldval); | 49 | ret = oldval = 0; |
32 | break; | 50 | else |
33 | case FUTEX_OP_ADD: | 51 | ret = get_user(oldval, uaddr); |
34 | ret = atomic_futex_op_xchg_add(oparg, uaddr, &oldval); | 52 | |
35 | break; | 53 | if (ret) break; |
36 | case FUTEX_OP_OR: | 54 | |
37 | ret = atomic_futex_op_xchg_or(oparg, uaddr, &oldval); | 55 | switch (op) { |
38 | break; | 56 | case FUTEX_OP_SET: |
39 | case FUTEX_OP_ANDN: | 57 | newval = oparg; |
40 | ret = atomic_futex_op_xchg_and(~oparg, uaddr, &oldval); | 58 | break; |
41 | break; | 59 | case FUTEX_OP_ADD: |
42 | case FUTEX_OP_XOR: | 60 | newval = oldval + oparg; |
43 | ret = atomic_futex_op_xchg_xor(oparg, uaddr, &oldval); | 61 | break; |
44 | break; | 62 | case FUTEX_OP_OR: |
45 | default: | 63 | newval = oldval | oparg; |
46 | ret = -ENOSYS; | 64 | break; |
47 | break; | 65 | case FUTEX_OP_ANDN: |
48 | } | 66 | newval = oldval & ~oparg; |
67 | break; | ||
68 | case FUTEX_OP_XOR: | ||
69 | newval = oldval ^ oparg; | ||
70 | break; | ||
71 | default: | ||
72 | ret = -ENOSYS; | ||
73 | break; | ||
74 | } | ||
75 | |||
76 | if (ret) break; | ||
77 | |||
78 | ret = futex_atomic_cmpxchg_inatomic(&prev, uaddr, oldval, newval); | ||
79 | } while (!ret && prev != oldval); | ||
49 | 80 | ||
50 | pagefault_enable(); | 81 | pagefault_enable(); |
51 | 82 | ||
@@ -53,10 +84,10 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) | |||
53 | switch (cmp) { | 84 | switch (cmp) { |
54 | case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break; | 85 | case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break; |
55 | case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break; | 86 | case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break; |
56 | case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break; | 87 | case FUTEX_OP_CMP_LT: ret = ((int)oldval < (int)cmparg); break; |
57 | case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break; | 88 | case FUTEX_OP_CMP_GE: ret = ((int)oldval >= (int)cmparg); break; |
58 | case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break; | 89 | case FUTEX_OP_CMP_LE: ret = ((int)oldval <= (int)cmparg); break; |
59 | case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break; | 90 | case FUTEX_OP_CMP_GT: ret = ((int)oldval > (int)cmparg); break; |
60 | default: ret = -ENOSYS; | 91 | default: ret = -ENOSYS; |
61 | } | 92 | } |
62 | } | 93 | } |
@@ -64,15 +95,5 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) | |||
64 | return ret; | 95 | return ret; |
65 | } | 96 | } |
66 | 97 | ||
67 | static inline int | ||
68 | futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, | ||
69 | u32 oldval, u32 newval) | ||
70 | { | ||
71 | if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) | ||
72 | return -EFAULT; | ||
73 | |||
74 | return atomic_futex_op_cmpxchg_inatomic(uval, uaddr, oldval, newval); | ||
75 | } | ||
76 | |||
77 | #endif /* __KERNEL__ */ | 98 | #endif /* __KERNEL__ */ |
78 | #endif /* __ASM_SH_FUTEX_H */ | 99 | #endif /* __ASM_SH_FUTEX_H */ |