Updates for the VDSO subsystem:
- Provide the missing 64-bit variant of clock_getres()
This allows the extension of CONFIG_COMPAT_32BIT_TIME to the vDSO and
finally the removal of 32-bit time types from the kernel and UAPI.
- Remove the useless and broken getcpu_cache from the VDSO
The intention was to provide a trivial way to retrieve the CPU number from
the VDSO, but as the VDSO data is per process there is no way to make it
work.
- Switch get/put_unaligned() from packed struct to memcpy()
The packed struct violates strict aliasing rules which requires to pass
-fno-strict-aliasing to the compiler. As this are scalar values
__builtin_memcpy() turns them into simple loads and stores
- Use __typeof_unqual__() for __unqual_scalar_typeof()
The get/put_unaligned() changes triggered a new sparse warning when __beNN
types are used with get/put_unaligned() as sparse builds add a special
'bitwise' attribute to them which prevents sparse to evaluate the Generic
in __unqual_scalar_typeof().
Newer sparse versions support __typeof_unqual__() which avoids the problem,
but requires a recent sparse install. So this adds a sanity check to sparse
builds, which validates that sparse is available and capable of handling it.
- Force inline __cvdso_clock_getres_common()
Compilers sometimes un-inline agressively, which results in function call
overhead and problems with automatic stack variable initialization.
Interestingly enough the force inlining results in smaller code than the
un-inlined variant produced by GCC when optimizing for size.
-----BEGIN PGP SIGNATURE-----
iQJEBAABCgAuFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmmJ2eAQHHRnbHhAa2Vy
bmVsLm9yZwAKCRCmGPVMDXSYoXhQD/4mjneVlRBbQ6Nt9LxxhzPlYXvED6u8b4SX
R//DQ4qqqagh2fSpE57tr3f56HqhUmXptGJSgEvr6tVKoyugsLqP6sN9J9J3o181
jnPQR0VBP9dihQZ5X93pKo9NZWxLIn0uLD0RsdCbE9NTx4ciw4VIPFYQqkC4Rw6b
jiRrDR2l8EhV8cmxB6puW5WaQ932M6Awabw9RumzwH3MzIIlbc5Ero51S9eS64LL
byU5XeWUe295W1Gxze5RHHJWyNQEyx1eUCFfe3LWvfpz7FMzc2AQsKnIJDzW3GiO
UGu5MGptbLpG+ccvhVEs6/Ls5pWXcoCw4WuDNAunCCOmqda98oDniKf2LwRRbLT0
nAfLNatMnhXdTPk2zbS45z9uipUQAGKmVAE3/LVqB+ekcutmIGMyqHgR75QX0b4l
CQPkC9rBsV6gGsScWTnhRydhqioNO/uhhrQv0vEXnKZa0ysTbgZKt3JDZbgUEL2B
uDxXKyrqjpnqDZKlMMaoLtwd+l+T80ya4/NhHd4ZNGUpTUrHVw2H47lgE7ahCxEk
/SvXTZSU4Jp8sVQIQ+J6y5z2AQ/xGy++zvNKiZMyP9fQuPqhiDqYkLpmOp61bARx
wqyVsfGXZYSB1l16AiSC/CyUDcMqqsFohGXQ/Yf0SOiVtXu2WUyfofY1N0IXIGu4
WOVV9mH1yg==
=0ogX
-----END PGP SIGNATURE-----
Merge tag 'timers-vdso-2026-02-09' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull VDSO updates from Thomas Gleixner:
- Provide the missing 64-bit variant of clock_getres()
This allows the extension of CONFIG_COMPAT_32BIT_TIME to the vDSO and
finally the removal of 32-bit time types from the kernel and UAPI.
- Remove the useless and broken getcpu_cache from the VDSO
The intention was to provide a trivial way to retrieve the CPU number
from the VDSO, but as the VDSO data is per process there is no way to
make it work.
- Switch get/put_unaligned() from packed struct to memcpy()
The packed struct violates strict aliasing rules which requires to
pass -fno-strict-aliasing to the compiler. As this are scalar values
__builtin_memcpy() turns them into simple loads and stores
- Use __typeof_unqual__() for __unqual_scalar_typeof()
The get/put_unaligned() changes triggered a new sparse warning when
__beNN types are used with get/put_unaligned() as sparse builds add a
special 'bitwise' attribute to them which prevents sparse to evaluate
the Generic in __unqual_scalar_typeof().
Newer sparse versions support __typeof_unqual__() which avoids the
problem, but requires a recent sparse install. So this adds a sanity
check to sparse builds, which validates that sparse is available and
capable of handling it.
- Force inline __cvdso_clock_getres_common()
Compilers sometimes un-inline agressively, which results in function
call overhead and problems with automatic stack variable
initialization.
Interestingly enough the force inlining results in smaller code than
the un-inlined variant produced by GCC when optimizing for size.
* tag 'timers-vdso-2026-02-09' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
vdso/gettimeofday: Force inlining of __cvdso_clock_getres_common()
x86/percpu: Make CONFIG_USE_X86_SEG_SUPPORT work with sparse
compiler: Use __typeof_unqual__() for __unqual_scalar_typeof()
powerpc/vdso: Provide clock_getres_time64()
tools headers: Remove unneeded ignoring of warnings in unaligned.h
tools headers: Update the linux/unaligned.h copy with the kernel sources
vdso: Switch get/put_unaligned() from packed struct to memcpy()
parisc: Inline a type punning version of get_unaligned_le32()
vdso: Remove struct getcpu_cache
MIPS: vdso: Provide getres_time64() for 32-bit ABIs
arm64: vdso32: Provide clock_getres_time64()
ARM: VDSO: Provide clock_getres_time64()
ARM: VDSO: Patch out __vdso_clock_getres() if unavailable
x86/vdso: Provide clock_getres_time64() for x86-32
selftests: vDSO: vdso_test_abi: Add test for clock_getres_time64()
selftests: vDSO: vdso_test_abi: Use UAPI system call numbers
selftests: vDSO: vdso_config: Add configurations for clock_getres_time64()
vdso: Add prototype for __vdso_clock_getres_time64()
This commit is contained in:
commit
f1c538ca81
8
Makefile
8
Makefile
|
|
@ -1195,6 +1195,14 @@ CHECKFLAGS += $(if $(CONFIG_CPU_BIG_ENDIAN),-mbig-endian,-mlittle-endian)
|
|||
# the checker needs the correct machine size
|
||||
CHECKFLAGS += $(if $(CONFIG_64BIT),-m64,-m32)
|
||||
|
||||
# Validate the checker is available and functional
|
||||
ifneq ($(KBUILD_CHECKSRC), 0)
|
||||
ifneq ($(shell $(srctree)/scripts/checker-valid.sh $(CHECK) $(CHECKFLAGS)), 1)
|
||||
$(warning C=$(KBUILD_CHECKSRC) specified, but $(CHECK) is not available or not up to date)
|
||||
KBUILD_CHECKSRC = 0
|
||||
endif
|
||||
endif
|
||||
|
||||
# Default kernel image to build when no specific target is given.
|
||||
# KBUILD_IMAGE may be overruled on the command line or
|
||||
# set in the environment
|
||||
|
|
|
|||
|
|
@ -161,6 +161,8 @@ static void __init patch_vdso(void *ehdr)
|
|||
vdso_nullpatch_one(&einfo, "__vdso_gettimeofday");
|
||||
vdso_nullpatch_one(&einfo, "__vdso_clock_gettime");
|
||||
vdso_nullpatch_one(&einfo, "__vdso_clock_gettime64");
|
||||
vdso_nullpatch_one(&einfo, "__vdso_clock_getres");
|
||||
vdso_nullpatch_one(&einfo, "__vdso_clock_getres_time64");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ VERSION
|
|||
__vdso_gettimeofday;
|
||||
__vdso_clock_getres;
|
||||
__vdso_clock_gettime64;
|
||||
__vdso_clock_getres_time64;
|
||||
local: *;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@ int __vdso_clock_getres(clockid_t clock_id,
|
|||
return __cvdso_clock_getres_time32(clock_id, res);
|
||||
}
|
||||
|
||||
int __vdso_clock_getres_time64(clockid_t clock_id, struct __kernel_timespec *res)
|
||||
{
|
||||
return __cvdso_clock_getres(clock_id, res);
|
||||
}
|
||||
|
||||
/* Avoid unresolved references emitted by GCC */
|
||||
|
||||
void __aeabi_unwind_cpp_pr0(void)
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ VERSION
|
|||
__vdso_gettimeofday;
|
||||
__vdso_clock_getres;
|
||||
__vdso_clock_gettime64;
|
||||
__vdso_clock_getres_time64;
|
||||
local: *;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ int __vdso_clock_getres(clockid_t clock_id,
|
|||
return __cvdso_clock_getres_time32(clock_id, res);
|
||||
}
|
||||
|
||||
int __vdso_clock_getres_time64(clockid_t clock_id, struct __kernel_timespec *res)
|
||||
{
|
||||
return __cvdso_clock_getres(clock_id, res);
|
||||
}
|
||||
|
||||
/* Avoid unresolved references emitted by GCC */
|
||||
|
||||
void __aeabi_unwind_cpp_pr0(void)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
*/
|
||||
|
||||
#include <asm/vdso.h>
|
||||
#include <linux/getcpu.h>
|
||||
|
||||
static __always_inline int read_cpu_id(void)
|
||||
{
|
||||
|
|
@ -28,8 +27,8 @@ static __always_inline int read_cpu_id(void)
|
|||
}
|
||||
|
||||
extern
|
||||
int __vdso_getcpu(unsigned int *cpu, unsigned int *node, struct getcpu_cache *unused);
|
||||
int __vdso_getcpu(unsigned int *cpu, unsigned int *node, struct getcpu_cache *unused)
|
||||
int __vdso_getcpu(unsigned int *cpu, unsigned int *node, void *unused);
|
||||
int __vdso_getcpu(unsigned int *cpu, unsigned int *node, void *unused)
|
||||
{
|
||||
int cpu_id;
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ VERSION
|
|||
__vdso_clock_getres;
|
||||
#if _MIPS_SIM != _MIPS_SIM_ABI64
|
||||
__vdso_clock_gettime64;
|
||||
__vdso_clock_getres_time64;
|
||||
#endif
|
||||
#endif
|
||||
local: *;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ int __vdso_clock_gettime64(clockid_t clock,
|
|||
return __cvdso_clock_gettime(clock, ts);
|
||||
}
|
||||
|
||||
int __vdso_clock_getres_time64(clockid_t clock, struct __kernel_timespec *ts)
|
||||
{
|
||||
return __cvdso_clock_getres(clock, ts);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int __vdso_clock_gettime(clockid_t clock,
|
||||
|
|
|
|||
|
|
@ -278,6 +278,19 @@ static void parse_elf(void *output)
|
|||
free(phdrs);
|
||||
}
|
||||
|
||||
/*
|
||||
* The regular get_unaligned_le32 uses __builtin_memcpy which can trigger
|
||||
* warnings when reading a byte/char output_len as an integer, as the size of a
|
||||
* char is less than that of an integer. Use type punning and the packed
|
||||
* attribute, which requires -fno-strict-aliasing, to work around the problem.
|
||||
*/
|
||||
static u32 punned_get_unaligned_le32(const void *p)
|
||||
{
|
||||
const struct { __le32 x; } __packed * __get_pptr = p;
|
||||
|
||||
return le32_to_cpu(__get_pptr->x);
|
||||
}
|
||||
|
||||
asmlinkage unsigned long __visible decompress_kernel(unsigned int started_wide,
|
||||
unsigned int command_line,
|
||||
const unsigned int rd_start,
|
||||
|
|
@ -309,7 +322,7 @@ asmlinkage unsigned long __visible decompress_kernel(unsigned int started_wide,
|
|||
* leave 2 MB for the stack.
|
||||
*/
|
||||
vmlinux_addr = (unsigned long) &_ebss + 2*1024*1024;
|
||||
vmlinux_len = get_unaligned_le32(&output_len);
|
||||
vmlinux_len = punned_get_unaligned_le32(&output_len);
|
||||
output = (char *) vmlinux_addr;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -135,6 +135,8 @@ int __c_kernel_clock_gettime64(clockid_t clock, struct __kernel_timespec *ts,
|
|||
const struct vdso_time_data *vd);
|
||||
int __c_kernel_clock_getres(clockid_t clock_id, struct old_timespec32 *res,
|
||||
const struct vdso_time_data *vd);
|
||||
int __c_kernel_clock_getres_time64(clockid_t clock_id, struct __kernel_timespec *res,
|
||||
const struct vdso_time_data *vd);
|
||||
#endif
|
||||
int __c_kernel_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz,
|
||||
const struct vdso_time_data *vd);
|
||||
|
|
|
|||
|
|
@ -103,6 +103,18 @@ V_FUNCTION_BEGIN(__kernel_clock_getres)
|
|||
cvdso_call __c_kernel_clock_getres
|
||||
V_FUNCTION_END(__kernel_clock_getres)
|
||||
|
||||
/*
|
||||
* Exact prototype of clock_getres_time64()
|
||||
*
|
||||
* int __kernel_clock_getres(clockid_t clock_id, struct __timespec64 *res);
|
||||
*
|
||||
*/
|
||||
#ifndef __powerpc64__
|
||||
V_FUNCTION_BEGIN(__kernel_clock_getres_time64)
|
||||
cvdso_call __c_kernel_clock_getres_time64
|
||||
V_FUNCTION_END(__kernel_clock_getres_time64)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Exact prototype of time()
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ VERSION
|
|||
__kernel_clock_gettime;
|
||||
__kernel_clock_gettime64;
|
||||
__kernel_clock_getres;
|
||||
__kernel_clock_getres_time64;
|
||||
__kernel_time;
|
||||
__kernel_get_tbfreq;
|
||||
__kernel_sync_dicache;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@ int __c_kernel_clock_getres(clockid_t clock_id, struct old_timespec32 *res,
|
|||
{
|
||||
return __cvdso_clock_getres_time32_data(vd, clock_id, res);
|
||||
}
|
||||
|
||||
int __c_kernel_clock_getres_time64(clockid_t clock_id, struct __kernel_timespec *res,
|
||||
const struct vdso_time_data *vd)
|
||||
{
|
||||
return __cvdso_clock_getres_data(vd, clock_id, res);
|
||||
}
|
||||
#endif
|
||||
|
||||
int __c_kernel_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz,
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
/* Copyright IBM Corp. 2020 */
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/getcpu.h>
|
||||
#include <asm/timex.h>
|
||||
#include "vdso.h"
|
||||
|
||||
int __s390_vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused)
|
||||
int __s390_vdso_getcpu(unsigned *cpu, unsigned *node, void *unused)
|
||||
{
|
||||
union tod_clock clk;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@
|
|||
|
||||
#include <vdso/datapage.h>
|
||||
|
||||
struct getcpu_cache;
|
||||
|
||||
int __s390_vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused);
|
||||
int __s390_vdso_getcpu(unsigned *cpu, unsigned *node, void *unused);
|
||||
int __s390_vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz);
|
||||
int __s390_vdso_clock_gettime(clockid_t clock, struct __kernel_timespec *ts);
|
||||
int __s390_vdso_clock_getres(clockid_t clock, struct __kernel_timespec *ts);
|
||||
|
|
|
|||
|
|
@ -74,4 +74,12 @@ int __vdso_clock_getres(clockid_t clock, struct old_timespec32 *res)
|
|||
|
||||
int clock_getres(clockid_t, struct old_timespec32 *)
|
||||
__attribute__((weak, alias("__vdso_clock_getres")));
|
||||
|
||||
int __vdso_clock_getres_time64(clockid_t clock, struct __kernel_timespec *ts)
|
||||
{
|
||||
return __cvdso_clock_getres(clock, ts);
|
||||
}
|
||||
|
||||
int clock_getres_time64(clockid_t, struct __kernel_timespec *)
|
||||
__attribute__((weak, alias("__vdso_clock_getres_time64")));
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ VERSION
|
|||
__vdso_time;
|
||||
__vdso_clock_getres;
|
||||
__vdso_clock_gettime64;
|
||||
__vdso_clock_getres_time64;
|
||||
__vdso_getcpu;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,17 +6,16 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/getcpu.h>
|
||||
#include <asm/segment.h>
|
||||
#include <vdso/processor.h>
|
||||
|
||||
notrace long
|
||||
__vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused)
|
||||
__vdso_getcpu(unsigned *cpu, unsigned *node, void *unused)
|
||||
{
|
||||
vdso_read_cpunode(cpu, node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache)
|
||||
long getcpu(unsigned *cpu, unsigned *node, void *tcache)
|
||||
__attribute__((weak, alias("__vdso_getcpu")));
|
||||
|
|
|
|||
|
|
@ -137,12 +137,12 @@
|
|||
|
||||
#define __raw_cpu_read(size, qual, pcp) \
|
||||
({ \
|
||||
*(qual __my_cpu_type(pcp) *)__my_cpu_ptr(&(pcp)); \
|
||||
*(qual __my_cpu_type(pcp) * __force)__my_cpu_ptr(&(pcp)); \
|
||||
})
|
||||
|
||||
#define __raw_cpu_write(size, qual, pcp, val) \
|
||||
do { \
|
||||
*(qual __my_cpu_type(pcp) *)__my_cpu_ptr(&(pcp)) = (val); \
|
||||
#define __raw_cpu_write(size, qual, pcp, val) \
|
||||
do { \
|
||||
*(qual __my_cpu_type(pcp) * __force)__my_cpu_ptr(&(pcp)) = (val); \
|
||||
} while (0)
|
||||
|
||||
#define __raw_cpu_read_const(pcp) __raw_cpu_read(, , pcp)
|
||||
|
|
|
|||
|
|
@ -18,9 +18,7 @@ static __always_inline void cpu_relax(void)
|
|||
native_pause();
|
||||
}
|
||||
|
||||
struct getcpu_cache;
|
||||
|
||||
notrace long __vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused);
|
||||
notrace long __vdso_getcpu(unsigned *cpu, unsigned *node, void *unused);
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
|
||||
|
|
|
|||
|
|
@ -232,16 +232,6 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
|
|||
__BUILD_BUG_ON_ZERO_MSG(!__is_noncstr(p), \
|
||||
"must be non-C-string (not NUL-terminated)")
|
||||
|
||||
/*
|
||||
* Use __typeof_unqual__() when available.
|
||||
*
|
||||
* XXX: Remove test for __CHECKER__ once
|
||||
* sparse learns about __typeof_unqual__().
|
||||
*/
|
||||
#if CC_HAS_TYPEOF_UNQUAL && !defined(__CHECKER__)
|
||||
# define USE_TYPEOF_UNQUAL 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define TYPEOF_UNQUAL() to use __typeof_unqual__() as typeof
|
||||
* operator when available, to return an unqualified type of the exp.
|
||||
|
|
|
|||
|
|
@ -595,6 +595,14 @@ struct ftrace_likely_data {
|
|||
#define asm_inline asm
|
||||
#endif
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
/*
|
||||
* Use __typeof_unqual__() when available.
|
||||
*/
|
||||
#if CC_HAS_TYPEOF_UNQUAL || defined(__CHECKER__)
|
||||
# define USE_TYPEOF_UNQUAL 1
|
||||
#endif
|
||||
|
||||
/* Are two types/vars the same type (ignoring qualifiers)? */
|
||||
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
|
||||
|
||||
|
|
@ -602,6 +610,7 @@ struct ftrace_likely_data {
|
|||
* __unqual_scalar_typeof(x) - Declare an unqualified scalar type, leaving
|
||||
* non-scalar types unchanged.
|
||||
*/
|
||||
#ifndef USE_TYPEOF_UNQUAL
|
||||
/*
|
||||
* 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.
|
||||
|
|
@ -619,6 +628,10 @@ struct ftrace_likely_data {
|
|||
__scalar_type_to_expr_cases(long), \
|
||||
__scalar_type_to_expr_cases(long long), \
|
||||
default: (x)))
|
||||
#else
|
||||
#define __unqual_scalar_typeof(x) __typeof_unqual__(x)
|
||||
#endif
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
/*
|
||||
* __signed_scalar_typeof(x) - Declare a signed scalar type, leaving
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_GETCPU_H
|
||||
#define _LINUX_GETCPU_H 1
|
||||
|
||||
/* Cache for getcpu() to speed it up. Results might be a short time
|
||||
out of date, but will be faster.
|
||||
|
||||
User programs should not refer to the contents of this structure.
|
||||
I repeat they should not refer to it. If they do they will break
|
||||
in future kernels.
|
||||
|
||||
It is only a private cache for vgetcpu(). It will change in future kernels.
|
||||
The user program must store this information per thread (__thread)
|
||||
If you want 100% accurate information pass NULL instead. */
|
||||
struct getcpu_cache {
|
||||
unsigned long blob[128 / sizeof(long)];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -59,7 +59,6 @@ struct compat_stat;
|
|||
struct old_timeval32;
|
||||
struct robust_list_head;
|
||||
struct futex_waitv;
|
||||
struct getcpu_cache;
|
||||
struct old_linux_dirent;
|
||||
struct perf_event_attr;
|
||||
struct file_handle;
|
||||
|
|
@ -718,7 +717,7 @@ asmlinkage long sys_getrusage(int who, struct rusage __user *ru);
|
|||
asmlinkage long sys_umask(int mask);
|
||||
asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5);
|
||||
asmlinkage long sys_getcpu(unsigned __user *cpu, unsigned __user *node, struct getcpu_cache __user *cache);
|
||||
asmlinkage long sys_getcpu(unsigned __user *cpu, unsigned __user *node, void __user *cache);
|
||||
asmlinkage long sys_gettimeofday(struct __kernel_old_timeval __user *tv,
|
||||
struct timezone __user *tz);
|
||||
asmlinkage long sys_settimeofday(struct __kernel_old_timeval __user *tv,
|
||||
|
|
|
|||
|
|
@ -20,5 +20,6 @@ int __vdso_clock_gettime(clockid_t clock, struct __kernel_timespec *ts);
|
|||
__kernel_old_time_t __vdso_time(__kernel_old_time_t *t);
|
||||
int __vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz);
|
||||
int __vdso_clock_gettime64(clockid_t clock, struct __kernel_timespec *ts);
|
||||
int __vdso_clock_getres_time64(clockid_t clock, struct __kernel_timespec *ts);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2,14 +2,43 @@
|
|||
#ifndef __VDSO_UNALIGNED_H
|
||||
#define __VDSO_UNALIGNED_H
|
||||
|
||||
#define __get_unaligned_t(type, ptr) ({ \
|
||||
const struct { type x; } __packed * __get_pptr = (typeof(__get_pptr))(ptr); \
|
||||
__get_pptr->x; \
|
||||
#include <linux/compiler_types.h>
|
||||
|
||||
/**
|
||||
* __get_unaligned_t - read an unaligned value from memory.
|
||||
* @type: the type to load from the pointer.
|
||||
* @ptr: the pointer to load from.
|
||||
*
|
||||
* Use memcpy to affect an unaligned type sized load avoiding undefined behavior
|
||||
* from approaches like type punning that require -fno-strict-aliasing in order
|
||||
* to be correct. As type may be const, use __unqual_scalar_typeof to map to a
|
||||
* non-const type - you can't memcpy into a const type. The
|
||||
* __get_unaligned_ctrl_type gives __unqual_scalar_typeof its required
|
||||
* expression rather than type, a pointer is used to avoid warnings about mixing
|
||||
* the use of 0 and NULL. The void* cast silences ubsan warnings.
|
||||
*/
|
||||
#define __get_unaligned_t(type, ptr) ({ \
|
||||
type *__get_unaligned_ctrl_type __always_unused = NULL; \
|
||||
__unqual_scalar_typeof(*__get_unaligned_ctrl_type) __get_unaligned_val; \
|
||||
__builtin_memcpy(&__get_unaligned_val, (void *)(ptr), \
|
||||
sizeof(__get_unaligned_val)); \
|
||||
__get_unaligned_val; \
|
||||
})
|
||||
|
||||
#define __put_unaligned_t(type, val, ptr) do { \
|
||||
struct { type x; } __packed * __put_pptr = (typeof(__put_pptr))(ptr); \
|
||||
__put_pptr->x = (val); \
|
||||
/**
|
||||
* __put_unaligned_t - write an unaligned value to memory.
|
||||
* @type: the type of the value to store.
|
||||
* @val: the value to store.
|
||||
* @ptr: the pointer to store to.
|
||||
*
|
||||
* Use memcpy to affect an unaligned type sized store avoiding undefined
|
||||
* behavior from approaches like type punning that require -fno-strict-aliasing
|
||||
* in order to be correct. The void* cast silences ubsan warnings.
|
||||
*/
|
||||
#define __put_unaligned_t(type, val, ptr) do { \
|
||||
type __put_unaligned_val = (val); \
|
||||
__builtin_memcpy((void *)(ptr), &__put_unaligned_val, \
|
||||
sizeof(__put_unaligned_val)); \
|
||||
} while (0)
|
||||
|
||||
#endif /* __VDSO_UNALIGNED_H */
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#include <linux/tty.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/cn_proc.h>
|
||||
#include <linux/getcpu.h>
|
||||
#include <linux/task_io_accounting_ops.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/cpu.h>
|
||||
|
|
@ -2882,8 +2881,7 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
|||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
|
||||
struct getcpu_cache __user *, unused)
|
||||
SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep, void __user *, unused)
|
||||
{
|
||||
int err = 0;
|
||||
int cpu = raw_smp_processor_id();
|
||||
|
|
|
|||
|
|
@ -421,7 +421,7 @@ static __maybe_unused __kernel_old_time_t __cvdso_time(__kernel_old_time_t *time
|
|||
#endif /* VDSO_HAS_TIME */
|
||||
|
||||
#ifdef VDSO_HAS_CLOCK_GETRES
|
||||
static __maybe_unused
|
||||
static __always_inline
|
||||
bool __cvdso_clock_getres_common(const struct vdso_time_data *vd, clockid_t clock,
|
||||
struct __kernel_timespec *res)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh -eu
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
[ ! -x "$(command -v "$1")" ] && exit 1
|
||||
|
||||
tmp_file=$(mktemp)
|
||||
trap "rm -f $tmp_file" EXIT
|
||||
|
||||
cat << EOF >$tmp_file
|
||||
static inline int u(const int *q)
|
||||
{
|
||||
__typeof_unqual__(*q) v = *q;
|
||||
return v;
|
||||
}
|
||||
EOF
|
||||
|
||||
# sparse happily exits with 0 on error so validate
|
||||
# there is none on stderr. Use awk as grep is a pain with sh -e
|
||||
$@ $tmp_file 2>&1 | awk -v c=1 '/error/{c=0}END{print c}'
|
||||
|
|
@ -24,4 +24,26 @@
|
|||
#define asm_goto_output(x...) asm goto(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* __unqual_scalar_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.
|
||||
*/
|
||||
#define __scalar_type_to_expr_cases(type) \
|
||||
unsigned type: (unsigned type)0, \
|
||||
signed type: (signed type)0
|
||||
|
||||
#define __unqual_scalar_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: (x)))
|
||||
|
||||
#endif /* __LINUX_COMPILER_TYPES_H */
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@
|
|||
* This is the most generic implementation of unaligned accesses
|
||||
* and should work almost anywhere.
|
||||
*/
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpacked"
|
||||
#pragma GCC diagnostic ignored "-Wattributes"
|
||||
#include <vdso/unaligned.h>
|
||||
|
||||
#define get_unaligned(ptr) __get_unaligned_t(typeof(*(ptr)), (ptr))
|
||||
|
|
@ -143,6 +140,5 @@ static inline u64 get_unaligned_be48(const void *p)
|
|||
{
|
||||
return __get_unaligned_be48(p);
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif /* __LINUX_UNALIGNED_H */
|
||||
|
|
|
|||
|
|
@ -2,14 +2,43 @@
|
|||
#ifndef __VDSO_UNALIGNED_H
|
||||
#define __VDSO_UNALIGNED_H
|
||||
|
||||
#define __get_unaligned_t(type, ptr) ({ \
|
||||
const struct { type x; } __packed * __get_pptr = (typeof(__get_pptr))(ptr); \
|
||||
__get_pptr->x; \
|
||||
#include <linux/compiler_types.h>
|
||||
|
||||
/**
|
||||
* __get_unaligned_t - read an unaligned value from memory.
|
||||
* @type: the type to load from the pointer.
|
||||
* @ptr: the pointer to load from.
|
||||
*
|
||||
* Use memcpy to affect an unaligned type sized load avoiding undefined behavior
|
||||
* from approaches like type punning that require -fno-strict-aliasing in order
|
||||
* to be correct. As type may be const, use __unqual_scalar_typeof to map to a
|
||||
* non-const type - you can't memcpy into a const type. The
|
||||
* __get_unaligned_ctrl_type gives __unqual_scalar_typeof its required
|
||||
* expression rather than type, a pointer is used to avoid warnings about mixing
|
||||
* the use of 0 and NULL. The void* cast silences ubsan warnings.
|
||||
*/
|
||||
#define __get_unaligned_t(type, ptr) ({ \
|
||||
type *__get_unaligned_ctrl_type __always_unused = NULL; \
|
||||
__unqual_scalar_typeof(*__get_unaligned_ctrl_type) __get_unaligned_val; \
|
||||
__builtin_memcpy(&__get_unaligned_val, (void *)(ptr), \
|
||||
sizeof(__get_unaligned_val)); \
|
||||
__get_unaligned_val; \
|
||||
})
|
||||
|
||||
#define __put_unaligned_t(type, val, ptr) do { \
|
||||
struct { type x; } __packed * __put_pptr = (typeof(__put_pptr))(ptr); \
|
||||
__put_pptr->x = (val); \
|
||||
/**
|
||||
* __put_unaligned_t - write an unaligned value to memory.
|
||||
* @type: the type of the value to store.
|
||||
* @val: the value to store.
|
||||
* @ptr: the pointer to store to.
|
||||
*
|
||||
* Use memcpy to affect an unaligned type sized store avoiding undefined
|
||||
* behavior from approaches like type punning that require -fno-strict-aliasing
|
||||
* in order to be correct. The void* cast silences ubsan warnings.
|
||||
*/
|
||||
#define __put_unaligned_t(type, val, ptr) do { \
|
||||
type __put_unaligned_val = (val); \
|
||||
__builtin_memcpy((void *)(ptr), &__put_unaligned_val, \
|
||||
sizeof(__put_unaligned_val)); \
|
||||
} while (0)
|
||||
|
||||
#endif /* __VDSO_UNALIGNED_H */
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ static const char *versions[7] = {
|
|||
};
|
||||
|
||||
__attribute__((unused))
|
||||
static const char *names[2][7] = {
|
||||
static const char *names[2][8] = {
|
||||
{
|
||||
"__kernel_gettimeofday",
|
||||
"__kernel_clock_gettime",
|
||||
|
|
@ -75,6 +75,7 @@ static const char *names[2][7] = {
|
|||
"__kernel_getcpu",
|
||||
"__kernel_clock_gettime64",
|
||||
"__kernel_getrandom",
|
||||
"__kernel_clock_getres_time64",
|
||||
},
|
||||
{
|
||||
"__vdso_gettimeofday",
|
||||
|
|
@ -84,6 +85,7 @@ static const char *names[2][7] = {
|
|||
"__vdso_getcpu",
|
||||
"__vdso_clock_gettime64",
|
||||
"__vdso_getrandom",
|
||||
"__vdso_clock_getres_time64",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ typedef long (*vdso_gettimeofday_t)(struct timeval *tv, struct timezone *tz);
|
|||
typedef long (*vdso_clock_gettime_t)(clockid_t clk_id, struct timespec *ts);
|
||||
typedef long (*vdso_clock_gettime64_t)(clockid_t clk_id, struct vdso_timespec64 *ts);
|
||||
typedef long (*vdso_clock_getres_t)(clockid_t clk_id, struct timespec *ts);
|
||||
typedef long (*vdso_clock_getres_time64_t)(clockid_t clk_id, struct vdso_timespec64 *ts);
|
||||
typedef time_t (*vdso_time_t)(time_t *t);
|
||||
|
||||
static const char * const vdso_clock_name[] = {
|
||||
|
|
@ -179,7 +180,7 @@ static void vdso_test_clock_getres(clockid_t clk_id)
|
|||
clock_getres_fail++;
|
||||
}
|
||||
|
||||
ret = syscall(SYS_clock_getres, clk_id, &sys_ts);
|
||||
ret = syscall(__NR_clock_getres, clk_id, &sys_ts);
|
||||
|
||||
ksft_print_msg("The syscall resolution is %lld %lld\n",
|
||||
(long long)sys_ts.tv_sec, (long long)sys_ts.tv_nsec);
|
||||
|
|
@ -196,6 +197,55 @@ static void vdso_test_clock_getres(clockid_t clk_id)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef __NR_clock_getres_time64
|
||||
static void vdso_test_clock_getres_time64(clockid_t clk_id)
|
||||
{
|
||||
int clock_getres_fail = 0;
|
||||
|
||||
/* Find clock_getres. */
|
||||
vdso_clock_getres_time64_t vdso_clock_getres_time64 =
|
||||
(vdso_clock_getres_time64_t)vdso_sym(version, name[7]);
|
||||
|
||||
if (!vdso_clock_getres_time64) {
|
||||
ksft_print_msg("Couldn't find %s\n", name[7]);
|
||||
ksft_test_result_skip("%s %s\n", name[7],
|
||||
vdso_clock_name[clk_id]);
|
||||
return;
|
||||
}
|
||||
|
||||
struct vdso_timespec64 ts, sys_ts;
|
||||
long ret = VDSO_CALL(vdso_clock_getres_time64, 2, clk_id, &ts);
|
||||
|
||||
if (ret == 0) {
|
||||
ksft_print_msg("The vdso resolution is %lld %lld\n",
|
||||
(long long)ts.tv_sec, (long long)ts.tv_nsec);
|
||||
} else {
|
||||
clock_getres_fail++;
|
||||
}
|
||||
|
||||
ret = syscall(__NR_clock_getres_time64, clk_id, &sys_ts);
|
||||
|
||||
ksft_print_msg("The syscall resolution is %lld %lld\n",
|
||||
(long long)sys_ts.tv_sec, (long long)sys_ts.tv_nsec);
|
||||
|
||||
if ((sys_ts.tv_sec != ts.tv_sec) || (sys_ts.tv_nsec != ts.tv_nsec))
|
||||
clock_getres_fail++;
|
||||
|
||||
if (clock_getres_fail > 0) {
|
||||
ksft_test_result_fail("%s %s\n", name[7],
|
||||
vdso_clock_name[clk_id]);
|
||||
} else {
|
||||
ksft_test_result_pass("%s %s\n", name[7],
|
||||
vdso_clock_name[clk_id]);
|
||||
}
|
||||
}
|
||||
#else /* !__NR_clock_getres_time64 */
|
||||
static void vdso_test_clock_getres_time64(clockid_t clk_id)
|
||||
{
|
||||
ksft_test_result_skip("%s %s\n", name[7], vdso_clock_name[clk_id]);
|
||||
}
|
||||
#endif /* __NR_clock_getres_time64 */
|
||||
|
||||
/*
|
||||
* This function calls vdso_test_clock_gettime and vdso_test_clock_getres
|
||||
* with different values for clock_id.
|
||||
|
|
@ -208,9 +258,10 @@ static inline void vdso_test_clock(clockid_t clock_id)
|
|||
vdso_test_clock_gettime64(clock_id);
|
||||
|
||||
vdso_test_clock_getres(clock_id);
|
||||
vdso_test_clock_getres_time64(clock_id);
|
||||
}
|
||||
|
||||
#define VDSO_TEST_PLAN 29
|
||||
#define VDSO_TEST_PLAN 38
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,9 +16,7 @@
|
|||
#include "vdso_config.h"
|
||||
#include "vdso_call.h"
|
||||
|
||||
struct getcpu_cache;
|
||||
typedef long (*getcpu_t)(unsigned int *, unsigned int *,
|
||||
struct getcpu_cache *);
|
||||
typedef long (*getcpu_t)(unsigned int *, unsigned int *, void *);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue