mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
![Mark Rutland](/assets/img/avatar_default.png)
Currently the atomics are documented in Documentation/atomic_t.txt, and have no kerneldoc comments. There are a sufficient number of gotchas (e.g. semantics, noinstr-safety) that it would be nice to have comments to call these out, and it would be nice to have kerneldoc comments such that these can be collated. While it's possible to derive the semantics from the code, this can be painful given the amount of indirection we currently have (e.g. fallback paths), and it's easy to be mislead by naming, e.g. * The unconditional void-returning ops *only* have relaxed variants without a _relaxed suffix, and can easily be mistaken for being fully ordered. It would be nice to give these a _relaxed() suffix, but this would result in significant churn throughout the kernel. * Our naming of conditional and unconditional+test ops is rather inconsistent, and it can be difficult to derive the name of an operation, or to identify where an op is conditional or unconditional+test. Some ops are clearly conditional: - dec_if_positive - add_unless - dec_unless_positive - inc_unless_negative Some ops are clearly unconditional+test: - sub_and_test - dec_and_test - inc_and_test However, what exactly those test is not obvious. A _test_zero suffix might be clearer. Others could be read ambiguously: - inc_not_zero // conditional - add_negative // unconditional+test It would probably be worth renaming these, e.g. to inc_unless_zero and add_test_negative. As a step towards making this more consistent and easier to understand, this patch adds kerneldoc comments for all generated *atomic*_*() functions. These are generated from templates, with some common text shared, making it easy to extend these in future if necessary. I've tried to make these as consistent and clear as possible, and I've deliberately ensured: * All ops have their ordering explicitly mentioned in the short and long description. * All test ops have "test" in their short description. * All ops are described as an expression using their usual C operator. For example: andnot: "Atomically updates @v to (@v & ~@i)" inc: "Atomically updates @v to (@v + 1)" Which may be clearer to non-naative English speakers, and allows all the operations to be described in the same style. * All conditional ops have their condition described as an expression using the usual C operators. For example: add_unless: "If (@v != @u), atomically updates @v to (@v + @i)" cmpxchg: "If (@v == @old), atomically updates @v to @new" Which may be clearer to non-naative English speakers, and allows all the operations to be described in the same style. * All bitwise ops (and,andnot,or,xor) explicitly mention that they are bitwise in their short description, so that they are not mistaken for performing their logical equivalents. * The noinstr safety of each op is explicitly described, with a description of whether or not to use the raw_ form of the op. There should be no functional change as a result of this patch. Reported-by: Paul E. McKenney <paulmck@kernel.org> Signed-off-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: Kees Cook <keescook@chromium.org> Link: https://lore.kernel.org/r/20230605070124.3741859-26-mark.rutland@arm.com
333 lines
8.3 KiB
Bash
Executable file
333 lines
8.3 KiB
Bash
Executable file
#!/bin/sh
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
|
|
ATOMICDIR=$(dirname $0)
|
|
|
|
. ${ATOMICDIR}/atomic-tbl.sh
|
|
|
|
#gen_template_fallback(template, meta, pfx, name, sfx, order, atomic, int, args...)
|
|
gen_template_fallback()
|
|
{
|
|
local template="$1"; shift
|
|
local meta="$1"; shift
|
|
local pfx="$1"; shift
|
|
local name="$1"; shift
|
|
local sfx="$1"; shift
|
|
local order="$1"; shift
|
|
local atomic="$1"; shift
|
|
local int="$1"; shift
|
|
|
|
local ret="$(gen_ret_type "${meta}" "${int}")"
|
|
local retstmt="$(gen_ret_stmt "${meta}")"
|
|
local params="$(gen_params "${int}" "${atomic}" "$@")"
|
|
local args="$(gen_args "$@")"
|
|
|
|
. ${template}
|
|
}
|
|
|
|
#gen_order_fallback(meta, pfx, name, sfx, order, atomic, int, args...)
|
|
gen_order_fallback()
|
|
{
|
|
local meta="$1"; shift
|
|
local pfx="$1"; shift
|
|
local name="$1"; shift
|
|
local sfx="$1"; shift
|
|
local order="$1"; shift
|
|
|
|
local tmpl_order=${order#_}
|
|
local tmpl="${ATOMICDIR}/fallbacks/${tmpl_order:-fence}"
|
|
gen_template_fallback "${tmpl}" "${meta}" "${pfx}" "${name}" "${sfx}" "${order}" "$@"
|
|
}
|
|
|
|
#gen_proto_fallback(meta, pfx, name, sfx, order, atomic, int, args...)
|
|
gen_proto_fallback()
|
|
{
|
|
local meta="$1"; shift
|
|
local pfx="$1"; shift
|
|
local name="$1"; shift
|
|
local sfx="$1"; shift
|
|
local order="$1"; shift
|
|
|
|
local tmpl="$(find_fallback_template "${pfx}" "${name}" "${sfx}" "${order}")"
|
|
gen_template_fallback "${tmpl}" "${meta}" "${pfx}" "${name}" "${sfx}" "${order}" "$@"
|
|
}
|
|
|
|
#gen_proto_order_variant(meta, pfx, name, sfx, order, atomic, int, args...)
|
|
gen_proto_order_variant()
|
|
{
|
|
local meta="$1"; shift
|
|
local pfx="$1"; shift
|
|
local name="$1"; shift
|
|
local sfx="$1"; shift
|
|
local order="$1"; shift
|
|
local atomic="$1"; shift
|
|
local int="$1"; shift
|
|
|
|
local atomicname="${atomic}_${pfx}${name}${sfx}${order}"
|
|
local basename="${atomic}_${pfx}${name}${sfx}"
|
|
|
|
local template="$(find_fallback_template "${pfx}" "${name}" "${sfx}" "${order}")"
|
|
|
|
local ret="$(gen_ret_type "${meta}" "${int}")"
|
|
local retstmt="$(gen_ret_stmt "${meta}")"
|
|
local params="$(gen_params "${int}" "${atomic}" "$@")"
|
|
local args="$(gen_args "$@")"
|
|
|
|
gen_kerneldoc "raw_" "${meta}" "${pfx}" "${name}" "${sfx}" "${order}" "${atomic}" "${int}" "$@"
|
|
|
|
printf "static __always_inline ${ret}\n"
|
|
printf "raw_${atomicname}(${params})\n"
|
|
printf "{\n"
|
|
|
|
# Where there is no possible fallback, this order variant is mandatory
|
|
# and must be provided by arch code. Add a comment to the header to
|
|
# make this obvious.
|
|
#
|
|
# Ideally we'd error on a missing definition, but arch code might
|
|
# define this order variant as a C function without a preprocessor
|
|
# symbol.
|
|
if [ -z ${template} ] && [ -z "${order}" ] && ! meta_has_relaxed "${meta}"; then
|
|
printf "\t${retstmt}arch_${atomicname}(${args});\n"
|
|
printf "}\n\n"
|
|
return
|
|
fi
|
|
|
|
printf "#if defined(arch_${atomicname})\n"
|
|
printf "\t${retstmt}arch_${atomicname}(${args});\n"
|
|
|
|
# Allow FULL/ACQUIRE/RELEASE ops to be defined in terms of RELAXED ops
|
|
if [ "${order}" != "_relaxed" ] && meta_has_relaxed "${meta}"; then
|
|
printf "#elif defined(arch_${basename}_relaxed)\n"
|
|
gen_order_fallback "${meta}" "${pfx}" "${name}" "${sfx}" "${order}" "${atomic}" "${int}" "$@"
|
|
fi
|
|
|
|
# Allow ACQUIRE/RELEASE/RELAXED ops to be defined in terms of FULL ops
|
|
if [ ! -z "${order}" ]; then
|
|
printf "#elif defined(arch_${basename})\n"
|
|
printf "\t${retstmt}arch_${basename}(${args});\n"
|
|
fi
|
|
|
|
printf "#else\n"
|
|
if [ ! -z "${template}" ]; then
|
|
gen_proto_fallback "${meta}" "${pfx}" "${name}" "${sfx}" "${order}" "${atomic}" "${int}" "$@"
|
|
else
|
|
printf "#error \"Unable to define raw_${atomicname}\"\n"
|
|
fi
|
|
|
|
printf "#endif\n"
|
|
printf "}\n\n"
|
|
}
|
|
|
|
|
|
#gen_proto_order_variants(meta, pfx, name, sfx, atomic, int, args...)
|
|
gen_proto_order_variants()
|
|
{
|
|
local meta="$1"; shift
|
|
local pfx="$1"; shift
|
|
local name="$1"; shift
|
|
local sfx="$1"; shift
|
|
local atomic="$1"
|
|
|
|
gen_proto_order_variant "${meta}" "${pfx}" "${name}" "${sfx}" "" "$@"
|
|
|
|
if meta_has_acquire "${meta}"; then
|
|
gen_proto_order_variant "${meta}" "${pfx}" "${name}" "${sfx}" "_acquire" "$@"
|
|
fi
|
|
|
|
if meta_has_release "${meta}"; then
|
|
gen_proto_order_variant "${meta}" "${pfx}" "${name}" "${sfx}" "_release" "$@"
|
|
fi
|
|
|
|
if meta_has_relaxed "${meta}"; then
|
|
gen_proto_order_variant "${meta}" "${pfx}" "${name}" "${sfx}" "_relaxed" "$@"
|
|
fi
|
|
}
|
|
|
|
#gen_basic_fallbacks(basename)
|
|
gen_basic_fallbacks()
|
|
{
|
|
local basename="$1"; shift
|
|
cat << EOF
|
|
#define raw_${basename}_acquire arch_${basename}
|
|
#define raw_${basename}_release arch_${basename}
|
|
#define raw_${basename}_relaxed arch_${basename}
|
|
EOF
|
|
}
|
|
|
|
gen_order_fallbacks()
|
|
{
|
|
local xchg="$1"; shift
|
|
|
|
cat <<EOF
|
|
|
|
#define raw_${xchg}_relaxed arch_${xchg}_relaxed
|
|
|
|
#ifdef arch_${xchg}_acquire
|
|
#define raw_${xchg}_acquire arch_${xchg}_acquire
|
|
#else
|
|
#define raw_${xchg}_acquire(...) \\
|
|
__atomic_op_acquire(arch_${xchg}, __VA_ARGS__)
|
|
#endif
|
|
|
|
#ifdef arch_${xchg}_release
|
|
#define raw_${xchg}_release arch_${xchg}_release
|
|
#else
|
|
#define raw_${xchg}_release(...) \\
|
|
__atomic_op_release(arch_${xchg}, __VA_ARGS__)
|
|
#endif
|
|
|
|
#ifdef arch_${xchg}
|
|
#define raw_${xchg} arch_${xchg}
|
|
#else
|
|
#define raw_${xchg}(...) \\
|
|
__atomic_op_fence(arch_${xchg}, __VA_ARGS__)
|
|
#endif
|
|
|
|
EOF
|
|
}
|
|
|
|
gen_xchg_order_fallback()
|
|
{
|
|
local xchg="$1"; shift
|
|
local order="$1"; shift
|
|
local forder="${order:-_fence}"
|
|
|
|
printf "#if defined(arch_${xchg}${order})\n"
|
|
printf "#define raw_${xchg}${order} arch_${xchg}${order}\n"
|
|
|
|
if [ "${order}" != "_relaxed" ]; then
|
|
printf "#elif defined(arch_${xchg}_relaxed)\n"
|
|
printf "#define raw_${xchg}${order}(...) \\\\\n"
|
|
printf " __atomic_op${forder}(arch_${xchg}, __VA_ARGS__)\n"
|
|
fi
|
|
|
|
if [ ! -z "${order}" ]; then
|
|
printf "#elif defined(arch_${xchg})\n"
|
|
printf "#define raw_${xchg}${order} arch_${xchg}\n"
|
|
fi
|
|
|
|
printf "#else\n"
|
|
printf "extern void raw_${xchg}${order}_not_implemented(void);\n"
|
|
printf "#define raw_${xchg}${order}(...) raw_${xchg}${order}_not_implemented()\n"
|
|
printf "#endif\n\n"
|
|
}
|
|
|
|
gen_xchg_fallbacks()
|
|
{
|
|
local xchg="$1"; shift
|
|
|
|
for order in "" "_acquire" "_release" "_relaxed"; do
|
|
gen_xchg_order_fallback "${xchg}" "${order}"
|
|
done
|
|
}
|
|
|
|
gen_try_cmpxchg_fallback()
|
|
{
|
|
local cmpxchg="$1"; shift;
|
|
local order="$1"; shift;
|
|
|
|
cat <<EOF
|
|
#define raw_try_${cmpxchg}${order}(_ptr, _oldp, _new) \\
|
|
({ \\
|
|
typeof(*(_ptr)) *___op = (_oldp), ___o = *___op, ___r; \\
|
|
___r = raw_${cmpxchg}${order}((_ptr), ___o, (_new)); \\
|
|
if (unlikely(___r != ___o)) \\
|
|
*___op = ___r; \\
|
|
likely(___r == ___o); \\
|
|
})
|
|
EOF
|
|
}
|
|
|
|
gen_try_cmpxchg_order_fallback()
|
|
{
|
|
local cmpxchg="$1"; shift
|
|
local order="$1"; shift
|
|
local forder="${order:-_fence}"
|
|
|
|
printf "#if defined(arch_try_${cmpxchg}${order})\n"
|
|
printf "#define raw_try_${cmpxchg}${order} arch_try_${cmpxchg}${order}\n"
|
|
|
|
if [ "${order}" != "_relaxed" ]; then
|
|
printf "#elif defined(arch_try_${cmpxchg}_relaxed)\n"
|
|
printf "#define raw_try_${cmpxchg}${order}(...) \\\\\n"
|
|
printf " __atomic_op${forder}(arch_try_${cmpxchg}, __VA_ARGS__)\n"
|
|
fi
|
|
|
|
if [ ! -z "${order}" ]; then
|
|
printf "#elif defined(arch_try_${cmpxchg})\n"
|
|
printf "#define raw_try_${cmpxchg}${order} arch_try_${cmpxchg}\n"
|
|
fi
|
|
|
|
printf "#else\n"
|
|
gen_try_cmpxchg_fallback "${cmpxchg}" "${order}"
|
|
printf "#endif\n\n"
|
|
}
|
|
|
|
gen_try_cmpxchg_fallbacks()
|
|
{
|
|
local cmpxchg="$1"; shift;
|
|
|
|
for order in "" "_acquire" "_release" "_relaxed"; do
|
|
gen_try_cmpxchg_order_fallback "${cmpxchg}" "${order}"
|
|
done
|
|
}
|
|
|
|
gen_cmpxchg_local_fallbacks()
|
|
{
|
|
local cmpxchg="$1"; shift
|
|
|
|
printf "#define raw_${cmpxchg} arch_${cmpxchg}\n\n"
|
|
printf "#ifdef arch_try_${cmpxchg}\n"
|
|
printf "#define raw_try_${cmpxchg} arch_try_${cmpxchg}\n"
|
|
printf "#else\n"
|
|
gen_try_cmpxchg_fallback "${cmpxchg}" ""
|
|
printf "#endif\n\n"
|
|
}
|
|
|
|
cat << EOF
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
// Generated by $0
|
|
// DO NOT MODIFY THIS FILE DIRECTLY
|
|
|
|
#ifndef _LINUX_ATOMIC_FALLBACK_H
|
|
#define _LINUX_ATOMIC_FALLBACK_H
|
|
|
|
#include <linux/compiler.h>
|
|
|
|
EOF
|
|
|
|
for xchg in "xchg" "cmpxchg" "cmpxchg64" "cmpxchg128"; do
|
|
gen_xchg_fallbacks "${xchg}"
|
|
done
|
|
|
|
for cmpxchg in "cmpxchg" "cmpxchg64" "cmpxchg128"; do
|
|
gen_try_cmpxchg_fallbacks "${cmpxchg}"
|
|
done
|
|
|
|
for cmpxchg in "cmpxchg_local" "cmpxchg64_local" "cmpxchg128_local"; do
|
|
gen_cmpxchg_local_fallbacks "${cmpxchg}" ""
|
|
done
|
|
|
|
for cmpxchg in "sync_cmpxchg"; do
|
|
printf "#define raw_${cmpxchg} arch_${cmpxchg}\n\n"
|
|
done
|
|
|
|
grep '^[a-z]' "$1" | while read name meta args; do
|
|
gen_proto "${meta}" "${name}" "atomic" "int" ${args}
|
|
done
|
|
|
|
cat <<EOF
|
|
#ifdef CONFIG_GENERIC_ATOMIC64
|
|
#include <asm-generic/atomic64.h>
|
|
#endif
|
|
|
|
EOF
|
|
|
|
grep '^[a-z]' "$1" | while read name meta args; do
|
|
gen_proto "${meta}" "${name}" "atomic64" "s64" ${args}
|
|
done
|
|
|
|
cat <<EOF
|
|
#endif /* _LINUX_ATOMIC_FALLBACK_H */
|
|
EOF
|