1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-07-29 11:41:21 +03:00

linux: Only use 64-bit syscall if required for select

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.  This also avoids the need
to use supports_time64() (which breaks the usage case of live migration
like CRIU or similar).

It also fixes an issue on 32-bit select call for !__ASSUME_PSELECT
(microblase with older kernels only) where the expected timeout
is a 'struct timeval' instead of 'struct timespec'.

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 <lukma@denx.de>
This commit is contained in:
Adhemerval Zanella
2021-06-15 21:00:50 -03:00
parent 91cf411ad3
commit 4c3df0eba5
4 changed files with 71 additions and 53 deletions

View File

@ -21,6 +21,11 @@ extern int __pselect32 (int __nfds, fd_set *__readfds,
const struct __timespec64 *__timeout, const struct __timespec64 *__timeout,
const __sigset_t *__sigmask) const __sigset_t *__sigmask)
attribute_hidden; attribute_hidden;
extern int __select32 (int __nfds, fd_set *__readfds,
fd_set *__writefds, fd_set *__exceptfds,
const struct __timespec64 *ts64,
struct __timeval64 *timeout)
attribute_hidden;
extern int __select64 (int __nfds, fd_set *__readfds, extern int __select64 (int __nfds, fd_set *__readfds,
fd_set *__writefds, fd_set *__exceptfds, fd_set *__writefds, fd_set *__exceptfds,

View File

@ -169,5 +169,7 @@ $(objpfx)tst-allocate_once-mem.out: $(objpfx)tst-allocate_once.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-allocate_once.mtrace > $@; \ $(common-objpfx)malloc/mtrace $(objpfx)tst-allocate_once.mtrace > $@; \
$(evaluate-test) $(evaluate-test)
$(objpfx)tst-select: $(librt)
$(objpfx)tst-select-time64: $(librt)
$(objpfx)tst-pselect: $(librt) $(objpfx)tst-pselect: $(librt)
$(objpfx)tst-pselect-time64: $(librt) $(objpfx)tst-pselect-time64: $(librt)

View File

@ -17,6 +17,7 @@
<https://www.gnu.org/licenses/>. */ <https://www.gnu.org/licenses/>. */
#include <errno.h> #include <errno.h>
#include <intprops.h>
#include <support/capture_subprocess.h> #include <support/capture_subprocess.h>
#include <support/check.h> #include <support/check.h>
#include <support/support.h> #include <support/support.h>
@ -31,12 +32,6 @@ struct child_args
struct timeval tmo; struct timeval tmo;
}; };
static void
alarm_handler (int signum)
{
/* Do nothing. */
}
static void static void
do_test_child (void *clousure) do_test_child (void *clousure)
{ {
@ -69,17 +64,20 @@ do_test_child (void *clousure)
static void static void
do_test_child_alarm (void *clousure) do_test_child_alarm (void *clousure)
{ {
struct sigaction act = { .sa_handler = alarm_handler }; struct child_args *args = (struct child_args *) clousure;
xsigaction (SIGALRM, &act, NULL);
alarm (1);
struct timeval tv = { .tv_sec = 10, .tv_usec = 0 }; support_create_timer (0, 100000000, false, NULL);
struct timeval tv = { .tv_sec = args->tmo.tv_sec, .tv_usec = 0 };
int r = select (0, NULL, NULL, NULL, &tv); int r = select (0, NULL, NULL, NULL, &tv);
TEST_COMPARE (r, -1); TEST_COMPARE (r, -1);
TEST_COMPARE (errno, EINTR); if (args->tmo.tv_sec > INT_MAX)
TEST_VERIFY (errno == EINTR || errno == EOVERFLOW);
if (support_select_modifies_timeout ()) else
TEST_VERIFY (tv.tv_sec < 10); {
TEST_COMPARE (errno, EINTR);
if (support_select_modifies_timeout ())
TEST_VERIFY (tv.tv_sec < args->tmo.tv_sec);
}
} }
static int static int
@ -121,13 +119,24 @@ do_test (void)
xclose (args.fds[0][0]); xclose (args.fds[0][0]);
xclose (args.fds[1][1]); xclose (args.fds[1][1]);
args.tmo = (struct timeval) { .tv_sec = 10, .tv_usec = 0 };
{ {
struct support_capture_subprocess result; struct support_capture_subprocess result;
result = support_capture_subprocess (do_test_child_alarm, NULL); result = support_capture_subprocess (do_test_child_alarm, &args);
support_capture_subprocess_check (&result, "tst-select-child", 0, support_capture_subprocess_check (&result, "tst-select-child", 0,
sc_allow_none); sc_allow_none);
} }
args.tmo = (struct timeval) { .tv_sec = TYPE_MAXIMUM (time_t),
.tv_usec = 0 };
{
struct support_capture_subprocess result;
result = support_capture_subprocess (do_test_child_alarm, &args);
support_capture_subprocess_check (&result, "tst-select-child", 0,
sc_allow_none);
}
args.tmo = (struct timeval) { .tv_sec = 0, .tv_usec = 0 };
{ {
fd_set rfds; fd_set rfds;
FD_ZERO (&rfds); FD_ZERO (&rfds);

View File

@ -21,7 +21,6 @@
#include <sys/select.h> #include <sys/select.h>
#include <errno.h> #include <errno.h>
#include <sysdep-cancel.h> #include <sysdep-cancel.h>
#include <time64-support.h>
/* Check the first NFDS descriptors each in READFDS (if not NULL) for read /* Check the first NFDS descriptors each in READFDS (if not NULL) for read
readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS
@ -65,53 +64,56 @@ __select64 (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
#ifndef __NR_pselect6_time64 #ifndef __NR_pselect6_time64
# define __NR_pselect6_time64 __NR_pselect6 # define __NR_pselect6_time64 __NR_pselect6
#endif #endif
int r;
if (supports_time64 ())
{
r = SYSCALL_CANCEL (pselect6_time64, nfds, readfds, writefds, exceptfds,
pts64, NULL);
/* Linux by default will update the timeout after a pselect6 syscall
(though the pselect() glibc call suppresses this behavior).
Since select() on Linux has the same behavior as the pselect6
syscall, we update the timeout here. */
if (r >= 0 || errno != ENOSYS)
{
if (timeout != NULL)
TIMESPEC_TO_TIMEVAL (timeout, &ts64);
return r;
}
mark_time64_unsupported (); #ifdef __ASSUME_TIME64_SYSCALLS
int r = SYSCALL_CANCEL (pselect6_time64, nfds, readfds, writefds, exceptfds,
pts64, NULL);
if (timeout != NULL)
TIMESPEC_TO_TIMEVAL (timeout, pts64);
return r;
#else
bool need_time64 = timeout != NULL && !in_time_t_range (timeout->tv_sec);
if (need_time64)
{
int r = SYSCALL_CANCEL (pselect6_time64, nfds, readfds, writefds,
exceptfds, pts64, NULL);
if ((r >= 0 || errno != ENOSYS) && timeout != NULL)
{
TIMESPEC_TO_TIMEVAL (timeout, &ts64);
}
else
__set_errno (EOVERFLOW);
return r;
} }
#ifndef __ASSUME_TIME64_SYSCALLS # ifdef __ASSUME_PSELECT
struct timespec ts32, *pts32 = NULL; struct timespec ts32, *pts32 = NULL;
if (pts64 != NULL) if (pts64 != NULL)
{ {
if (! in_time_t_range (pts64->tv_sec)) ts32.tv_sec = pts64->tv_sec;
{ ts32.tv_nsec = pts64->tv_nsec;
__set_errno (EINVAL);
return -1;
}
ts32.tv_sec = s;
ts32.tv_nsec = ns;
pts32 = &ts32; pts32 = &ts32;
} }
# ifndef __ASSUME_PSELECT
# ifdef __NR__newselect
# undef __NR_select
# define __NR_select __NR__newselect
# endif
r = SYSCALL_CANCEL (select, nfds, readfds, writefds, exceptfds, pts32);
# else
r = SYSCALL_CANCEL (pselect6, nfds, readfds, writefds, exceptfds, pts32,
NULL);
# endif
if (timeout != NULL)
*timeout = valid_timespec_to_timeval64 (ts32);
#endif
int r = SYSCALL_CANCEL (pselect6, nfds, readfds, writefds, exceptfds, pts32,
NULL);
if (timeout != NULL)
TIMESPEC_TO_TIMEVAL (timeout, pts32);
return r; return r;
# else
struct timeval tv32, *ptv32 = NULL;
if (pts64 != NULL)
{
tv32 = valid_timespec64_to_timeval (*pts64);
ptv32 = &tv32;
}
int r = SYSCALL_CANCEL (select, nfds, readfds, writefds, exceptfds, ptv32);
if (timeout != NULL)
*timeout = valid_timeval_to_timeval64 (tv32);
return r;
# endif /* __ASSUME_PSELECT */
#endif
} }
#if __TIMESIZE != 64 #if __TIMESIZE != 64