From: Alistair Francis <[email protected]>
Convert tools/perf to only use a 64-bit time_t. On 64-bit architectures
this isn't a functional change. On 32-bit architectures we now only
perform 64-bit time_t syscalls (__NR_futex_time64) and use a struct
timespec64.
This won't work on kernels before 5.1, but as perf is tied to the kernel
that's ok.
This allows us to build perf for 32-bit architectures with 64-bit time_t
like RISC-V 32-bit.
Signed-off-by: Alistair Francis <[email protected]>
---
tools/perf/bench/futex.h | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/tools/perf/bench/futex.h b/tools/perf/bench/futex.h
index b3853aac3021c..c1ca75657c32e 100644
--- a/tools/perf/bench/futex.h
+++ b/tools/perf/bench/futex.h
@@ -27,12 +27,17 @@ struct bench_futex_parameters {
unsigned int nrequeue;
};
+struct timespec64 {
+ long long tv_sec; /* seconds */
+ long long tv_nsec; /* nanoseconds */
+};
+
/**
* futex() - SYS_futex syscall wrapper
* @uaddr: address of first futex
* @op: futex op code
* @val: typically expected value of uaddr, but varies by op
- * @timeout: typically an absolute struct timespec (except where noted
+ * @timeout: typically an absolute struct timespec64 (except where noted
* otherwise). Overloaded by some ops
* @uaddr2: address of second futex for some ops
* @val3: varies by op
@@ -47,15 +52,26 @@ struct bench_futex_parameters {
* These argument descriptions are the defaults for all
* like-named arguments in the following wrappers except where noted below.
*/
-#define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
- syscall(SYS_futex, uaddr, op | opflags, val, timeout, uaddr2, val3)
+/**
+ * We only support 64-bit time_t for the timeout.
+ * On 64-bit architectures we can use __NR_futex
+ * On 32-bit architectures we use __NR_futex_time64. This only works on kernel
+ * versions 5.1+.
+ */
+#if __BITS_PER_LONG == 64 || defined(__i386__)
+# define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
+ syscall(__NR_futex, uaddr, op | opflags, val, timeout, uaddr2, val3)
+#else
+# define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
+ syscall(__NR_futex_time64, uaddr, op | opflags, val, timeout, uaddr2, val3)
+#endif
/**
* futex_wait() - block on uaddr with optional timeout
* @timeout: relative timeout
*/
static inline int
-futex_wait(u_int32_t *uaddr, u_int32_t val, struct timespec *timeout, int opflags)
+futex_wait(u_int32_t *uaddr, u_int32_t val, struct timespec64 *timeout, int opflags)
{
return futex(uaddr, FUTEX_WAIT, val, timeout, NULL, 0, opflags);
}
@@ -74,7 +90,7 @@ futex_wake(u_int32_t *uaddr, int nr_wake, int opflags)
* futex_lock_pi() - block on uaddr as a PI mutex
*/
static inline int
-futex_lock_pi(u_int32_t *uaddr, struct timespec *timeout, int opflags)
+futex_lock_pi(u_int32_t *uaddr, struct timespec64 *timeout, int opflags)
{
return futex(uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0, opflags);
}
@@ -111,7 +127,7 @@ futex_cmp_requeue(u_int32_t *uaddr, u_int32_t val, u_int32_t *uaddr2, int nr_wak
*/
static inline int
futex_wait_requeue_pi(u_int32_t *uaddr, u_int32_t val, u_int32_t *uaddr2,
- struct timespec *timeout, int opflags)
+ struct timespec64 *timeout, int opflags)
{
return futex(uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0,
opflags);
--
2.31.1
From: Alistair Francis <[email protected]>
Convert the futex selftests to only use a 64-bit time_t. On 64-bit
architectures this isn't a functional change. On 32-bit architectures
we now only perform 64-bit time_t syscalls (__NR_futex_time64) and
use a struct timespec64.
This won't work on kernels before 5.1, but as perf is tied to the kernel
that's ok.
This allows the tests to run and pass on RISC-V 32-bit.
Signed-off-by: Alistair Francis <[email protected]>
---
.../futex/functional/futex_requeue.c | 2 +-
.../futex/functional/futex_requeue_pi.c | 6 +--
.../selftests/futex/functional/futex_wait.c | 2 +-
.../futex_wait_private_mapped_file.c | 2 +-
.../futex/functional/futex_wait_timeout.c | 8 ++--
.../futex/functional/futex_wait_wouldblock.c | 2 +-
.../selftests/futex/include/futextest.h | 39 +++++++++++++++----
7 files changed, 43 insertions(+), 18 deletions(-)
diff --git a/tools/testing/selftests/futex/functional/futex_requeue.c b/tools/testing/selftests/futex/functional/futex_requeue.c
index 51485be6eb2f1..f51aedffcd161 100644
--- a/tools/testing/selftests/futex/functional/futex_requeue.c
+++ b/tools/testing/selftests/futex/functional/futex_requeue.c
@@ -27,7 +27,7 @@ void usage(char *prog)
void *waiterfn(void *arg)
{
- struct timespec to;
+ struct timespec64 to;
to.tv_sec = 0;
to.tv_nsec = timeout_ns;
diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi.c b/tools/testing/selftests/futex/functional/futex_requeue_pi.c
index 1ee5518ee6b7f..497c0dc80916f 100644
--- a/tools/testing/selftests/futex/functional/futex_requeue_pi.c
+++ b/tools/testing/selftests/futex/functional/futex_requeue_pi.c
@@ -48,7 +48,7 @@ static int locked;
struct thread_arg {
long id;
- struct timespec *timeout;
+ struct timespec64 *timeout;
int lock;
int ret;
};
@@ -281,7 +281,7 @@ int unit_test(int broadcast, long lock, int third_party_owner, long timeout_ns)
struct thread_arg blocker_arg = THREAD_ARG_INITIALIZER;
struct thread_arg waker_arg = THREAD_ARG_INITIALIZER;
pthread_t waiter[THREAD_MAX], waker, blocker;
- struct timespec ts, *tsp = NULL;
+ struct timespec64 ts, *tsp = NULL;
struct thread_arg args[THREAD_MAX];
int *waiter_ret;
int i, ret = RET_PASS;
@@ -290,7 +290,7 @@ int unit_test(int broadcast, long lock, int third_party_owner, long timeout_ns)
time_t secs;
info("timeout_ns = %ld\n", timeout_ns);
- ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ ret = gettime64(CLOCK_MONOTONIC, &ts);
secs = (ts.tv_nsec + timeout_ns) / 1000000000;
ts.tv_nsec = ((int64_t)ts.tv_nsec + timeout_ns) % 1000000000;
ts.tv_sec += secs;
diff --git a/tools/testing/selftests/futex/functional/futex_wait.c b/tools/testing/selftests/futex/functional/futex_wait.c
index 685140d9b93d2..d1c8a4212c74c 100644
--- a/tools/testing/selftests/futex/functional/futex_wait.c
+++ b/tools/testing/selftests/futex/functional/futex_wait.c
@@ -30,7 +30,7 @@ void usage(char *prog)
static void *waiterfn(void *arg)
{
- struct timespec to;
+ struct timespec64 to;
unsigned int flags = 0;
if (arg)
diff --git a/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c b/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c
index fb4148f23fa37..5e84e136ad99e 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c
@@ -38,7 +38,7 @@ futex_t val = 1;
char pad2[PAGE_SZ] = {1};
#define WAKE_WAIT_US 3000000
-struct timespec wait_timeout = { .tv_sec = 5, .tv_nsec = 0};
+struct timespec64 wait_timeout = { .tv_sec = 5, .tv_nsec = 0};
void usage(char *prog)
{
diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
index 1f8f6daaf1e70..86b1e847a0246 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
@@ -71,11 +71,11 @@ static void test_timeout(int res, int *ret, char *test_name, int err)
/*
* Calculate absolute timeout and correct overflow
*/
-static int futex_get_abs_timeout(clockid_t clockid, struct timespec *to,
+static int futex_get_abs_timeout(clockid_t clockid, struct timespec64 *to,
long timeout_ns)
{
- if (clock_gettime(clockid, to)) {
- error("clock_gettime failed\n", errno);
+ if (gettime64(clockid, to)) {
+ error("gettime64 failed\n", errno);
return errno;
}
@@ -93,7 +93,7 @@ int main(int argc, char *argv[])
{
futex_t f1 = FUTEX_INITIALIZER;
int res, ret = RET_PASS;
- struct timespec to;
+ struct timespec64 to;
pthread_t thread;
int c;
diff --git a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
index 0ae390ff81644..76faa664544d6 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
@@ -38,7 +38,7 @@ void usage(char *prog)
int main(int argc, char *argv[])
{
- struct timespec to = {.tv_sec = 0, .tv_nsec = timeout_ns};
+ struct timespec64 to = {.tv_sec = 0, .tv_nsec = timeout_ns};
futex_t f1 = FUTEX_INITIALIZER;
int res, ret = RET_PASS;
int c;
diff --git a/tools/testing/selftests/futex/include/futextest.h b/tools/testing/selftests/futex/include/futextest.h
index ddbcfc9b7bac4..72361b41cdbae 100644
--- a/tools/testing/selftests/futex/include/futextest.h
+++ b/tools/testing/selftests/futex/include/futextest.h
@@ -47,12 +47,17 @@ typedef volatile u_int32_t futex_t;
FUTEX_PRIVATE_FLAG)
#endif
+struct timespec64 {
+ long long tv_sec; /* seconds */
+ long long tv_nsec; /* nanoseconds */
+};
+
/**
* futex() - SYS_futex syscall wrapper
* @uaddr: address of first futex
* @op: futex op code
* @val: typically expected value of uaddr, but varies by op
- * @timeout: typically an absolute struct timespec (except where noted
+ * @timeout: typically an absolute struct timespec64 (except where noted
* otherwise). Overloaded by some ops
* @uaddr2: address of second futex for some ops\
* @val3: varies by op
@@ -67,15 +72,35 @@ typedef volatile u_int32_t futex_t;
* These argument descriptions are the defaults for all
* like-named arguments in the following wrappers except where noted below.
*/
-#define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
- syscall(SYS_futex, uaddr, op | opflags, val, timeout, uaddr2, val3)
+/**
+ * We only support 64-bit time_t for the timeout.
+ * On 64-bit architectures we can use __NR_futex
+ * On 32-bit architectures we use __NR_futex_time64. This only works on kernel
+ * versions 5.1+.
+ */
+#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
+# define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
+ syscall(__NR_futex, uaddr, op | opflags, val, timeout, uaddr2, val3)
+#else
+# define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
+ syscall(__NR_futex_time64, uaddr, op | opflags, val, timeout, uaddr2, val3)
+#endif
+
+static inline int gettime64(clock_t clockid, struct timespec64 *tv)
+{
+#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
+ return syscall(__NR_clock_gettime, clockid, tv);
+#else
+ return syscall(__NR_clock_gettime64, clockid, tv);
+#endif
+}
/**
* futex_wait() - block on uaddr with optional timeout
* @timeout: relative timeout
*/
static inline int
-futex_wait(futex_t *uaddr, futex_t val, struct timespec *timeout, int opflags)
+futex_wait(futex_t *uaddr, futex_t val, struct timespec64 *timeout, int opflags)
{
return futex(uaddr, FUTEX_WAIT, val, timeout, NULL, 0, opflags);
}
@@ -95,7 +120,7 @@ futex_wake(futex_t *uaddr, int nr_wake, int opflags)
* @bitset: bitset to be used with futex_wake_bitset
*/
static inline int
-futex_wait_bitset(futex_t *uaddr, futex_t val, struct timespec *timeout,
+futex_wait_bitset(futex_t *uaddr, futex_t val, struct timespec64 *timeout,
u_int32_t bitset, int opflags)
{
return futex(uaddr, FUTEX_WAIT_BITSET, val, timeout, NULL, bitset,
@@ -118,7 +143,7 @@ futex_wake_bitset(futex_t *uaddr, int nr_wake, u_int32_t bitset, int opflags)
* @detect: whether (1) or not (0) to perform deadlock detection
*/
static inline int
-futex_lock_pi(futex_t *uaddr, struct timespec *timeout, int detect,
+futex_lock_pi(futex_t *uaddr, struct timespec64 *timeout, int detect,
int opflags)
{
return futex(uaddr, FUTEX_LOCK_PI, detect, timeout, NULL, 0, opflags);
@@ -183,7 +208,7 @@ futex_cmp_requeue(futex_t *uaddr, futex_t val, futex_t *uaddr2, int nr_wake,
*/
static inline int
futex_wait_requeue_pi(futex_t *uaddr, futex_t val, futex_t *uaddr2,
- struct timespec *timeout, int opflags)
+ struct timespec64 *timeout, int opflags)
{
return futex(uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0,
opflags);
--
2.31.1
On Thu, Oct 14, 2021 at 7:55 AM Alistair Francis
<[email protected]> wrote:
> +/**
> + * We only support 64-bit time_t for the timeout.
> + * On 64-bit architectures we can use __NR_futex
> + * On 32-bit architectures we use __NR_futex_time64. This only works on kernel
> + * versions 5.1+.
> + */
> +#if __BITS_PER_LONG == 64 || defined(__i386__)
> +# define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
> + syscall(__NR_futex, uaddr, op | opflags, val, timeout, uaddr2, val3)
> +#else
> +# define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
> + syscall(__NR_futex_time64, uaddr, op | opflags, val, timeout, uaddr2, val3)
> +#endif
That __i386__ check looks wrong, was this meant to check for x32 instead?
In that case, I wouldn't bother, as x32 can also just use the futex_time64 call
like the normal 32-bit architectures.
Arnd
On Thu, Oct 14, 2021 at 7:55 AM Alistair Francis
<[email protected]> wrote:
>
> From: Alistair Francis <[email protected]>
>
> Convert the futex selftests to only use a 64-bit time_t. On 64-bit
> architectures this isn't a functional change. On 32-bit architectures
> we now only perform 64-bit time_t syscalls (__NR_futex_time64) and
> use a struct timespec64.
>
> This won't work on kernels before 5.1, but as perf is tied to the kernel
> that's ok.
>
> This allows the tests to run and pass on RISC-V 32-bit.
>
> Signed-off-by: Alistair Francis <[email protected]>
This looks correct to me, two minor comments:
> +struct timespec64 {
> + long long tv_sec; /* seconds */
> + long long tv_nsec; /* nanoseconds */
> +};
This is a bit different from the normal timespec definition, which has to
use a '__kernel_long_t' tv_nsec for POSIX compliance. The difference
is harmless, because the bit layout has the lower 32 bits in the same
position, and the kernel zeroes the upper 32 bits on the syscall boundary.
I would just use
#define timespec64 __kernel_timespec
for the same effect, since that is what __kernel_timespec is meant for.
> +#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
> +# define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
> + syscall(__NR_futex, uaddr, op | opflags, val, timeout, uaddr2, val3)
> +#else
> +# define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
> + syscall(__NR_futex_time64, uaddr, op | opflags, val, timeout, uaddr2, val3)
> +#endif
The check for x32 user space looks correct here, but as I commented in
the other patch, I would keep it simple and use futex_time64() on all 32-bit
ABIs. There are approximately zero users of x32, and they don't benefit from
being able to run new perf binaries on pre-5.1 kernels when everyone else
can't.
Arnd