[PATCH 1/7] tools/nolibc: remove __nolibc_enosys() fallback from time64-related functions

Thomas Weißschuh posted 7 patches 5 months, 3 weeks ago
[PATCH 1/7] tools/nolibc: remove __nolibc_enosys() fallback from time64-related functions
Posted by Thomas Weißschuh 5 months, 3 weeks ago
These fallbacks where added when no explicit fallbacks for time64 was
implemented. Now that these fallbacks are in place, the additional
fallback to __nolibc_enosys() is superfluous.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 tools/include/nolibc/poll.h        | 4 +---
 tools/include/nolibc/sys.h         | 4 +---
 tools/include/nolibc/sys/timerfd.h | 8 ++------
 tools/include/nolibc/time.h        | 8 ++------
 4 files changed, 6 insertions(+), 18 deletions(-)

diff --git a/tools/include/nolibc/poll.h b/tools/include/nolibc/poll.h
index 1765acb17ea01ff53cbad0b4750e4938446b6a45..0d053f93ea99b0363b4bdfd2c400d6df2f932684 100644
--- a/tools/include/nolibc/poll.h
+++ b/tools/include/nolibc/poll.h
@@ -39,10 +39,8 @@ int sys_poll(struct pollfd *fds, int nfds, int timeout)
 		t.tv_nsec = (timeout % 1000) * 1000000;
 	}
 	return my_syscall5(__NR_ppoll_time64, fds, nfds, (timeout >= 0) ? &t : NULL, NULL, 0);
-#elif defined(__NR_poll)
-	return my_syscall3(__NR_poll, fds, nfds, timeout);
 #else
-	return __nolibc_enosys(__func__, fds, nfds, timeout);
+	return my_syscall3(__NR_poll, fds, nfds, timeout);
 #endif
 }
 
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h
index 295e71d34abadb7f9c7ca995012b4395b6830975..4e5389edda03a61e76ad0377213e8ef0a621d300 100644
--- a/tools/include/nolibc/sys.h
+++ b/tools/include/nolibc/sys.h
@@ -801,7 +801,7 @@ int sys_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeva
 		t.tv_nsec = timeout->tv_usec * 1000;
 	}
 	return my_syscall6(__NR_pselect6, nfds, rfds, wfds, efds, timeout ? &t : NULL, NULL);
-#elif defined(__NR_pselect6_time64)
+#else
 	struct __kernel_timespec t;
 
 	if (timeout) {
@@ -809,8 +809,6 @@ int sys_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeva
 		t.tv_nsec = timeout->tv_usec * 1000;
 	}
 	return my_syscall6(__NR_pselect6_time64, nfds, rfds, wfds, efds, timeout ? &t : NULL, NULL);
-#else
-	return __nolibc_enosys(__func__, nfds, rfds, wfds, efds, timeout);
 #endif
 }
 
diff --git a/tools/include/nolibc/sys/timerfd.h b/tools/include/nolibc/sys/timerfd.h
index 4375d546ba58f849b3ba48e7b221e6ec3c15fcf6..5dd61030c9914d2b6b2a71130d07a32ef7c37781 100644
--- a/tools/include/nolibc/sys/timerfd.h
+++ b/tools/include/nolibc/sys/timerfd.h
@@ -34,7 +34,7 @@ int sys_timerfd_gettime(int fd, struct itimerspec *curr_value)
 {
 #if defined(__NR_timerfd_gettime)
 	return my_syscall2(__NR_timerfd_gettime, fd, curr_value);
-#elif defined(__NR_timerfd_gettime64)
+#else
 	struct __kernel_itimerspec kcurr_value;
 	int ret;
 
@@ -42,8 +42,6 @@ int sys_timerfd_gettime(int fd, struct itimerspec *curr_value)
 	__nolibc_timespec_kernel_to_user(&kcurr_value.it_interval, &curr_value->it_interval);
 	__nolibc_timespec_kernel_to_user(&kcurr_value.it_value, &curr_value->it_value);
 	return ret;
-#else
-	return __nolibc_enosys(__func__, fd, curr_value);
 #endif
 }
 
@@ -60,7 +58,7 @@ int sys_timerfd_settime(int fd, int flags,
 {
 #if defined(__NR_timerfd_settime)
 	return my_syscall4(__NR_timerfd_settime, fd, flags, new_value, old_value);
-#elif defined(__NR_timerfd_settime64)
+#else
 	struct __kernel_itimerspec knew_value, kold_value;
 	int ret;
 
@@ -72,8 +70,6 @@ int sys_timerfd_settime(int fd, int flags,
 		__nolibc_timespec_kernel_to_user(&kold_value.it_value, &old_value->it_value);
 	}
 	return ret;
-#else
-	return __nolibc_enosys(__func__, fd, flags, new_value, old_value);
 #endif
 }
 
diff --git a/tools/include/nolibc/time.h b/tools/include/nolibc/time.h
index e9c1b976791a65c0d73268bebbcfd4f2a57a47ee..6c276b8d646a4e8fd84162b6cb74c73649a092c2 100644
--- a/tools/include/nolibc/time.h
+++ b/tools/include/nolibc/time.h
@@ -45,7 +45,7 @@ int sys_clock_getres(clockid_t clockid, struct timespec *res)
 {
 #if defined(__NR_clock_getres)
 	return my_syscall2(__NR_clock_getres, clockid, res);
-#elif defined(__NR_clock_getres_time64)
+#else
 	struct __kernel_timespec kres;
 	int ret;
 
@@ -53,8 +53,6 @@ int sys_clock_getres(clockid_t clockid, struct timespec *res)
 	if (res)
 		__nolibc_timespec_kernel_to_user(&kres, res);
 	return ret;
-#else
-	return __nolibc_enosys(__func__, clockid, res);
 #endif
 }
 
@@ -69,7 +67,7 @@ int sys_clock_gettime(clockid_t clockid, struct timespec *tp)
 {
 #if defined(__NR_clock_gettime)
 	return my_syscall2(__NR_clock_gettime, clockid, tp);
-#elif defined(__NR_clock_gettime64)
+#else
 	struct __kernel_timespec ktp;
 	int ret;
 
@@ -77,8 +75,6 @@ int sys_clock_gettime(clockid_t clockid, struct timespec *tp)
 	if (tp)
 		__nolibc_timespec_kernel_to_user(&ktp, tp);
 	return ret;
-#else
-	return __nolibc_enosys(__func__, clockid, tp);
 #endif
 }
 

-- 
2.50.1

Re: [PATCH 1/7] tools/nolibc: remove __nolibc_enosys() fallback from time64-related functions
Posted by Arnd Bergmann 4 months, 1 week ago
On Thu, Aug 21, 2025, at 17:40, Thomas Weißschuh wrote:
> These fallbacks where added when no explicit fallbacks for time64 was
> implemented. Now that these fallbacks are in place, the additional
> fallback to __nolibc_enosys() is superfluous.
>
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>

Hi Thomas,

I just saw these fly by as they made it into mainline and
I noticed that there is still something wrong here:

> @@ -39,10 +39,8 @@ int sys_poll(struct pollfd *fds, int nfds, int 
> timeout)
>  		t.tv_nsec = (timeout % 1000) * 1000000;
>  	}
>  	return my_syscall5(__NR_ppoll_time64, fds, nfds, (timeout >= 0) ? &t 
> : NULL, NULL, 0);
> -#elif defined(__NR_poll)
> -	return my_syscall3(__NR_poll, fds, nfds, timeout);
>  #else
> -	return __nolibc_enosys(__func__, fds, nfds, timeout);
> +	return my_syscall3(__NR_poll, fds, nfds, timeout);
>  #endif
>  }

The change is fine, as there is always at least one of the
time64 or the old syscalls implemented, for any of the affected
calls.

However, the problem here is the default to the old time
types on 32-bit targets, for two reasons:

 - this fails when turning off CONFIG_COMPAT_32BIT_TIME
 - the old types are often too short, both for the y2038
   overflow and for the file system types.

I suspect the problem is that the kernel's uapi/linux/time.h
still defines the old types as the default, and nolibc
historically just picks it up from there.

The proper solution I think would be to do the same thing
that klibc has and use sane defaults for all the types
along with the modern syscalls. At least __kernel_time_t,
__kernel_timespec, __kernel_ino_t, __kernel_loff_t and
probably a few more. We should also consider drop the
legacy type definitions from uapi/linux/time.h and
require each libc to define their own.

      Arnd
Re: [PATCH 1/7] tools/nolibc: remove __nolibc_enosys() fallback from time64-related functions
Posted by Thomas Weißschuh 4 months ago
Hi Arnd,

On 2025-10-01 09:43:37+0200, Arnd Bergmann wrote:
> On Thu, Aug 21, 2025, at 17:40, Thomas Weißschuh wrote:
> > These fallbacks where added when no explicit fallbacks for time64 was
> > implemented. Now that these fallbacks are in place, the additional
> > fallback to __nolibc_enosys() is superfluous.
> >
> > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> 
> I just saw these fly by as they made it into mainline and
> I noticed that there is still something wrong here:
> 
> > @@ -39,10 +39,8 @@ int sys_poll(struct pollfd *fds, int nfds, int 
> > timeout)
> >  		t.tv_nsec = (timeout % 1000) * 1000000;
> >  	}
> >  	return my_syscall5(__NR_ppoll_time64, fds, nfds, (timeout >= 0) ? &t 
> > : NULL, NULL, 0);
> > -#elif defined(__NR_poll)
> > -	return my_syscall3(__NR_poll, fds, nfds, timeout);
> >  #else
> > -	return __nolibc_enosys(__func__, fds, nfds, timeout);
> > +	return my_syscall3(__NR_poll, fds, nfds, timeout);
> >  #endif
> >  }
> 
> The change is fine, as there is always at least one of the
> time64 or the old syscalls implemented, for any of the affected
> calls.
> 
> However, the problem here is the default to the old time
> types on 32-bit targets, for two reasons:
> 
>  - this fails when turning off CONFIG_COMPAT_32BIT_TIME

Good point.

>  - the old types are often too short, both for the y2038
>    overflow and for the file system types.

So far this was not something we actively tried to support,
especially with the restriction mentioned below.

> I suspect the problem is that the kernel's uapi/linux/time.h
> still defines the old types as the default, and nolibc
> historically just picks it up from there.

So far we have tried to keep nolibc compatible with the kernel UAPI when
included in any order. This forced us to use 'struct timespec' from
uapi/linux/time.h. With the upcoming implementation of signals in nolibc
this guideline is relaxed a bit, so we should be able to use our own
always-64-bit 'struct timespec'.

> The proper solution I think would be to do the same thing
> that klibc has and use sane defaults for all the types
> along with the modern syscalls. At least __kernel_time_t,
> __kernel_timespec, __kernel_ino_t, __kernel_loff_t and
> probably a few more.

Thanks for the pointers and ack to all of them.
I'll take a look at those.

> We should also consider drop the
> legacy type definitions from uapi/linux/time.h and
> require each libc to define their own.

Can we even just drop them? Or should they also get some backwards
compat guards?


Thomas
Re: [PATCH 1/7] tools/nolibc: remove __nolibc_enosys() fallback from time64-related functions
Posted by Arnd Bergmann 4 months ago
On Mon, Oct 6, 2025, at 22:14, Thomas Weißschuh wrote:
> On 2025-10-01 09:43:37+0200, Arnd Bergmann wrote:
>> On Thu, Aug 21, 2025, at 17:40, Thomas Weißschuh wrote:
>>  - the old types are often too short, both for the y2038
>>    overflow and for the file system types.
>
> So far this was not something we actively tried to support,
> especially with the restriction mentioned below.
>
>> I suspect the problem is that the kernel's uapi/linux/time.h
>> still defines the old types as the default, and nolibc
>> historically just picks it up from there.
>
> So far we have tried to keep nolibc compatible with the kernel UAPI when
> included in any order. This forced us to use 'struct timespec' from
> uapi/linux/time.h. With the upcoming implementation of signals in nolibc
> this guideline is relaxed a bit, so we should be able to use our own
> always-64-bit 'struct timespec'.

You can probably either "#define timespec __kernel_timespec" or
"#define __kernel_timespec timespec" before including
linux/time_types.h.

Note that there is no time64 variant of "struct timeval", so
any syscall that needs this has to be implemented in userspace
as a wrapper around the timespec based one, e.g. gettimeofday()
needs to call clock_gettime() on all 32-bit systems.

>> We should also consider drop the
>> legacy type definitions from uapi/linux/time.h and
>> require each libc to define their own.
>
> Can we even just drop them? Or should they also get some backwards
> compat guards?

This is the big question, and we kind of left this one open
to be decided later when we finished the actual binary interface.

I think simply dropping the old definition is one of several
options we have, because that does not change the ABI in an
incompatible way and just requires the few user space sources
that use this to either require old kernel headers or make
simple source-level changes that they should have done for
portability anyway.

I see multiple decisions we have to make in that option space
once we decide to do anything:

- do we change the headers for both 32-bit and 64-bit userspace
  for consistency, or only for 32-bit userspace to limit
  the impact to those users that care about 32-bit?

- do we remove only the type definitions (timespec, timeval,
  itimerspec, itimerval, timex) or also the syscall macros
  for the time32 syscalls using them?

- What method (if any) would be used to choose between the
  time32 definitions, the time64 definitions or none of them
  when including the kernel headers?

      Arnd