All of lore.kernel.org
 help / color / mirror / Atom feed
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 5/5] sparc32: document the compare-and-swap trap ABI
Date: Wed, 23 Sep 2026 22:17:21 +0200	[thread overview]
Message-ID: <20260923201830.865553-6-linmag7@gmail.com> (raw)
In-Reply-To: <20260923201830.865553-1-linmag7@gmail.com>

The trap is userspace ABI rather than an internal kernel facility, so
write down what it promises: the instruction and trap type, the register
convention, the failure cases, what the AT_HWCAP bit means, and the limits
a caller has to work within.

Several of those limits are easy to miss. Replacing the __atomic_* helper
symbols does not by itself route every atomic write through the trap,
because the compiler can expand some of them inline and covering libc's
own uses is a different scope from covering application code built with
atomic builtins. The service is one aligned 32-bit word, which is not the
same as general byte and halfword atomics. The guarantee is indivisibility
of that one word and no ordering beyond it. And futex_robust_unlock()
currently writes the futex word without taking the lock the trap uses, so
that one kernel path is outside the guarantee.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 Documentation/arch/sparc/cas-trap.rst | 163 ++++++++++++++++++++++++++
 Documentation/arch/sparc/index.rst    |   1 +
 2 files changed, 164 insertions(+)
 create mode 100644 Documentation/arch/sparc/cas-trap.rst

diff --git a/Documentation/arch/sparc/cas-trap.rst b/Documentation/arch/sparc/cas-trap.rst
new file mode 100644
index 000000000000..f09d81e8f1ed
--- /dev/null
+++ b/Documentation/arch/sparc/cas-trap.rst
@@ -0,0 +1,163 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================================
+The sparc32 compare-and-swap trap
+=================================
+
+Pre-v9 sparc has no compare-and-swap instruction.  A lock held inside one
+process cannot make a word atomic against another process mapping the same
+page, and a lock kept inside the word itself costs value bits, so the kernel
+performs the operation instead.  parisc provides the same service for the same
+reason.
+
+Interface
+=========
+
+``ta 0x11`` - software trap 0x11, trap type 0x91, ``SP_TRAP_CAS`` in
+``asm/traps.h`` - asks the kernel to compare and swap one 32-bit word:
+
+=======  =========================================
+``%o0``  word aligned user address
+``%o1``  expected value
+``%o2``  new value
+=======  =========================================
+
+On return the carry bit is clear and ``%o0`` holds the value that was found at
+the address.  The caller compares it against what it expected to learn whether
+the swap happened; there is no separate success flag.
+
+Registers
+---------
+
+``%o0`` is read and written.  ``%o1`` and ``%o2`` are read and are not
+modified.  Every other windowed and global register is preserved across the
+trap.  The integer condition codes are written - that is how success and
+failure are reported - and must be treated as clobbered.
+
+An inline-assembly wrapper therefore wants ``%o0`` as an in/out operand,
+``%o1`` and ``%o2`` as inputs, and both ``"cc"`` and ``"memory"`` in the
+clobber list, the latter so the compiler does not move accesses across the
+operation.
+
+On failure the carry bit is set and ``%o0`` holds an error number:
+
+==========  =========================================
+``EINVAL``  the address is not word aligned
+``EFAULT``  the memory access could not be completed
+==========  =========================================
+
+A successful return does not establish that the mapping is writable.  On the
+software path the kernel writes only when the comparison succeeds, so a
+mismatching value on a read-only mapping can be returned without the kernel
+ever discovering that the mapping could not be written.  The two
+implementations are not required to agree about an invalid operand: pass an
+aligned word in memory that is both readable and writable.
+
+The operation is atomic against another process issuing the same trap, against
+the futex operations in ``asm/futex_32.h``, and - on a cpu that implements
+``casa`` - against a binary using that instruction directly.
+
+The service guarantees an indivisible compare-and-swap on the addressed word
+under the participation rules below.  It does not provide any additional
+acquire, release or full-barrier guarantee for other memory locations.
+Userspace must supply whatever ordering the operation it is implementing
+requires.
+
+One kernel path does not participate.  ``futex_robust_unlock()`` clears the
+futex word through ``unsafe_atomic_store_release_user()``, for which sparc32
+uses the generic definition - ultimately a plain user store.  On a no-casa SMP
+system that store does not acquire the lock this trap uses, so it can land
+between the trap's load and its conditional store, and the compare-and-swap
+then overwrites the unlock while still reporting success.
+
+On no-casa SMP systems the non-PI robust-unlock operations
+``FUTEX_UNLOCK_WAKE_LIST32`` and ``FUTEX_UNLOCK_BITSET_LIST32``, including
+their private variants, therefore do not serialise with the software-CAS lock
+domain.  Their store can race with a trap-based compare-and-swap, and equally
+with the lock-backed operations in ``asm/futex_32.h``, which take the lock but
+still load and store separately.
+
+The integration point for this store remains an open question for review.
+
+What userspace has to do as well
+================================
+
+On a cpu without ``casa`` the kernel does the swap under a lock of its own, and
+**an ordinary store does not take that lock**::
+
+	trap                        another thread's plain store
+
+	lock
+	read word -> A
+	                            store B -> word
+	write C -> word
+	unlock
+
+``B`` is lost, and no ordering of an atomic compare-and-swap and an atomic
+store makes that a legal outcome.
+
+So userspace using this service to build atomic operations must build *every*
+atomic write from it, not just compare-and-exchange: an atomic store and an
+atomic exchange each become a compare-and-swap retry loop.  An implementation
+that routes read-modify-writes through the trap but leaves atomic stores as
+plain stores is not atomic, and the failure is silent.
+
+This does not apply to a cpu that has ``casa``, where the kernel uses the
+instruction and userspace can too.
+
+Replacing the ``__atomic_*`` helpers is not sufficient
+-----------------------------------------------------
+
+Providing replacement ``__atomic_store_4`` and ``__atomic_exchange_4`` symbols
+does not by itself route every atomic write through the trap.  gcc can expand
+an atomic store to a plain store, a 32-bit atomic exchange to ``swap``, and an
+atomic test-and-set to ``ldstub`` on v8.  ``swap`` and ``ldstub`` are
+themselves atomic, but they do not take the kernel's lock and so do not exclude
+against its separate read and write.  A call the compiler never emits cannot be
+intercepted.
+
+Two different integration scopes follow from that, and they are not
+interchangeable.  Changing libc's internal atomic macros covers libc's own
+uses.  It does not change application code compiled with atomic builtins; that
+needs compiler support - the possibility Andreas Larsson raised in 2019 - or a
+build that avoids the inline expansions.
+
+One aligned word is not general byte and halfword atomics
+--------------------------------------------------------
+
+The service operates on one naturally aligned 32-bit word.  A byte or halfword
+atomic synthesised by read-modify-writing its containing word is not safe
+merely because every atomic on the target uses the trap::
+
+	word:  [ atomic byte A | ordinary byte B | ... ]
+
+	cpu 0                             cpu 1
+
+	read the whole word under the lock
+	                                  plain store to B
+	write the whole word back,
+	  including the old B
+
+The ordinary writer of the neighbouring object ``B`` has no reason to
+participate, and its store is lost.  Byte and halfword atomics built this way
+need either width-aware accesses sharing this lock domain, or storage rules
+that give the atomic object its containing word to itself.
+
+Availability
+============
+
+``AT_HWCAP`` carries ``HWCAP_SPARC_CASTRAP`` (0x10000000) when the kernel
+implements the trap.  Userspace must consult it rather than probing, because
+there is no safe way to probe:
+
+- on a kernel without this support the trap is fatal;
+- on sparc64 a 32-bit process reads its hwcap word from ``asm/elf_64.h``, where
+  the bit is reserved and always clear, and ``ta 0x11`` there is not an illegal
+  instruction but the old 64-bit system call trap.
+
+The bit describes the kernel service, not the hardware.  It is set whether or
+not the cpu has ``casa``, because a binary built for plain v8 cannot use that
+instruction even on a cpu that has it.  A binary that does have ``casa``
+available - one built for LEON3 - should use it inline and ignore the bit
+entirely; the kernel uses the instruction too where it exists, so the two agree
+on the same word.
diff --git a/Documentation/arch/sparc/index.rst b/Documentation/arch/sparc/index.rst
index ae884224eec2..3a7d6df91a84 100644
--- a/Documentation/arch/sparc/index.rst
+++ b/Documentation/arch/sparc/index.rst
@@ -7,6 +7,7 @@ Sparc Architecture
 
    console
    adi
+   cas-trap
 
    oradax/oracle-dax
 
-- 
2.43.0


      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 ` [RFC PATCH 3/5] sparc32: implement futex atomic ops with the compare-and-swap locks Magnus Lindholm
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 ` Magnus Lindholm [this message]

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-6-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.