Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lib: Add one-byte emulation function

Architectures are required to provide four-byte cmpxchg() and 64-bit
architectures are additionally required to provide eight-byte cmpxchg().
However, there are cases where one-byte cmpxchg() would be extremely
useful. Therefore, provide cmpxchg_emu_u8() that emulates one-byte
cmpxchg() in terms of four-byte cmpxchg().

Note that this emulations is fully ordered, and can (for example) cause
one-byte cmpxchg_relaxed() to incur the overhead of full ordering.
If this causes problems for a given architecture, that architecture is
free to provide its own lighter-weight primitives.

[ paulmck: Apply Marco Elver feedback. ]
[ paulmck: Apply kernel test robot feedback. ]
[ paulmck: Drop two-byte support per Arnd Bergmann feedback. ]

Link: https://lore.kernel.org/all/0733eb10-5e7a-4450-9b8a-527b97c842ff@paulmck-laptop/

Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
Acked-by: Marco Elver <elver@google.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "Peter Zijlstra (Intel)" <peterz@infradead.org>
Cc: Douglas Anderson <dianders@chromium.org>
Cc: Petr Mladek <pmladek@suse.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: <linux-arch@vger.kernel.org>

+64
+3
arch/Kconfig
··· 1609 1609 # strict alignment always, even with -falign-functions. 1610 1610 def_bool CC_HAS_MIN_FUNCTION_ALIGNMENT || CC_IS_CLANG 1611 1611 1612 + config ARCH_NEED_CMPXCHG_1_EMU 1613 + bool 1614 + 1612 1615 endmenu
+15
include/linux/cmpxchg-emu.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * Emulated 1-byte and 2-byte cmpxchg operations for architectures 4 + * lacking direct support for these sizes. These are implemented in terms 5 + * of 4-byte cmpxchg operations. 6 + * 7 + * Copyright (C) 2024 Paul E. McKenney. 8 + */ 9 + 10 + #ifndef __LINUX_CMPXCHG_EMU_H 11 + #define __LINUX_CMPXCHG_EMU_H 12 + 13 + uintptr_t cmpxchg_emu_u8(volatile u8 *p, uintptr_t old, uintptr_t new); 14 + 15 + #endif /* __LINUX_CMPXCHG_EMU_H */
+1
lib/Makefile
··· 236 236 lib-$(CONFIG_GENERIC_BUG) += bug.o 237 237 238 238 obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o 239 + obj-$(CONFIG_ARCH_NEED_CMPXCHG_1_EMU) += cmpxchg-emu.o 239 240 240 241 obj-$(CONFIG_DYNAMIC_DEBUG_CORE) += dynamic_debug.o 241 242 #ensure exported functions have prototypes
+45
lib/cmpxchg-emu.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Emulated 1-byte cmpxchg operation for architectures lacking direct 4 + * support for this size. This is implemented in terms of 4-byte cmpxchg 5 + * operations. 6 + * 7 + * Copyright (C) 2024 Paul E. McKenney. 8 + */ 9 + 10 + #include <linux/types.h> 11 + #include <linux/export.h> 12 + #include <linux/instrumented.h> 13 + #include <linux/atomic.h> 14 + #include <linux/panic.h> 15 + #include <linux/bug.h> 16 + #include <asm-generic/rwonce.h> 17 + #include <linux/cmpxchg-emu.h> 18 + 19 + union u8_32 { 20 + u8 b[4]; 21 + u32 w; 22 + }; 23 + 24 + /* Emulate one-byte cmpxchg() in terms of 4-byte cmpxchg. */ 25 + uintptr_t cmpxchg_emu_u8(volatile u8 *p, uintptr_t old, uintptr_t new) 26 + { 27 + u32 *p32 = (u32 *)(((uintptr_t)p) & ~0x3); 28 + int i = ((uintptr_t)p) & 0x3; 29 + union u8_32 old32; 30 + union u8_32 new32; 31 + u32 ret; 32 + 33 + ret = READ_ONCE(*p32); 34 + do { 35 + old32.w = ret; 36 + if (old32.b[i] != old) 37 + return old32.b[i]; 38 + new32.w = old32.w; 39 + new32.b[i] = new; 40 + instrument_atomic_read_write(p, 1); 41 + ret = data_race(cmpxchg(p32, old32.w, new32.w)); // Overridden above. 42 + } while (ret != old32.w); 43 + return old; 44 + } 45 + EXPORT_SYMBOL_GPL(cmpxchg_emu_u8);