beegfs/client_module/build/feature-detect.sh
2025-08-10 01:34:16 +02:00

479 lines
13 KiB
Bash
Executable File

#!/bin/bash
set -e
# NOTE, we're using the argument array as part of this string here. As is
# often the case in shell scripts (especially when used with Makefiles) the
# quoting here is not correct in the sense that it can be broken for example
# with arguments that contain spaces. But it is expected to work with our input
# data.
CFLAGS="-D__KERNEL__ $LINUXINCLUDE $* -DKBUILD_BASENAME=\"beegfs\" -DKBUILD_MODNAME=\"beegfs\""
_generate_includes() {
for i in "$@"; do
echo "#include <$i>"
done
}
_marker_if_compiles() {
local marker=$1
shift
if $CC $CFLAGS -x c -o /dev/null -c - 2>/dev/null
then
echo -D$marker
fi
}
_check_type_input() {
local tp=$1
shift 1
_generate_includes "$@"
cat <<EOF
void want_tp(void) {
$tp name;
}
EOF
}
check_type() {
local tp=$1
local marker=$2
shift 2
_check_type_input "$tp" "$@" | _marker_if_compiles "$marker"
}
_check_struct_field_input() {
local field=$1
shift 1
_generate_includes "$@"
cat <<EOF
void want_symbol(void) {
struct ${field%%::*} s;
(void) (s.${field##*::});
}
EOF
}
check_struct_field() {
local field=$1
local marker=$2
shift 2
_check_struct_field_input "$field" "$@" | _marker_if_compiles "$marker"
}
_check_struct_field_type_input() {
local field=$1
local ftype=$2
shift 2
_generate_includes "$@"
cat <<EOF
void want_symbol(void) {
struct ${field%%::*} s;
char predicate[
2 * __builtin_types_compatible_p(__typeof(s.${field##*::}), $ftype) - 1
];
}
EOF
}
check_struct_field_type() {
local field=$1
local ftype=$2
local marker=$3
shift 3
_check_struct_field_type_input "$field" "$ftype" "$@" | _marker_if_compiles "$marker"
}
_check_expr_type_input() {
local name=$1
local signature=$2
shift 2
_generate_includes "$@"
cat <<EOF
void want_fn(void) {
char predicate[
2 * __builtin_types_compatible_p(__typeof($name), $signature) - 1
];
}
EOF
}
check_expr_type() {
local name=$1
local signature=$2
local marker=$3
shift 3
_check_expr_type_input "$name" "$signature" "$@" | _marker_if_compiles "$marker"
}
check_function() {
check_expr_type "$@"
}
_check_symbol_input() {
local name=$1
shift
_generate_includes "$@"
cat <<EOF
__typeof__($name) predicate = $name;
EOF
}
check_header() {
local header=$1
local marker=$2
shift 2
_generate_includes "$header" | _marker_if_compiles "$marker"
}
check_symbol() {
local name=$1
local marker=$2
shift 2
_check_symbol_input "$name" "$@" | _marker_if_compiles "$marker"
}
check_struct_field \
inode::i_ctime \
KERNEL_HAS_INODE_CTIME \
linux/fs.h
check_struct_field \
inode::i_mtime \
KERNEL_HAS_INODE_MTIME \
linux/fs.h
check_struct_field \
dentry::d_subdirs \
KERNEL_HAS_DENTRY_SUBDIRS \
linux/dcache.h
check_function \
generic_readlink "int (struct dentry *, char __user *, int)" \
KERNEL_HAS_GENERIC_READLINK \
linux/fs.h
check_header \
linux/sched/signal.h \
KERNEL_HAS_SCHED_SIG_H
check_header \
linux/stdarg.h \
KERNEL_HAS_LINUX_STDARG_H
check_header \
linux/filelock.h \
KERNEL_HAS_LINUX_FILELOCK_H
# cryptohash does not include linux/types.h, so the type comparison fails
check_function \
half_md4_transform "__u32 (__u32[4], __u32 const in[8])" \
KERNEL_HAS_HALF_MD4_TRANSFORM \
linux/types.h linux/cryptohash.h
check_function \
vfs_getattr "int (const struct path *, struct kstat *, u32, unsigned int)" \
KERNEL_HAS_STATX \
linux/fs.h
check_function \
kref_read "unsigned int (const struct kref*)" \
KERNEL_HAS_KREF_READ \
linux/kref.h
check_function \
file_dentry "struct dentry* (const struct file *file)" \
KERNEL_HAS_FILE_DENTRY \
linux/fs.h
check_function \
super_setup_bdi_name "int (struct super_block *sb, char *fmt, ...)" \
KERNEL_HAS_SUPER_SETUP_BDI_NAME \
linux/fs.h
check_function \
have_submounts "int (struct dentry *parent)" \
KERNEL_HAS_HAVE_SUBMOUNTS \
linux/dcache.h
check_function \
kernel_read "ssize_t (struct file *, void *, size_t, loff_t *)" \
KERNEL_HAS_KERNEL_READ \
linux/fs.h
check_function \
skwq_has_sleeper "bool (struct socket_wq*)" \
KERNEL_HAS_SKWQ_HAS_SLEEPER \
net/sock.h
check_struct_field_type \
sock::sk_data_ready "void (*)(struct sock*, int)" \
KERNEL_HAS_SK_DATA_READY_2 \
net/sock.h
check_struct_field \
sock::sk_sleep \
KERNEL_HAS_SK_SLEEP \
net/sock.h
check_function \
current_time "struct timespec64 (struct inode *)" \
KERNEL_HAS_CURRENT_TIME_SPEC64 \
linux/fs.h
check_function \
sk_has_sleeper "int (struct sock*)" \
KERNEL_HAS_SK_HAS_SLEEPER \
net/sock.h
check_function \
__wake_up_sync_key "void (struct wait_queue_head* , unsigned int, void*)" \
KERNEL_WAKE_UP_SYNC_KEY_HAS_3_ARGUMENTS \
linux/wait.h
check_expr_type \
'get_fs()' "mm_segment_t" \
KERNEL_HAS_GET_FS \
linux/uaccess.h
check_function \
iov_iter_kvec 'void (struct iov_iter *i, int direction, const struct kvec *iov, unsigned long nr_segs, size_t count)' \
KERNEL_HAS_IOV_ITER_KVEC \
linux/uio.h
# The iov_iter_kvec() function has the same API but differing behaviour
# across Linux versions:
#
# Linux < 4.20 requires ITER_KVEC to be set in "direction"
# Linux >= 4.20 issues a warning (including stacktrace) if it _is_ set.
#
# We can't detect this from the headers, only by looking at the source.
# BUT: The source is not guaranteed to be available when building.
#
# We have tried to check for this "feature" by looking at the Linux version
# as reported by the LINUX_VERSION_CODE macro.
#
# BUT: Some non-vanilla < 4.20 kernels have backported the >= 4.20
# functionality to < 4.20 kernels. So relying on the version code doesn't
# work either.
#
# What we're doing here now is our current best attempt to detect how the API
# must be used. We're checking something that is not really related to the
# actual function, but which seems to give the right results.
check_function \
iov_iter_is_kvec 'bool(const struct iov_iter *)' \
KERNEL_HAS_IOV_ITER_KVEC_NO_TYPE_FLAG_IN_DIRECTION \
linux/uio.h
# print_stack_trace() found on older Linux kernels < 5.2
check_function \
print_stack_trace 'void (struct stack_trace *trace, int spaces)' \
KERNEL_HAS_PRINT_STACK_TRACE \
linux/stacktrace.h
# Starting from kernel 5.2, print_stack_trace() is demoted to tools/ directory
# so not available to us.
# Starting from kernel 5.16 print_stack_trace() is removed completely, but
# stack_trace_print() can be used instead.
# Notably, the identifier stack_trace_print() existed even in older Linux
# versions, but with a different signature and different functionality.
check_function \
stack_trace_print 'void (unsigned long *trace, unsigned int nr_entries, int spaces)' \
KERNEL_HAS_STACK_TRACE_PRINT \
linux/stacktrace.h \
check_type 'struct file_lock_core' KERNEL_HAS_FILE_LOCK_CORE linux/filelock.h
check_type 'struct proc_ops' KERNEL_HAS_PROC_OPS linux/proc_fs.h
check_type sockptr_t KERNEL_HAS_SOCKPTR_T linux/sockptr.h
check_function \
sock_setsockopt 'int (struct socket *, int, int, sockptr_t, unsigned int)' \
KERNEL_HAS_SOCK_SETSOCKOPT_SOCKPTR_T_PARAM \
net/sock.h
check_type time64_t KERNEL_HAS_TIME64 linux/ktime.h
check_function ktime_get_ts64 'void (struct timespec64 *)' KERNEL_HAS_KTIME_GET_TS64 linux/ktime.h
check_function ktime_get_real_ts64 'void (struct timespec64 *)' KERNEL_HAS_KTIME_GET_REAL_TS64 linux/ktime.h
check_function ktime_get_coarse_real_ts64 'void (struct timespec64 *)' KERNEL_HAS_KTIME_GET_COARSE_REAL_TS64 linux/ktime.h
# latest kernel from 6.3 changes moved to timekeeping.h
check_function \
ktime_get_ts64 "void (struct timespec64 *ts)" \
KERNEL_HAS_KTIME_GET_TS64 \
linux/timekeeping.h
check_function \
ktime_get_real_ts64 "void (struct timespec64 *tv)" \
KERNEL_HAS_KTIME_GET_REAL_TS64 \
linux/timekeeping.h
check_function \
ktime_get_coarse_real_ts64 "void (struct timespec64 *ts)" \
KERNEL_HAS_KTIME_GET_COARSE_REAL_TS64 \
linux/timekeeping.h
check_function \
generic_file_splice_read "ssize_t (struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int)" \
KERNEL_HAS_GENERIC_FILE_SPLICE_READ \
linux/fs.h
check_function \
generic_permission "int (struct inode *, int)" \
KERNEL_HAS_GENERIC_PERMISSION_2 \
linux/fs.h
check_function \
generic_permission "int (struct inode *, int, int (*check_acl)(struct inode *, int)" \
KERNEL_HAS_GENERIC_PERMISSION_4 \
linux/fs.h
check_function \
setattr_prepare "int (struct mnt_idmap *, struct dentry *, struct iattr *)" \
KERNEL_HAS_SETATTR_PREPARE \
linux/fs.h
check_function \
setattr_prepare "int (struct dentry *dentry, struct iattr *attr)" \
KERNEL_HAS_SETATTR_PREPARE \
linux/fs.h
check_function \
setattr_prepare "int (struct user_namespace *, struct dentry *, struct iattr *)" \
KERNEL_HAS_SETATTR_PREPARE \
linux/fs.h
check_struct_field \
inode_operations::get_acl \
KERNEL_HAS_GET_ACL \
linux/fs.h
check_struct_field_type \
inode_operations::get_acl "struct posix_acl* (*)(struct mnt_idmap *, struct dentry *, int)" \
KERNEL_HAS_POSIX_GET_ACL_IDMAP \
linux/fs.h
check_struct_field_type \
inode_operations::get_acl "struct posix_acl* (*)(struct user_namespace *, struct dentry *, int)" \
KERNEL_HAS_POSIX_GET_ACL_NS \
linux/fs.h
check_symbol \
"extern const struct xattr_handler posix_acl_default_xattr_handler;" \
KERNEL_HAS_POSIX_ACL_DEFAULT_XATTR_HANDLER \
linux/posix_acl_xattr.h
check_struct_field_type \
inode_operations::get_acl "struct posix_acl* (*)(struct inode *, int, bool)" \
KERNEL_POSIX_GET_ACL_HAS_RCU \
linux/fs.h
check_struct_field_type \
inode_operations::get_inode_acl "struct posix_acl* (*)(struct inode *, int, bool)" \
KERNEL_HAS_GET_INODE_ACL \
linux/fs.h
check_struct_field \
inode_operations::set_acl \
KERNEL_HAS_SET_ACL \
linux/fs.h
check_struct_field_type \
inode_operations::set_acl "int (*)(struct user_namespace *, struct inode *, struct posix_acl *, int)" \
KERNEL_HAS_SET_ACL_NS_INODE \
linux/fs.h
check_struct_field_type \
inode_operations::set_acl "int (*)(struct mnt_idmap *, struct dentry *, struct posix_acl *, int)" \
KERNEL_HAS_SET_ACL_DENTRY \
linux/fs.h
check_struct_field_type \
inode_operations::set_acl "int (*)(struct user_namespace *, struct dentry *, struct posix_acl *, int)" \
KERNEL_HAS_SET_ACL_DENTRY \
linux/fs.h
check_function \
vfs_create "int (struct user_namespace *, struct inode *, struct dentry *, umode_t, bool)" \
KERNEL_HAS_USER_NS_MOUNTS \
linux/fs.h
check_function \
vfs_create "int (struct mnt_idmap *, struct inode *, struct dentry *, umode_t, bool)" \
KERNEL_HAS_IDMAPPED_MOUNTS \
linux/fs.h
check_struct_field_type \
file_operations::iterate "int (*)(struct file *, struct dir_context *)" \
KERNEL_HAS_FOPS_ITERATE \
linux/fs.h
check_struct_field_type \
xattr_handler::set "int (*)(const struct xattr_handler *, struct dentry *, struct inode *, const char *, const void *, size_t, int)" \
KERNEL_HAS_XATTR_HANDLERS_INODE_ARG \
linux/xattr.h
check_struct_field_type \
xattr_handler::set "int (*)(const struct xattr_handler *, struct user_namespace *, struct dentry *, struct inode *, const char *, const void *, size_t, int)" \
KERNEL_HAS_XATTR_HANDLERS_INODE_ARG \
linux/xattr.h
check_struct_field_type \
xattr_handler::set "int (*)(const struct xattr_handler *, struct mnt_idmap *, struct dentry *, struct inode *, const char *, const void *, size_t, int)" \
KERNEL_HAS_XATTR_HANDLERS_INODE_ARG \
linux/xattr.h
check_struct_field \
thread_info::cpu \
KERNEL_HAS_CPU_IN_THREAD_INFO \
linux/thread_info.h
check_function \
generic_fillattr "void (struct mnt_idmap *, u32, struct inode *, struct kstat *)" \
KERNEL_HAS_GENERIC_FILLATTR_REQUEST_MASK \
linux/fs.h
# Kernel 6.5 introduced getters and setters for struct inode's ctime field
check_function \
inode_get_ctime "struct timespec64 (const struct inode *inode)" \
KERNEL_HAS_INODE_GET_SET_CTIME \
linux/fs.h
# Kernel 6.6 introduced more getters and setters, also for atime and mtime
check_function \
inode_get_mtime "struct timespec64 (const struct inode *inode)" \
KERNEL_HAS_INODE_GET_SET_CTIME_MTIME_ATIME \
linux/fs.h
# From Linux kernel 6.12 onward, unaligned.h has been moved from <asm/> to <linux/> include path
check_header \
linux/unaligned.h \
KERNEL_HAS_LINUX_UNALIGNED_H
# we have to communicate with the calling makefile somehow. since we can't really use the return
# code of this script, we'll echo a special string at the end of our output for the caller to
# detect and remove again.
# this string has to be something that will, on its own, never be a valid compiler option. so let's
# choose something really, really unlikely like
echo "--~~success~~--"