// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ #ifndef BPF_ATOMIC_H #define BPF_ATOMIC_H #include #include #include "bpf_experimental.h" extern bool CONFIG_X86_64 __kconfig __weak; /* * __unqual_typeof(x) - Declare an unqualified scalar type, leaving * non-scalar types unchanged, * * Prefer C11 _Generic for better compile-times and simpler code. Note: 'char' * is not type-compatible with 'signed char', and we define a separate case. * * This is copied verbatim from kernel's include/linux/compiler_types.h, but * with default expression (for pointers) changed from (x) to (typeof(x)0). * * This is because LLVM has a bug where for lvalue (x), it does not get rid of * an extra address_space qualifier, but does in case of rvalue (typeof(x)0). * Hence, for pointers, we need to create an rvalue expression to get the * desired type. See https://github.com/llvm/llvm-project/issues/53400. */ #define __scalar_type_to_expr_cases(type) \ unsigned type : (unsigned type)0, signed type : (signed type)0 #define __unqual_typeof(x) \ typeof(_Generic((x), \ char: (char)0, \ __scalar_type_to_expr_cases(char), \ __scalar_type_to_expr_cases(short), \ __scalar_type_to_expr_cases(int), \ __scalar_type_to_expr_cases(long), \ __scalar_type_to_expr_cases(long long), \ default: (typeof(x))0)) /* No-op for BPF */ #define cpu_relax() ({}) #define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) #define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = (val)) #define cmpxchg(p, old, new) __sync_val_compare_and_swap((p), old, new) #define try_cmpxchg(p, pold, new) \ ({ \ __unqual_typeof(*(pold)) __o = *(pold); \ __unqual_typeof(*(p)) __r = cmpxchg(p, __o, new); \ if (__r != __o) \ *(pold) = __r; \ __r == __o; \ }) #define try_cmpxchg_relaxed(p, pold, new) try_cmpxchg(p, pold, new) #define try_cmpxchg_acquire(p, pold, new) try_cmpxchg(p, pold, new) #define smp_mb() \ ({ \ unsigned long __val; \ __sync_fetch_and_add(&__val, 0); \ }) #define smp_rmb() \ ({ \ if (!CONFIG_X86_64) \ smp_mb(); \ else \ barrier(); \ }) #define smp_wmb() \ ({ \ if (!CONFIG_X86_64) \ smp_mb(); \ else \ barrier(); \ }) /* Control dependency provides LOAD->STORE, provide LOAD->LOAD */ #define smp_acquire__after_ctrl_dep() ({ smp_rmb(); }) #define smp_load_acquire(p) \ ({ \ __unqual_typeof(*(p)) __v = READ_ONCE(*(p)); \ if (!CONFIG_X86_64) \ smp_mb(); \ barrier(); \ __v; \ }) #define smp_store_release(p, val) \ ({ \ if (!CONFIG_X86_64) \ smp_mb(); \ barrier(); \ WRITE_ONCE(*(p), val); \ }) #define smp_cond_load_relaxed_label(p, cond_expr, label) \ ({ \ typeof(p) __ptr = (p); \ __unqual_typeof(*(p)) VAL; \ for (;;) { \ VAL = (__unqual_typeof(*(p)))READ_ONCE(*__ptr); \ if (cond_expr) \ break; \ cond_break_label(label); \ cpu_relax(); \ } \ (typeof(*(p)))VAL; \ }) #define smp_cond_load_acquire_label(p, cond_expr, label) \ ({ \ __unqual_typeof(*p) __val = \ smp_cond_load_relaxed_label(p, cond_expr, label); \ smp_acquire__after_ctrl_dep(); \ (typeof(*(p)))__val; \ }) #define atomic_read(p) READ_ONCE((p)->counter) #define atomic_cond_read_relaxed_label(p, cond_expr, label) \ smp_cond_load_relaxed_label(&(p)->counter, cond_expr, label) #define atomic_cond_read_acquire_label(p, cond_expr, label) \ smp_cond_load_acquire_label(&(p)->counter, cond_expr, label) #define atomic_try_cmpxchg_relaxed(p, pold, new) \ try_cmpxchg_relaxed(&(p)->counter, pold, new) #define atomic_try_cmpxchg_acquire(p, pold, new) \ try_cmpxchg_acquire(&(p)->counter, pold, new) #endif /* BPF_ATOMIC_H */