blob: b5835325d44b06889027faad5c72086700b362d1 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001/* SPDX-License-Identifier: GPL-2.0 */
Carlos O'Donell342a0492006-09-07 13:05:17 -04002#ifndef _ASM_PARISC_FUTEX_H
3#define _ASM_PARISC_FUTEX_H
Jakub Jelinek4732efbe2005-09-06 15:16:25 -07004
Carlos O'Donell342a0492006-09-07 13:05:17 -04005#include <linux/futex.h>
Jeff Dike730f4122008-04-30 00:54:49 -07006#include <linux/uaccess.h>
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -04007#include <asm/atomic.h>
Carlos O'Donell342a0492006-09-07 13:05:17 -04008#include <asm/errno.h>
Carlos O'Donell342a0492006-09-07 13:05:17 -04009
John David Anglin8b232812011-10-09 16:40:10 -040010/* The following has to match the LWS code in syscall.S. We have
John David Anglind0585d72022-01-04 21:44:32 +000011 * 256 four-word locks. We use bits 20-27 of the futex virtual
12 * address for the hash index.
13 */
14
15static inline unsigned long _futex_hash_index(unsigned long ua)
16{
17 return (ua >> 2) & 0x3fc;
18}
John David Anglin8b232812011-10-09 16:40:10 -040019
20static inline void
John David Anglind0585d72022-01-04 21:44:32 +000021_futex_spin_lock_irqsave(arch_spinlock_t *s, unsigned long *flags)
John David Anglin8b232812011-10-09 16:40:10 -040022{
John David Anglind0585d72022-01-04 21:44:32 +000023 local_irq_save(*flags);
John David Anglin8b232812011-10-09 16:40:10 -040024 arch_spin_lock(s);
25}
26
27static inline void
John David Anglind0585d72022-01-04 21:44:32 +000028_futex_spin_unlock_irqrestore(arch_spinlock_t *s, unsigned long *flags)
John David Anglin8b232812011-10-09 16:40:10 -040029{
John David Anglin8b232812011-10-09 16:40:10 -040030 arch_spin_unlock(s);
John David Anglind0585d72022-01-04 21:44:32 +000031 local_irq_restore(*flags);
John David Anglin8b232812011-10-09 16:40:10 -040032}
33
Carlos O'Donell342a0492006-09-07 13:05:17 -040034static inline int
Jiri Slaby30d6e0a2017-08-24 09:31:05 +020035arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
Carlos O'Donell342a0492006-09-07 13:05:17 -040036{
John David Anglind0585d72022-01-04 21:44:32 +000037 extern u32 lws_lock_start[];
38 unsigned long ua = (unsigned long)uaddr;
39 arch_spinlock_t *s;
40 unsigned long flags;
John David Anglin99aed912016-05-21 15:03:54 -040041 int oldval, ret;
42 u32 tmp;
43
John David Anglind0585d72022-01-04 21:44:32 +000044 s = (arch_spinlock_t *)&lws_lock_start[_futex_hash_index(ua)];
45 _futex_spin_lock_irqsave(s, &flags);
Dave Anglin7e992712021-11-03 12:49:32 +010046
John David Anglind0585d72022-01-04 21:44:32 +000047 /* Return -EFAULT if we encounter a page fault or COW break */
48 if (unlikely(get_user(oldval, uaddr) != 0)) {
49 ret = -EFAULT;
John David Anglin99aed912016-05-21 15:03:54 -040050 goto out_pagefault_enable;
John David Anglind0585d72022-01-04 21:44:32 +000051 }
John David Anglin99aed912016-05-21 15:03:54 -040052
53 ret = 0;
54 tmp = oldval;
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -040055
Carlos O'Donell342a0492006-09-07 13:05:17 -040056 switch (op) {
57 case FUTEX_OP_SET:
John David Anglin99aed912016-05-21 15:03:54 -040058 tmp = oparg;
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -040059 break;
Carlos O'Donell342a0492006-09-07 13:05:17 -040060 case FUTEX_OP_ADD:
John David Anglin99aed912016-05-21 15:03:54 -040061 tmp += oparg;
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -040062 break;
Carlos O'Donell342a0492006-09-07 13:05:17 -040063 case FUTEX_OP_OR:
John David Anglin99aed912016-05-21 15:03:54 -040064 tmp |= oparg;
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -040065 break;
Carlos O'Donell342a0492006-09-07 13:05:17 -040066 case FUTEX_OP_ANDN:
John David Anglin99aed912016-05-21 15:03:54 -040067 tmp &= ~oparg;
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -040068 break;
Carlos O'Donell342a0492006-09-07 13:05:17 -040069 case FUTEX_OP_XOR:
John David Anglin99aed912016-05-21 15:03:54 -040070 tmp ^= oparg;
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -040071 break;
Carlos O'Donell342a0492006-09-07 13:05:17 -040072 default:
73 ret = -ENOSYS;
John David Anglind0585d72022-01-04 21:44:32 +000074 goto out_pagefault_enable;
Carlos O'Donell342a0492006-09-07 13:05:17 -040075 }
76
John David Anglind0585d72022-01-04 21:44:32 +000077 if (unlikely(put_user(tmp, uaddr) != 0))
John David Anglin99aed912016-05-21 15:03:54 -040078 ret = -EFAULT;
79
80out_pagefault_enable:
John David Anglind0585d72022-01-04 21:44:32 +000081 _futex_spin_unlock_irqrestore(s, &flags);
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -040082
Jiri Slaby30d6e0a2017-08-24 09:31:05 +020083 if (!ret)
84 *oval = oldval;
85
Carlos O'Donell342a0492006-09-07 13:05:17 -040086 return ret;
87}
88
Carlos O'Donell342a0492006-09-07 13:05:17 -040089static inline int
Michel Lespinasse8d7718a2011-03-10 18:50:58 -080090futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
91 u32 oldval, u32 newval)
Carlos O'Donell342a0492006-09-07 13:05:17 -040092{
John David Anglind0585d72022-01-04 21:44:32 +000093 extern u32 lws_lock_start[];
94 unsigned long ua = (unsigned long)uaddr;
95 arch_spinlock_t *s;
Michel Lespinasse8d7718a2011-03-10 18:50:58 -080096 u32 val;
John David Anglind0585d72022-01-04 21:44:32 +000097 unsigned long flags;
Carlos O'Donell342a0492006-09-07 13:05:17 -040098
Kyle McMartinc20a84c2008-03-01 10:25:52 -080099 /* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is
100 * our gateway page, and causes no end of trouble...
101 */
Al Virodb68ce12017-03-20 21:08:07 -0400102 if (uaccess_kernel() && !uaddr)
Kyle McMartinc20a84c2008-03-01 10:25:52 -0800103 return -EFAULT;
104
Linus Torvalds96d4f262019-01-03 18:57:57 -0800105 if (!access_ok(uaddr, sizeof(u32)))
Carlos O'Donell342a0492006-09-07 13:05:17 -0400106 return -EFAULT;
107
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -0400108 /* HPPA has no cmpxchg in hardware and therefore the
109 * best we can do here is use an array of locks. The
John David Anglind0585d72022-01-04 21:44:32 +0000110 * lock selected is based on a hash of the virtual
111 * address of the futex. This should scale to a couple
112 * of CPUs.
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -0400113 */
114
John David Anglind0585d72022-01-04 21:44:32 +0000115 s = (arch_spinlock_t *)&lws_lock_start[_futex_hash_index(ua)];
116 _futex_spin_lock_irqsave(s, &flags);
John David Anglin99aed912016-05-21 15:03:54 -0400117 if (unlikely(get_user(val, uaddr) != 0)) {
John David Anglind0585d72022-01-04 21:44:32 +0000118 _futex_spin_unlock_irqrestore(s, &flags);
John David Anglin99aed912016-05-21 15:03:54 -0400119 return -EFAULT;
120 }
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -0400121
John David Anglin99aed912016-05-21 15:03:54 -0400122 if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) {
John David Anglind0585d72022-01-04 21:44:32 +0000123 _futex_spin_unlock_irqrestore(s, &flags);
John David Anglin99aed912016-05-21 15:03:54 -0400124 return -EFAULT;
125 }
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -0400126
Michel Lespinasse37a9d912011-03-10 18:48:51 -0800127 *uval = val;
John David Anglind0585d72022-01-04 21:44:32 +0000128 _futex_spin_unlock_irqrestore(s, &flags);
Carlos O'Donelld9ba5fe2011-07-08 17:27:00 -0400129
John David Anglin99aed912016-05-21 15:03:54 -0400130 return 0;
Carlos O'Donell342a0492006-09-07 13:05:17 -0400131}
132
Kyle McMartinc20a84c2008-03-01 10:25:52 -0800133#endif /*_ASM_PARISC_FUTEX_H*/