| 1 | |
| 2 | |
| 3 | * Emulated 1-byte and 2-byte cmpxchg operations for architectures lacking
|
| 4 | * direct support for these sizes. These are implemented in terms of
|
| 5 | * 4-byte cmpxchg operations.
|
| 6 | *
|
| 7 | * Copyright (C) 2024 Paul E. McKenney <[email protected]>
|
| 8 | * Copyright (C) 2026 Bradley Morgan <[email protected]>
|
| 9 |
|
| 10 |
|
| 11 | #include <linux/types.h>
|
| 12 | #include <linux/export.h>
|
| 13 | #include <linux/instrumented.h>
|
| 14 | #include <linux/atomic.h>
|
| 15 | #include <linux/panic.h>
|
| 16 | #include <linux/bug.h>
|
| 17 | #include <asm-generic/rwonce.h>
|
| 18 | #include <linux/cmpxchg-emu.h>
|
| 19 |
|
| 20 | union u8_32 {
|
| 21 | u8 b[4];
|
| 22 | u32 w;
|
| 23 | };
|
| 24 |
|
| 25 |
|
| 26 | uintptr_t cmpxchg_emu_u8(volatile u8 *p, uintptr_t old, uintptr_t new)
|
| 27 | {
|
| 28 | u32 *p32 = (u32 *)(((uintptr_t)p) & ~0x3);
|
| 29 | int i = ((uintptr_t)p) & 0x3;
|
| 30 | union u8_32 old32;
|
| 31 | union u8_32 new32;
|
| 32 | u32 ret;
|
| 33 |
|
| 34 | ret = READ_ONCE(*p32);
|
| 35 | do {
|
| 36 | old32.w = ret;
|
| 37 | if (old32.b[i] != old)
|
| 38 | return old32.b[i];
|
| 39 | new32.w = old32.w;
|
| 40 | new32.b[i] = new;
|
| 41 | instrument_atomic_read_write(p, 1);
|
| 42 | ret = data_race(cmpxchg(p32, old32.w, new32.w)); |
| 43 | } while (ret != old32.w);
|
| 44 | return old;
|
| 45 | }
|
| 46 | EXPORT_SYMBOL_GPL(cmpxchg_emu_u8);
|
| 47 |
|
| 48 | union u16_32 {
|
| 49 | u16 h[2];
|
| 50 | u32 w;
|
| 51 | };
|
| 52 |
|
| 53 |
|
| 54 | unsigned long cmpxchg_emu_u16(volatile u16 *p, unsigned long old, unsigned long new)
|
| 55 | {
|
| 56 | u32 *p32 = (u32 *)(((uintptr_t)p) & ~0x3);
|
| 57 | int i = (((uintptr_t)p) & 0x2) / 2;
|
| 58 | union u16_32 old32;
|
| 59 | union u16_32 new32;
|
| 60 | u32 ret;
|
| 61 |
|
| 62 | WARN_ON_ONCE(((uintptr_t)p) & 0x1);
|
| 63 | ret = READ_ONCE(*p32);
|
| 64 | do {
|
| 65 | old32.w = ret;
|
| 66 | if (old32.h[i] != (u16)old)
|
| 67 | return (unsigned long)old32.h[i];
|
| 68 | new32.w = old32.w;
|
| 69 | new32.h[i] = (u16)new;
|
| 70 | instrument_atomic_read_write(p, 2);
|
| 71 | ret = data_race(cmpxchg(p32, old32.w, new32.w)); |
| 72 | } while (ret != old32.w);
|
| 73 | return (u16)old;
|
| 74 | }
|
| 75 | EXPORT_SYMBOL_GPL(cmpxchg_emu_u16);
|