aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRich Felker <dalias@libc.org>2016-04-25 17:58:03 -0400
committerRich Felker <dalias@libc.org>2016-08-04 23:29:36 -0400
commit00b73d8d1b7131da03aec73011a7286f566fe87f (patch)
tree5a557b49eeaa1717bba015c730c5a56e8d084985
parent2b47d54ed41c33baf5825185168b493317c5572f (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.h34
-rw-r--r--arch/sh/include/asm/futex-irq.h86
-rw-r--r--arch/sh/include/asm/futex-llsc.h41
-rw-r--r--arch/sh/include/asm/futex.h97
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
4static 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
5static 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
22static 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
39static 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
56static 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
73static 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
90static inline int atomic_futex_op_cmpxchg_inatomic(u32 *uval, 4static 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
4static 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
20static inline int
21futex_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
13static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) 30static 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
67static inline int
68futex_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 */