From eef7913c2f5512a954e658a5908a47dbc0ec8c2e Mon Sep 17 00:00:00 2001 From: Adhemerval Zanella Date: Tue, 15 Jun 2021 22:43:51 -0300 Subject: [PATCH] linux: Only use 64-bit syscall if required for semtimedop For !__ASSUME_TIME64_SYSCALLS there is no need to issue a 64-bit syscall if the provided timeout fits in a 32-bit one. The 64-bit usage should be rare since the timeout is a relative one. Checked on i686-linux-gnu on a 4.15 kernel and on a 5.11 kernel (with and without --enable-kernel=5.1) and on x86_64-linux-gnu. Reviewed-by: Lukasz Majewski --- sysdeps/unix/sysv/linux/semtimedop.c | 53 ++++++++++++++++------------ sysvipc/Makefile | 9 +++++ sysvipc/test-sysvsem.c | 22 +++++++++--- 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/sysdeps/unix/sysv/linux/semtimedop.c b/sysdeps/unix/sysv/linux/semtimedop.c index b732b6db48..d4fea4e55a 100644 --- a/sysdeps/unix/sysv/linux/semtimedop.c +++ b/sysdeps/unix/sysv/linux/semtimedop.c @@ -21,44 +21,51 @@ #include #include +static int +semtimedop_syscall (int semid, struct sembuf *sops, size_t nsops, + const struct __timespec64 *timeout) +{ +#ifdef __NR_semtimedop_time64 + return INLINE_SYSCALL_CALL (semtimedop_time64, semid, sops, nsops, timeout); +#elif defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS && defined __NR_semtimedop + return INLINE_SYSCALL_CALL (semtimedop, semid, sops, nsops, timeout); +#else + return INLINE_SYSCALL_CALL (ipc, IPCOP_semtimedop, semid, + SEMTIMEDOP_IPC_ARGS (nsops, sops, timeout)); +#endif +} + /* Perform user-defined atomical operation of array of semaphores. */ int __semtimedop64 (int semid, struct sembuf *sops, size_t nsops, const struct __timespec64 *timeout) { - int r; -#if defined __NR_semtimedop_time64 - r = INLINE_SYSCALL_CALL (semtimedop_time64, semid, sops, nsops, timeout); -#elif defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS && defined __NR_semtimedop - r = INLINE_SYSCALL_CALL (semtimedop, semid, sops, nsops, timeout); +#ifdef __ASSUME_TIME64_SYSCALLS + return semtimedop_syscall (semid, sops, nsops, timeout); #else - r = INLINE_SYSCALL_CALL (ipc, IPCOP_semtimedop, semid, - SEMTIMEDOP_IPC_ARGS (nsops, sops, timeout)); -#endif - -#ifndef __ASSUME_TIME64_SYSCALLS - if (r == 0 || errno != ENOSYS) - return r; + bool need_time64 = timeout != NULL && !in_time_t_range (timeout->tv_sec); + if (need_time64) + { + int r = semtimedop_syscall (semid, sops, nsops, timeout); + if (r == 0 || errno != ENOSYS) + return r; + __set_errno (EOVERFLOW); + return -1; + } struct timespec ts32, *pts32 = NULL; if (timeout != NULL) { - if (! in_time_t_range (timeout->tv_sec)) - { - __set_errno (EINVAL); - return -1; - } ts32 = valid_timespec64_to_timespec (*timeout); pts32 = &ts32; } -# if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS - r = INLINE_SYSCALL_CALL (semtimedop, semid, sops, nsops, pts32); +# ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS + return INLINE_SYSCALL_CALL (semtimedop, semid, sops, nsops, pts32); # else - r = INLINE_SYSCALL_CALL (ipc, IPCOP_semtimedop, semid, - SEMTIMEDOP_IPC_ARGS (nsops, sops, pts32)); + return INLINE_SYSCALL_CALL (ipc, IPCOP_semtimedop, semid, + SEMTIMEDOP_IPC_ARGS (nsops, sops, pts32)); # endif -#endif /* __ASSUME_TIME64_SYSCALLS */ - return r; +#endif } #if __TIMESIZE != 64 libc_hidden_def (__semtimedop64) diff --git a/sysvipc/Makefile b/sysvipc/Makefile index 86911803b5..d2acb6a70b 100644 --- a/sysvipc/Makefile +++ b/sysvipc/Makefile @@ -38,3 +38,12 @@ include ../Rules CFLAGS-msgrcv.c += -fexceptions -fasynchronous-unwind-tables CFLAGS-msgsnd.c += -fexceptions -fasynchronous-unwind-tables + +ifeq (yes,$(build-shared)) +librt = $(common-objpfx)rt/librt.so +else +librt = $(common-objpfx)rt/librt.a +endif + +$(objpfx)test-sysvsem: $(librt) +$(objpfx)test-sysvsem-time64: $(librt) diff --git a/sysvipc/test-sysvsem.c b/sysvipc/test-sysvsem.c index 092418205d..d9034c3dae 100644 --- a/sysvipc/test-sysvsem.c +++ b/sysvipc/test-sysvsem.c @@ -16,6 +16,7 @@ License along with the GNU C Library; if not, see . */ +#include #include #include #include @@ -30,6 +31,8 @@ #include #include #include +#include +#include /* These are for the temporary file we generate. */ static char *name; @@ -112,11 +115,20 @@ do_test (void) #ifdef _GNU_SOURCE /* Set a time for half a second. The semaphore operation should timeout with EAGAIN. */ - struct timespec ts = { 0 /* sec */, 500000000 /* nsec */ }; - if (semtimedop (semid, &sb2, 1, &ts) != -1 - || (errno != EAGAIN && errno != ENOSYS)) - FAIL_EXIT1 ("semtimedop succeed or returned errno != {EAGAIN,ENOSYS} " - "(errno=%i)", errno); + { + struct timespec ts = { 0 /* sec */, 500000000 /* nsec */ }; + if (semtimedop (semid, &sb2, 1, &ts) != -1 + || (errno != EAGAIN && errno != ENOSYS)) + FAIL_EXIT1 ("semtimedop succeed or returned errno != {EAGAIN,ENOSYS} " + "(errno=%i)", errno); + } + + { + support_create_timer (0, 100000000, false, NULL); + struct timespec ts = { TYPE_MAXIMUM (time_t), 0 }; + TEST_COMPARE (semtimedop (semid, &sb2, 1, &ts), -1); + TEST_VERIFY (errno == EINTR || errno == EOVERFLOW); + } #endif /* Finally free up the semnaphore resource. */