[PATCH] s390: fix in-user atomic futex operation.
[linux-2.6.git] / include / asm-s390 / futex.h
1 #ifndef _ASM_S390_FUTEX_H
2 #define _ASM_S390_FUTEX_H
3
4 #ifdef __KERNEL__
5
6 #include <linux/futex.h>
7 #include <asm/errno.h>
8 #include <asm/uaccess.h>
9
10 #ifndef __s390x__
11 #define __futex_atomic_fixup \
12                      ".section __ex_table,\"a\"\n"                      \
13                      "   .align 4\n"                                    \
14                      "   .long  0b,4b,2b,4b,3b,4b\n"                    \
15                      ".previous"
16 #else /* __s390x__ */
17 #define __futex_atomic_fixup \
18                      ".section __ex_table,\"a\"\n"                      \
19                      "   .align 8\n"                                    \
20                      "   .quad  0b,4b,2b,4b,3b,4b\n"                    \
21                      ".previous"
22 #endif /* __s390x__ */
23
24 #define __futex_atomic_op(insn, ret, oldval, newval, uaddr, oparg)      \
25         asm volatile("   sacf 256\n"                                    \
26                      "0: l   %1,0(%6)\n"                                \
27                      "1: " insn                                         \
28                      "2: cs  %1,%2,0(%6)\n"                             \
29                      "3: jl  1b\n"                                      \
30                      "   lhi %0,0\n"                                    \
31                      "4: sacf 0\n"                                      \
32                      __futex_atomic_fixup                               \
33                      : "=d" (ret), "=&d" (oldval), "=&d" (newval),      \
34                        "=m" (*uaddr)                                    \
35                      : "0" (-EFAULT), "d" (oparg), "a" (uaddr),         \
36                        "m" (*uaddr) : "cc" );
37
38 static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr)
39 {
40         int op = (encoded_op >> 28) & 7;
41         int cmp = (encoded_op >> 24) & 15;
42         int oparg = (encoded_op << 8) >> 20;
43         int cmparg = (encoded_op << 20) >> 20;
44         int oldval = 0, newval, ret;
45         if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
46                 oparg = 1 << oparg;
47
48         if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int)))
49                 return -EFAULT;
50
51         inc_preempt_count();
52
53         switch (op) {
54         case FUTEX_OP_SET:
55                 __futex_atomic_op("lr %2,%5\n",
56                                   ret, oldval, newval, uaddr, oparg);
57                 break;
58         case FUTEX_OP_ADD:
59                 __futex_atomic_op("lr %2,%1\nar %2,%5\n",
60                                   ret, oldval, newval, uaddr, oparg);
61                 break;
62         case FUTEX_OP_OR:
63                 __futex_atomic_op("lr %2,%1\nor %2,%5\n",
64                                   ret, oldval, newval, uaddr, oparg);
65                 break;
66         case FUTEX_OP_ANDN:
67                 __futex_atomic_op("lr %2,%1\nnr %2,%5\n",
68                                   ret, oldval, newval, uaddr, oparg);
69                 break;
70         case FUTEX_OP_XOR:
71                 __futex_atomic_op("lr %2,%1\nxr %2,%5\n",
72                                   ret, oldval, newval, uaddr, oparg);
73                 break;
74         default:
75                 ret = -ENOSYS;
76         }
77
78         dec_preempt_count();
79
80         if (!ret) {
81                 switch (cmp) {
82                 case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
83                 case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
84                 case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
85                 case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
86                 case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
87                 case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
88                 default: ret = -ENOSYS;
89                 }
90         }
91         return ret;
92 }
93
94 static inline int
95 futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval)
96 {
97         int ret;
98
99         if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int)))
100                 return -EFAULT;
101         asm volatile("   cs   %1,%4,0(%5)\n"
102                      "0: lr   %0,%1\n"
103                      "1:\n"
104 #ifndef __s390x__
105                      ".section __ex_table,\"a\"\n"
106                      "   .align 4\n"
107                      "   .long  0b,1b\n"
108                      ".previous"
109 #else /* __s390x__ */
110                      ".section __ex_table,\"a\"\n"
111                      "   .align 8\n"
112                      "   .quad  0b,1b\n"
113                      ".previous"
114 #endif /* __s390x__ */
115                      : "=d" (ret), "+d" (oldval), "=m" (*uaddr)
116                      : "0" (-EFAULT), "d" (newval), "a" (uaddr), "m" (*uaddr)
117                      : "cc", "memory" );
118         return oldval;
119 }
120
121 #endif /* __KERNEL__ */
122 #endif /* _ASM_S390_FUTEX_H */