1*8c1e1c33SEmil Tsalapatis // SPDX-License-Identifier: GPL-2.0 2*8c1e1c33SEmil Tsalapatis /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3*8c1e1c33SEmil Tsalapatis #ifndef BPF_ATOMIC_H 4*8c1e1c33SEmil Tsalapatis #define BPF_ATOMIC_H 5*8c1e1c33SEmil Tsalapatis 6*8c1e1c33SEmil Tsalapatis #include <vmlinux.h> 7*8c1e1c33SEmil Tsalapatis #include <bpf/bpf_helpers.h> 8*8c1e1c33SEmil Tsalapatis #include <bpf_may_goto.h> 9*8c1e1c33SEmil Tsalapatis 10*8c1e1c33SEmil Tsalapatis extern bool CONFIG_X86_64 __kconfig __weak; 11*8c1e1c33SEmil Tsalapatis 12*8c1e1c33SEmil Tsalapatis /* 13*8c1e1c33SEmil Tsalapatis * __unqual_typeof(x) - Declare an unqualified scalar type, leaving 14*8c1e1c33SEmil Tsalapatis * non-scalar types unchanged, 15*8c1e1c33SEmil Tsalapatis * 16*8c1e1c33SEmil Tsalapatis * Prefer C11 _Generic for better compile-times and simpler code. Note: 'char' 17*8c1e1c33SEmil Tsalapatis * is not type-compatible with 'signed char', and we define a separate case. 18*8c1e1c33SEmil Tsalapatis * 19*8c1e1c33SEmil Tsalapatis * This is copied verbatim from kernel's include/linux/compiler_types.h, but 20*8c1e1c33SEmil Tsalapatis * with default expression (for pointers) changed from (x) to (typeof(x)0). 21*8c1e1c33SEmil Tsalapatis * 22*8c1e1c33SEmil Tsalapatis * This is because LLVM has a bug where for lvalue (x), it does not get rid of 23*8c1e1c33SEmil Tsalapatis * an extra address_space qualifier, but does in case of rvalue (typeof(x)0). 24*8c1e1c33SEmil Tsalapatis * Hence, for pointers, we need to create an rvalue expression to get the 25*8c1e1c33SEmil Tsalapatis * desired type. See https://github.com/llvm/llvm-project/issues/53400. 26*8c1e1c33SEmil Tsalapatis */ 27*8c1e1c33SEmil Tsalapatis #define __scalar_type_to_expr_cases(type) \ 28*8c1e1c33SEmil Tsalapatis unsigned type : (unsigned type)0, signed type : (signed type)0 29*8c1e1c33SEmil Tsalapatis 30*8c1e1c33SEmil Tsalapatis #define __unqual_typeof(x) \ 31*8c1e1c33SEmil Tsalapatis typeof(_Generic((x), \ 32*8c1e1c33SEmil Tsalapatis char: (char)0, \ 33*8c1e1c33SEmil Tsalapatis __scalar_type_to_expr_cases(char), \ 34*8c1e1c33SEmil Tsalapatis __scalar_type_to_expr_cases(short), \ 35*8c1e1c33SEmil Tsalapatis __scalar_type_to_expr_cases(int), \ 36*8c1e1c33SEmil Tsalapatis __scalar_type_to_expr_cases(long), \ 37*8c1e1c33SEmil Tsalapatis __scalar_type_to_expr_cases(long long), \ 38*8c1e1c33SEmil Tsalapatis default: (typeof(x))0)) 39*8c1e1c33SEmil Tsalapatis 40*8c1e1c33SEmil Tsalapatis /* No-op for BPF */ 41*8c1e1c33SEmil Tsalapatis #define cpu_relax() ({}) 42*8c1e1c33SEmil Tsalapatis 43*8c1e1c33SEmil Tsalapatis #define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) 44*8c1e1c33SEmil Tsalapatis 45*8c1e1c33SEmil Tsalapatis #ifndef WRITE_ONCE 46*8c1e1c33SEmil Tsalapatis #define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = (val)) 47*8c1e1c33SEmil Tsalapatis #endif 48*8c1e1c33SEmil Tsalapatis 49*8c1e1c33SEmil Tsalapatis #define cmpxchg(p, old, new) __sync_val_compare_and_swap((p), old, new) 50*8c1e1c33SEmil Tsalapatis 51*8c1e1c33SEmil Tsalapatis #define try_cmpxchg(p, pold, new) \ 52*8c1e1c33SEmil Tsalapatis ({ \ 53*8c1e1c33SEmil Tsalapatis __unqual_typeof(*(pold)) __o = *(pold); \ 54*8c1e1c33SEmil Tsalapatis __unqual_typeof(*(p)) __r = cmpxchg(p, __o, new); \ 55*8c1e1c33SEmil Tsalapatis if (__r != __o) \ 56*8c1e1c33SEmil Tsalapatis *(pold) = __r; \ 57*8c1e1c33SEmil Tsalapatis __r == __o; \ 58*8c1e1c33SEmil Tsalapatis }) 59*8c1e1c33SEmil Tsalapatis 60*8c1e1c33SEmil Tsalapatis #define try_cmpxchg_relaxed(p, pold, new) try_cmpxchg(p, pold, new) 61*8c1e1c33SEmil Tsalapatis 62*8c1e1c33SEmil Tsalapatis #define try_cmpxchg_acquire(p, pold, new) try_cmpxchg(p, pold, new) 63*8c1e1c33SEmil Tsalapatis 64*8c1e1c33SEmil Tsalapatis #define smp_mb() \ 65*8c1e1c33SEmil Tsalapatis ({ \ 66*8c1e1c33SEmil Tsalapatis volatile unsigned long __val; \ 67*8c1e1c33SEmil Tsalapatis __sync_fetch_and_add(&__val, 0); \ 68*8c1e1c33SEmil Tsalapatis }) 69*8c1e1c33SEmil Tsalapatis 70*8c1e1c33SEmil Tsalapatis #define smp_rmb() \ 71*8c1e1c33SEmil Tsalapatis ({ \ 72*8c1e1c33SEmil Tsalapatis if (!CONFIG_X86_64) \ 73*8c1e1c33SEmil Tsalapatis smp_mb(); \ 74*8c1e1c33SEmil Tsalapatis else \ 75*8c1e1c33SEmil Tsalapatis barrier(); \ 76*8c1e1c33SEmil Tsalapatis }) 77*8c1e1c33SEmil Tsalapatis 78*8c1e1c33SEmil Tsalapatis #define smp_wmb() \ 79*8c1e1c33SEmil Tsalapatis ({ \ 80*8c1e1c33SEmil Tsalapatis if (!CONFIG_X86_64) \ 81*8c1e1c33SEmil Tsalapatis smp_mb(); \ 82*8c1e1c33SEmil Tsalapatis else \ 83*8c1e1c33SEmil Tsalapatis barrier(); \ 84*8c1e1c33SEmil Tsalapatis }) 85*8c1e1c33SEmil Tsalapatis 86*8c1e1c33SEmil Tsalapatis /* Control dependency provides LOAD->STORE, provide LOAD->LOAD */ 87*8c1e1c33SEmil Tsalapatis #define smp_acquire__after_ctrl_dep() ({ smp_rmb(); }) 88*8c1e1c33SEmil Tsalapatis 89*8c1e1c33SEmil Tsalapatis #define smp_load_acquire(p) \ 90*8c1e1c33SEmil Tsalapatis ({ \ 91*8c1e1c33SEmil Tsalapatis __unqual_typeof(*(p)) __v = READ_ONCE(*(p)); \ 92*8c1e1c33SEmil Tsalapatis if (!CONFIG_X86_64) \ 93*8c1e1c33SEmil Tsalapatis smp_mb(); \ 94*8c1e1c33SEmil Tsalapatis barrier(); \ 95*8c1e1c33SEmil Tsalapatis __v; \ 96*8c1e1c33SEmil Tsalapatis }) 97*8c1e1c33SEmil Tsalapatis 98*8c1e1c33SEmil Tsalapatis #define smp_store_release(p, val) \ 99*8c1e1c33SEmil Tsalapatis ({ \ 100*8c1e1c33SEmil Tsalapatis if (!CONFIG_X86_64) \ 101*8c1e1c33SEmil Tsalapatis smp_mb(); \ 102*8c1e1c33SEmil Tsalapatis barrier(); \ 103*8c1e1c33SEmil Tsalapatis WRITE_ONCE(*(p), val); \ 104*8c1e1c33SEmil Tsalapatis }) 105*8c1e1c33SEmil Tsalapatis 106*8c1e1c33SEmil Tsalapatis #define smp_cond_load_relaxed_label(p, cond_expr, label) \ 107*8c1e1c33SEmil Tsalapatis ({ \ 108*8c1e1c33SEmil Tsalapatis typeof(p) __ptr = (p); \ 109*8c1e1c33SEmil Tsalapatis __unqual_typeof(*(p)) VAL; \ 110*8c1e1c33SEmil Tsalapatis for (;;) { \ 111*8c1e1c33SEmil Tsalapatis VAL = (__unqual_typeof(*(p)))READ_ONCE(*__ptr); \ 112*8c1e1c33SEmil Tsalapatis if (cond_expr) \ 113*8c1e1c33SEmil Tsalapatis break; \ 114*8c1e1c33SEmil Tsalapatis cond_break_label(label); \ 115*8c1e1c33SEmil Tsalapatis cpu_relax(); \ 116*8c1e1c33SEmil Tsalapatis } \ 117*8c1e1c33SEmil Tsalapatis (typeof(*(p)))VAL; \ 118*8c1e1c33SEmil Tsalapatis }) 119*8c1e1c33SEmil Tsalapatis 120*8c1e1c33SEmil Tsalapatis #define smp_cond_load_acquire_label(p, cond_expr, label) \ 121*8c1e1c33SEmil Tsalapatis ({ \ 122*8c1e1c33SEmil Tsalapatis __unqual_typeof(*p) __val = \ 123*8c1e1c33SEmil Tsalapatis smp_cond_load_relaxed_label(p, cond_expr, label); \ 124*8c1e1c33SEmil Tsalapatis smp_acquire__after_ctrl_dep(); \ 125*8c1e1c33SEmil Tsalapatis (typeof(*(p)))__val; \ 126*8c1e1c33SEmil Tsalapatis }) 127*8c1e1c33SEmil Tsalapatis 128*8c1e1c33SEmil Tsalapatis #define atomic_read(p) READ_ONCE((p)->counter) 129*8c1e1c33SEmil Tsalapatis 130*8c1e1c33SEmil Tsalapatis #define atomic_cond_read_relaxed_label(p, cond_expr, label) \ 131*8c1e1c33SEmil Tsalapatis smp_cond_load_relaxed_label(&(p)->counter, cond_expr, label) 132*8c1e1c33SEmil Tsalapatis 133*8c1e1c33SEmil Tsalapatis #define atomic_cond_read_acquire_label(p, cond_expr, label) \ 134*8c1e1c33SEmil Tsalapatis smp_cond_load_acquire_label(&(p)->counter, cond_expr, label) 135*8c1e1c33SEmil Tsalapatis 136*8c1e1c33SEmil Tsalapatis #define atomic_try_cmpxchg_relaxed(p, pold, new) \ 137*8c1e1c33SEmil Tsalapatis try_cmpxchg_relaxed(&(p)->counter, pold, new) 138*8c1e1c33SEmil Tsalapatis 139*8c1e1c33SEmil Tsalapatis #define atomic_try_cmpxchg_acquire(p, pold, new) \ 140*8c1e1c33SEmil Tsalapatis try_cmpxchg_acquire(&(p)->counter, pold, new) 141*8c1e1c33SEmil Tsalapatis 142*8c1e1c33SEmil Tsalapatis #endif /* BPF_ATOMIC_H */ 143