From: Magnus Lindholm <linmag7@gmail.com>
To: davem@davemloft.net, andreas@gaisler.com
Cc: sam@ravnborg.org, sparclinux@vger.kernel.org,
linux-kernel@vger.kernel.org, linmag7@gmail.com
Subject: [RFC PATCH 3/5] sparc32: implement futex atomic ops with the compare-and-swap locks
Date: Wed, 23 Sep 2026 22:17:19 +0200 [thread overview]
Message-ID: <20260923201830.865553-4-linmag7@gmail.com> (raw)
In-Reply-To: <20260923201830.865553-1-linmag7@gmail.com>
sparc32 had no futex implementation of its own and fell back to the
asm-generic one, which on SMP cannot work: it needs to update a user word
atomically, and pre-v9 sparc has no instruction for that. CONFIG_FUTEX
was therefore made to depend on !(SPARC32 && SMP).
Now that the kernel owns a compare-and-swap on behalf of userspace, the
futex code can use the same mechanism. Where the cpu lacks the
instruction both sides serialise on the lock array used by the trap type
0x91 handler; both callers go through sparc32_atomic_lock(), so the two
hashes cannot drift apart, parisc keeps the same invariant with a comment
asking that its futex hash match its LWS code.
Where the cpu has the instruction that lock cannot serve for the
read-modify-write. A binary built for such a cpu does its own casa and
knows nothing about the array, so a lock, get_user, put_user, unlock
sequence can lose that binary's update between the two accesses while both
operations appear to have succeeded and FUTEX_WAKE_OP defines its update
of uaddr2 as atomic. arch_futex_atomic_op_inuser() therefore retries a
casa until the word is still the one the new value was computed from, the
way sparc64 does it. Only the pre-v9 path takes the lock, where it is
correct because userspace takes the same lock through the trap.
Both entry points are called with page faults already disabled, so the
user accesses fail rather than sleeping under the lock.
With a real cmpxchg available the Kconfig dependency can go, which is what
lets FUTEX_PI and robust futexes be built for this configuration rather
than refused. Their runtime behaviour here has not been tested.
One gap is known and not closed here.
futex_robust_unlock() clears the futex word with
unsafe_atomic_store_release_user(), before the hash bucket is locked and
without going through either helper added here. SPARC32 currently uses
the generic implementation of unsafe_atomic_store_release_user(), which
ultimately performs a plain user-memory store. On a no-CASA SMP system
that store does not acquire the lock used by the SPARC32 software-CAS
implementation. It can therefore occur between the trap handler's load
and conditional store, allowing the CAS to overwrite the unlock:
cpu 0, trap compare-and-swap cpu 1, robust unlock
take sparc32_atomic_lock(uaddr)
read *uaddr -> T
store 0 -> *uaddr, no lock
compare against T succeeds
store T | FUTEX_WAITERS -> *uaddr
release the lock, report success
Neither order of those two operations permits that result, so the missing
serialisation is what produced it. The flag itself is not gated
CONFIG_FUTEX_ROBUST_UNLOCK covers only the rseq fixup path, so dropping
the Kconfig dependency is what exposes this on SPARC32 SMP. It has not
been observed in practice; it is reported because it follows from the
implementation.
The requirement is only that these two operations agree on how they
serialise access to the word. The macro is overridable, so SPARC32 could
supply its own definition and keep this inside the architecture; whether
that is preferable to a futex-specific interface is a question for the
futex maintainers, and is asked rather than guessed at.
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/include/asm/futex_32.h | 137 +++++++++++++++++++++++++++++-
init/Kconfig | 1 -
2 files changed, 135 insertions(+), 3 deletions(-)
diff --git a/arch/sparc/include/asm/futex_32.h b/arch/sparc/include/asm/futex_32.h
index 6a332a9f099c..35ff08a6b2bd 100644
--- a/arch/sparc/include/asm/futex_32.h
+++ b/arch/sparc/include/asm/futex_32.h
@@ -1,6 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_FUTEX_H
#define _ASM_FUTEX_H
-#include <asm-generic/futex.h>
+#include <linux/futex.h>
+#include <linux/uaccess.h>
+#include <asm/errno.h>
+#include <asm/cas_32.h>
+#include <asm/cpu_type.h>
-#endif
+/* These share the compare-and-swap trap's lock array and must hash
+ * identically to it; see asm/cas_32.h.
+ */
+
+static inline int sparc32_futex_op(int op, int oparg, u32 oldval, u32 *newval)
+{
+ switch (op) {
+ case FUTEX_OP_SET:
+ *newval = oparg;
+ break;
+ case FUTEX_OP_ADD:
+ *newval = oldval + oparg;
+ break;
+ case FUTEX_OP_OR:
+ *newval = oldval | oparg;
+ break;
+ case FUTEX_OP_ANDN:
+ *newval = oldval & ~oparg;
+ break;
+ case FUTEX_OP_XOR:
+ *newval = oldval ^ oparg;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+static inline int
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
+{
+ raw_spinlock_t *lock;
+ unsigned long flags;
+ u32 oldval, newval, prev;
+ int ret = 0;
+
+ if (sparc32_has_casa) {
+ if (!access_ok(uaddr, sizeof(u32)))
+ return -EFAULT;
+
+ if (unlikely(get_user(oldval, uaddr) != 0))
+ return -EFAULT;
+
+ /* Retry until the word still holds what newval was computed
+ * from, making the read-modify-write one atomic step.
+ */
+ for (;;) {
+ ret = sparc32_futex_op(op, oparg, oldval, &newval);
+ if (ret)
+ return ret;
+
+ if (unlikely(__sparc32_casa_user(uaddr, oldval,
+ newval, &prev) != 0))
+ return -EFAULT;
+
+ if (likely(prev == oldval))
+ break;
+
+ oldval = prev;
+ }
+
+ *oval = oldval;
+ return 0;
+ }
+
+ lock = sparc32_atomic_lock(uaddr);
+ raw_spin_lock_irqsave(lock, flags);
+
+ if (unlikely(get_user(oldval, uaddr) != 0)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = sparc32_futex_op(op, oparg, oldval, &newval);
+ if (ret)
+ goto out;
+
+ if (unlikely(put_user(newval, uaddr) != 0))
+ ret = -EFAULT;
+
+out:
+ raw_spin_unlock_irqrestore(lock, flags);
+
+ if (!ret)
+ *oval = oldval;
+
+ return ret;
+}
+
+static inline int
+futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
+ u32 oldval, u32 newval)
+{
+ raw_spinlock_t *lock;
+ unsigned long flags;
+ u32 val;
+
+ if (!access_ok(uaddr, sizeof(u32)))
+ return -EFAULT;
+
+ /* casa is atomic against userspace's own; a lock of ours is not. */
+ if (sparc32_has_casa) {
+ if (__sparc32_casa_user(uaddr, oldval, newval, &val))
+ return -EFAULT;
+ *uval = val;
+ return 0;
+ }
+
+ lock = sparc32_atomic_lock(uaddr);
+ raw_spin_lock_irqsave(lock, flags);
+
+ if (unlikely(get_user(val, uaddr) != 0)) {
+ raw_spin_unlock_irqrestore(lock, flags);
+ return -EFAULT;
+ }
+
+ if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) {
+ raw_spin_unlock_irqrestore(lock, flags);
+ return -EFAULT;
+ }
+
+ raw_spin_unlock_irqrestore(lock, flags);
+
+ *uval = val;
+ return 0;
+}
+
+#endif /* _ASM_FUTEX_H */
diff --git a/init/Kconfig b/init/Kconfig
index 8583d9f06c52..9eb8086af7db 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1875,7 +1875,6 @@ config BASE_SMALL
config FUTEX
bool "Enable futex support" if EXPERT
- depends on !(SPARC32 && SMP)
default y
imply RT_MUTEXES
help
--
2.43.0
next prev parent reply other threads:[~2026-09-23 20:19 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-23 20:17 [RFC PATCH 0/5] sparc32: kernel assisted compare-and-swap, and futex on SMP Magnus Lindholm
2026-09-23 20:17 ` [RFC PATCH 1/5] sparc32: detect the compare-and-swap instruction at boot Magnus Lindholm
2026-09-23 20:17 ` [RFC PATCH 2/5] sparc32: add a kernel assisted compare-and-swap Magnus Lindholm
2026-09-23 20:17 ` Magnus Lindholm [this message]
2026-09-23 20:17 ` [RFC PATCH 4/5] sparc32: advertise the compare-and-swap trap in AT_HWCAP Magnus Lindholm
2026-09-23 20:17 ` [RFC PATCH 5/5] sparc32: document the compare-and-swap trap ABI Magnus Lindholm
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260923201830.865553-4-linmag7@gmail.com \
--to=linmag7@gmail.com \
--cc=andreas@gaisler.com \
--cc=davem@davemloft.net \
--cc=linux-kernel@vger.kernel.org \
--cc=sam@ravnborg.org \
--cc=sparclinux@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®