]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: use futexes with 64-bit time on Linux
authorDaniel Maslowski <info@orangecms.org>
Thu, 18 Sep 2025 15:43:42 +0000 (15:43 +0000)
committerJorropo <jorropo.pgm@gmail.com>
Fri, 19 Sep 2025 04:04:12 +0000 (21:04 -0700)
Linux introduced new syscalls to fix the year 2038 issue.
To still be able to use the old ones, the Kconfig option
COMPAT_32BIT_TIME would be necessary.

Use the new syscall with 64-bit values for futex by default.
Define _ENOSYS for detecting if it's not available.
Add a fallback to use the older syscall in case the new one is
not available, since Go runs on Linux from 2.6.32 on, per
https://go.dev/wiki/MinimumRequirements.

Updates #75133

Change-Id: I65daff0a3d06b55440ff05d8f5a9aa1c07eb201d
GitHub-Last-Rev: 96dd1bd84bd12d898e971157fc83da562cc4f6b4
GitHub-Pull-Request: golang/go#75306
Reviewed-on: https://go-review.googlesource.com/c/go/+/701615
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Mark Freeman <markfreeman@google.com>
Reviewed-by: Jorropo <jorropo.pgm@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

19 files changed:
src/runtime/defs2_linux.go
src/runtime/defs_linux.go
src/runtime/defs_linux_386.go
src/runtime/defs_linux_amd64.go
src/runtime/defs_linux_arm.go
src/runtime/defs_linux_arm64.go
src/runtime/defs_linux_loong64.go
src/runtime/defs_linux_mips64x.go
src/runtime/defs_linux_mipsx.go
src/runtime/defs_linux_ppc64.go
src/runtime/defs_linux_ppc64le.go
src/runtime/defs_linux_riscv64.go
src/runtime/defs_linux_s390x.go
src/runtime/os_linux.go
src/runtime/os_linux_futex32.go [new file with mode: 0644]
src/runtime/os_linux_futex64.go [new file with mode: 0644]
src/runtime/sys_linux_386.s
src/runtime/sys_linux_arm.s
src/runtime/sys_linux_mipsx.s

index 5d6730a7ad7fd8cf981d294e7c84f4eab148d986..597073c39d3d78dd427eb6c1670072b09efae7fd 100644 (file)
@@ -48,6 +48,7 @@ const (
        EINTR  = C.EINTR
        EAGAIN = C.EAGAIN
        ENOMEM = C.ENOMEM
+       ENOSYS = C.ENOSYS
 
        PROT_NONE  = C.PROT_NONE
        PROT_READ  = C.PROT_READ
index 296fcb4bfd6a7ad656365388b78e7c1aa8b5fe20..d2b619ecabc0e80f30c12f2da4b80a8fceb4a69a 100644 (file)
@@ -37,6 +37,7 @@ const (
        EINTR  = C.EINTR
        EAGAIN = C.EAGAIN
        ENOMEM = C.ENOMEM
+       ENOSYS = C.ENOSYS
 
        PROT_NONE  = C.PROT_NONE
        PROT_READ  = C.PROT_READ
index 5fef55610f39d2965933fd8d677fce56af357720..e902d8175c3b0dee71c4886162b6eb17cee59349 100644 (file)
@@ -9,6 +9,7 @@ const (
        _EINTR  = 0x4
        _EAGAIN = 0xb
        _ENOMEM = 0xc
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0x0
        _PROT_READ  = 0x1
@@ -136,16 +137,30 @@ type fpstate struct {
        anon0     [48]byte
 }
 
-type timespec struct {
+// The timespec structs and types are defined in Linux in
+// include/uapi/linux/time_types.h and include/uapi/asm-generic/posix_types.h.
+type timespec32 struct {
        tv_sec  int32
        tv_nsec int32
 }
 
 //go:nosplit
-func (ts *timespec) setNsec(ns int64) {
+func (ts *timespec32) setNsec(ns int64) {
        ts.tv_sec = timediv(ns, 1e9, &ts.tv_nsec)
 }
 
+type timespec struct {
+       tv_sec  int64
+       tv_nsec int64
+}
+
+//go:nosplit
+func (ts *timespec) setNsec(ns int64) {
+       var newNS int32
+       ts.tv_sec = int64(timediv(ns, 1e9, &newNS))
+       ts.tv_nsec = int64(newNS)
+}
+
 type timeval struct {
        tv_sec  int32
        tv_usec int32
@@ -223,8 +238,8 @@ type ucontext struct {
 }
 
 type itimerspec struct {
-       it_interval timespec
-       it_value    timespec
+       it_interval timespec32
+       it_value    timespec32
 }
 
 type itimerval struct {
index dce7799b6adc9cb2cdc1e7001775109a72943eb0..9a908c94000a588ede10f11037be57b008fca7e9 100644 (file)
@@ -9,6 +9,7 @@ const (
        _EINTR  = 0x4
        _EAGAIN = 0xb
        _ENOMEM = 0xc
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0x0
        _PROT_READ  = 0x1
index 71cf8c6d50e7609bb5fb492197afe3821e88c294..35c4faf96405d372867d79fddf5b6ef990ce4094 100644 (file)
@@ -11,6 +11,7 @@ const (
        _EINTR  = 0x4
        _ENOMEM = 0xc
        _EAGAIN = 0xb
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0
        _PROT_READ  = 0x1
@@ -95,16 +96,30 @@ const (
        _SOCK_DGRAM = 0x2
 )
 
-type timespec struct {
+// The timespec structs and types are defined in Linux in
+// include/uapi/linux/time_types.h and include/uapi/asm-generic/posix_types.h.
+type timespec32 struct {
        tv_sec  int32
        tv_nsec int32
 }
 
 //go:nosplit
-func (ts *timespec) setNsec(ns int64) {
+func (ts *timespec32) setNsec(ns int64) {
        ts.tv_sec = timediv(ns, 1e9, &ts.tv_nsec)
 }
 
+type timespec struct {
+       tv_sec  int64
+       tv_nsec int64
+}
+
+//go:nosplit
+func (ts *timespec) setNsec(ns int64) {
+       var newNS int32
+       ts.tv_sec = int64(timediv(ns, 1e9, &newNS))
+       ts.tv_nsec = int64(newNS)
+}
+
 type stackt struct {
        ss_sp    *byte
        ss_flags int32
@@ -155,8 +170,8 @@ func (tv *timeval) set_usec(x int32) {
 }
 
 type itimerspec struct {
-       it_interval timespec
-       it_value    timespec
+       it_interval timespec32
+       it_value    timespec32
 }
 
 type itimerval struct {
index 606cd70494e37ad9b23f81b2e8e3b286267f5269..4992e91ea68b8a139d5bbb247e694596ff3c0c93 100644 (file)
@@ -9,6 +9,7 @@ const (
        _EINTR  = 0x4
        _EAGAIN = 0xb
        _ENOMEM = 0xc
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0x0
        _PROT_READ  = 0x1
index b983725160faf7763dca1386a945379123e1811f..670d4c318dd6cc80187de4bb5bde14b1f4c6abba 100644 (file)
@@ -10,6 +10,7 @@ const (
        _EINTR  = 0x4
        _EAGAIN = 0xb
        _ENOMEM = 0xc
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0x0
        _PROT_READ  = 0x1
index 8a0af41234f22629343f24ebb0958f58d7078d33..7449d2cfac96d783c98b76d4f737459890a1396c 100644 (file)
@@ -12,6 +12,7 @@ const (
        _EINTR  = 0x4
        _EAGAIN = 0xb
        _ENOMEM = 0xc
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0x0
        _PROT_READ  = 0x1
index 8322beab2b1c20e536b425182fe8630c059078bb..cec504c885662b734b70198b5e6f26026b4b6871 100644 (file)
@@ -12,6 +12,7 @@ const (
        _EINTR  = 0x4
        _EAGAIN = 0xb
        _ENOMEM = 0xc
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0x0
        _PROT_READ  = 0x1
@@ -93,16 +94,30 @@ const (
        _SIGEV_THREAD_ID = 0x4
 )
 
-type timespec struct {
+// The timespec structs and types are defined in Linux in
+// include/uapi/linux/time_types.h and include/uapi/asm-generic/posix_types.h.
+type timespec32 struct {
        tv_sec  int32
        tv_nsec int32
 }
 
 //go:nosplit
-func (ts *timespec) setNsec(ns int64) {
+func (ts *timespec32) setNsec(ns int64) {
        ts.tv_sec = timediv(ns, 1e9, &ts.tv_nsec)
 }
 
+type timespec struct {
+       tv_sec  int64
+       tv_nsec int64
+}
+
+//go:nosplit
+func (ts *timespec) setNsec(ns int64) {
+       var newNS int32
+       ts.tv_sec = int64(timediv(ns, 1e9, &newNS))
+       ts.tv_nsec = int64(newNS)
+}
+
 type timeval struct {
        tv_sec  int32
        tv_usec int32
@@ -138,8 +153,8 @@ type siginfo struct {
 }
 
 type itimerspec struct {
-       it_interval timespec
-       it_value    timespec
+       it_interval timespec32
+       it_value    timespec32
 }
 
 type itimerval struct {
index f87924affe65ef190e95da2cf7bf768cb2c58c1e..dc45f37b7c3b251a42341c76dceca69c5938797e 100644 (file)
@@ -9,6 +9,7 @@ const (
        _EINTR  = 0x4
        _EAGAIN = 0xb
        _ENOMEM = 0xc
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0x0
        _PROT_READ  = 0x1
index f87924affe65ef190e95da2cf7bf768cb2c58c1e..dc45f37b7c3b251a42341c76dceca69c5938797e 100644 (file)
@@ -9,6 +9,7 @@ const (
        _EINTR  = 0x4
        _EAGAIN = 0xb
        _ENOMEM = 0xc
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0x0
        _PROT_READ  = 0x1
index 29b1ef2a50a94c9f6dd6be96b4e08ed03c4d4dad..b73e208ac3491ec4123ed9355867496f46a840bc 100644 (file)
@@ -10,6 +10,7 @@ const (
        _EINTR  = 0x4
        _EAGAIN = 0xb
        _ENOMEM = 0xc
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0x0
        _PROT_READ  = 0x1
index b0280213b3d12f6207ad44b83dd88edded9bd066..c03d0f21171a6196027b99f84ebe7db2a02f4b77 100644 (file)
@@ -10,6 +10,7 @@ const (
        _EINTR  = 0x4
        _EAGAIN = 0xb
        _ENOMEM = 0xc
+       _ENOSYS = 0x26
 
        _PROT_NONE  = 0x0
        _PROT_READ  = 0x1
index f9fe1b5f3373a14704cf6d4b493550c68402eea6..080dd9653232ed79289e25d9df7ccd64e22939d1 100644 (file)
@@ -40,9 +40,6 @@ type mOS struct {
        waitsema uint32 // semaphore for parking on locks
 }
 
-//go:noescape
-func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32
-
 // Linux futex.
 //
 //     futexsleep(uint32 *addr, uint32 val)
@@ -79,7 +76,7 @@ func futexsleep(addr *uint32, val uint32, ns int64) {
 
        var ts timespec
        ts.setNsec(ns)
-       futex(unsafe.Pointer(addr), _FUTEX_WAIT_PRIVATE, val, unsafe.Pointer(&ts), nil, 0)
+       futex(unsafe.Pointer(addr), _FUTEX_WAIT_PRIVATE, val, &ts, nil, 0)
 }
 
 // If any procs are sleeping on addr, wake up at most cnt.
diff --git a/src/runtime/os_linux_futex32.go b/src/runtime/os_linux_futex32.go
new file mode 100644 (file)
index 0000000..fdf99e5
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && (386 || arm || mips || mipsle || ppc)
+
+package runtime
+
+import (
+       "internal/runtime/atomic"
+       "unsafe"
+)
+
+//go:noescape
+func futex_time32(addr unsafe.Pointer, op int32, val uint32, ts *timespec32, addr2 unsafe.Pointer, val3 uint32) int32
+
+//go:noescape
+func futex_time64(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32
+
+var is32bitOnly atomic.Bool
+
+//go:nosplit
+func futex(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32 {
+       if !is32bitOnly.Load() {
+               ret := futex_time64(addr, op, val, ts, addr2, val3)
+               // futex_time64 is only supported on Linux 5.0+
+               if ret != -_ENOSYS {
+                       return ret
+               }
+               is32bitOnly.Store(true)
+       }
+       // Downgrade ts.
+       var ts32 timespec32
+       var pts32 *timespec32
+       if ts != nil {
+               ts32.setNsec(ts.tv_sec*1e9 + ts.tv_nsec)
+               pts32 = &ts32
+       }
+       return futex_time32(addr, op, val, pts32, addr2, val3)
+}
diff --git a/src/runtime/os_linux_futex64.go b/src/runtime/os_linux_futex64.go
new file mode 100644 (file)
index 0000000..487d0e0
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !(386 || arm || mips || mipsle || ppc || s390)
+
+package runtime
+
+import (
+       "unsafe"
+)
+
+//go:noescape
+func futex(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32
index 8e832687e05cd10a8a964a5065a70a975486a619..1c3f1ff3e6e575af14e96f4b753e6c75842823a8 100644 (file)
@@ -48,6 +48,7 @@
 #define SYS_madvise            219
 #define SYS_gettid             224
 #define SYS_futex              240
+#define SYS_futex_time64       422
 #define SYS_sched_getaffinity  242
 #define SYS_set_thread_area    243
 #define SYS_exit_group         252
@@ -532,9 +533,10 @@ TEXT runtime·madvise(SB),NOSPLIT,$0
        MOVL    AX, ret+12(FP)
        RET
 
+// Linux: kernel/futex/syscalls.c, requiring COMPAT_32BIT_TIME
 // int32 futex(int32 *uaddr, int32 op, int32 val,
-//     struct timespec *timeout, int32 *uaddr2, int32 val2);
-TEXT runtime·futex(SB),NOSPLIT,$0
+//     struct old_timespec32 *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time32(SB),NOSPLIT,$0
        MOVL    $SYS_futex, AX
        MOVL    addr+0(FP), BX
        MOVL    op+4(FP), CX
@@ -546,6 +548,21 @@ TEXT runtime·futex(SB),NOSPLIT,$0
        MOVL    AX, ret+24(FP)
        RET
 
+// Linux: kernel/futex/syscalls.c
+// int32 futex(int32 *uaddr, int32 op, int32 val,
+//     struct timespec *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time64(SB),NOSPLIT,$0
+       MOVL    $SYS_futex_time64, AX
+       MOVL    addr+0(FP), BX
+       MOVL    op+4(FP), CX
+       MOVL    val+8(FP), DX
+       MOVL    ts+12(FP), SI
+       MOVL    addr2+16(FP), DI
+       MOVL    val3+20(FP), BP
+       INVOKE_SYSCALL
+       MOVL    AX, ret+24(FP)
+       RET
+
 // int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
 TEXT runtime·clone(SB),NOSPLIT,$0
        MOVL    $SYS_clone, AX
index 992d32ab6c9b586a1e43f0b9953a8e8f58238621..44b56ccb9f009463de3d2cadc288f0c4bd915101 100644 (file)
@@ -30,6 +30,7 @@
 #define SYS_sigaltstack (SYS_BASE + 186)
 #define SYS_mmap2 (SYS_BASE + 192)
 #define SYS_futex (SYS_BASE + 240)
+#define SYS_futex_time64 (SYS_BASE + 422)
 #define SYS_exit_group (SYS_BASE + 248)
 #define SYS_munmap (SYS_BASE + 91)
 #define SYS_madvise (SYS_BASE + 220)
@@ -403,9 +404,10 @@ finish:
 
        RET
 
+// Linux: kernel/futex/syscalls.c, requiring COMPAT_32BIT_TIME
 // int32 futex(int32 *uaddr, int32 op, int32 val,
-//     struct timespec *timeout, int32 *uaddr2, int32 val2);
-TEXT runtime·futex(SB),NOSPLIT,$0
+//     struct old_timespec32 *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time32(SB),NOSPLIT,$0
        MOVW    addr+0(FP), R0
        MOVW    op+4(FP), R1
        MOVW    val+8(FP), R2
@@ -417,6 +419,21 @@ TEXT runtime·futex(SB),NOSPLIT,$0
        MOVW    R0, ret+24(FP)
        RET
 
+// Linux: kernel/futex/syscalls.c
+// int32 futex(int32 *uaddr, int32 op, int32 val,
+//     struct timespec *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time64(SB),NOSPLIT,$0
+       MOVW    addr+0(FP), R0
+       MOVW    op+4(FP), R1
+       MOVW    val+8(FP), R2
+       MOVW    ts+12(FP), R3
+       MOVW    addr2+16(FP), R4
+       MOVW    val3+20(FP), R5
+       MOVW    $SYS_futex_time64, R7
+       SWI     $0
+       MOVW    R0, ret+24(FP)
+       RET
+
 // int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
 TEXT runtime·clone(SB),NOSPLIT,$0
        MOVW    flags+0(FP), R0
index 5e6b6c150418d2ddfbf846541471553df80dfbfd..6f11841efc73e2ba73e8b1676d1de470defdcb00 100644 (file)
@@ -34,6 +34,7 @@
 #define SYS_mincore            4217
 #define SYS_gettid             4222
 #define SYS_futex              4238
+#define SYS_futex_time64       4422
 #define SYS_sched_getaffinity  4240
 #define SYS_exit_group         4246
 #define SYS_timer_create       4257
@@ -362,8 +363,10 @@ TEXT runtime·madvise(SB),NOSPLIT,$0-16
        MOVW    R2, ret+12(FP)
        RET
 
-// int32 futex(int32 *uaddr, int32 op, int32 val, struct timespec *timeout, int32 *uaddr2, int32 val2);
-TEXT runtime·futex(SB),NOSPLIT,$20-28
+// Linux: kernel/futex/syscalls.c, requiring COMPAT_32BIT_TIME
+// int32 futex(int32 *uaddr, int32 op, int32 val,
+//     struct old_timespec32 *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time32(SB),NOSPLIT,$20-28
        MOVW    addr+0(FP), R4
        MOVW    op+4(FP), R5
        MOVW    val+8(FP), R6
@@ -382,6 +385,27 @@ TEXT runtime·futex(SB),NOSPLIT,$20-28
        MOVW    R2, ret+24(FP)
        RET
 
+// Linux: kernel/futex/syscalls.c
+// int32 futex(int32 *uaddr, int32 op, int32 val,
+//     struct timespec *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex_time64(SB),NOSPLIT,$20-28
+       MOVW    addr+0(FP), R4
+       MOVW    op+4(FP), R5
+       MOVW    val+8(FP), R6
+       MOVW    ts+12(FP), R7
+
+       MOVW    addr2+16(FP), R8
+       MOVW    val3+20(FP), R9
+
+       MOVW    R8, 16(R29)
+       MOVW    R9, 20(R29)
+
+       MOVW    $SYS_futex_time64, R2
+       SYSCALL
+       BEQ     R7, 2(PC)
+       SUBU    R2, R0, R2      // caller expects negative errno
+       MOVW    R2, ret+24(FP)
+       RET
 
 // int32 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void));
 TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0-24