mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

As BPF doesn't include any barrier instructions, smp_mb() is implemented
by doing a dummy value returning atomic operation. Such an operation
acts a full barrier as enforced by LKMM and also by the work in progress
BPF memory model.
If the returned value is not used, clang[1] can optimize the value
returning atomic instruction in to a normal atomic instruction which
provides no ordering guarantees.
Mark the variable as volatile so the above optimization is never
performed and smp_mb() works as expected.
[1] https://godbolt.org/z/qzze7bG6z
Fixes: 88d706ba7c
("selftests/bpf: Introduce arena spin lock")
Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
Link: https://lore.kernel.org/r/20250710175434.18829-2-puranjay@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
140 lines
4.9 KiB
C++
140 lines
4.9 KiB
C++
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
|
#ifndef BPF_ATOMIC_H
|
|
#define BPF_ATOMIC_H
|
|
|
|
#include <vmlinux.h>
|
|
#include <bpf/bpf_helpers.h>
|
|
#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() \
|
|
({ \
|
|
volatile 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 */
|