]> Cypherpunks repositories - gostls13.git/commitdiff
internal/syscall/unix: add KernelVersionGE
authorKir Kolyshkin <kolyshkin@gmail.com>
Wed, 3 Sep 2025 23:12:39 +0000 (16:12 -0700)
committerKirill Kolyshkin <kolyshkin@gmail.com>
Mon, 15 Sep 2025 18:46:39 +0000 (11:46 -0700)
There are a few places in the code which checks that the running kernel
is greater than or equal to x.y. The check takes a few lines and the
checking code is somewhat distracting.

Let's abstract this check into a simple function, KernelVersionGE,
and convert the users accordingly.

Add a test case (I'm not sure it has much value, can be dropped).

Change-Id: I8ec91dcc7452363361f95e46794701c0ae57d956
Reviewed-on: https://go-review.googlesource.com/c/go/+/700796
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Mark Freeman <markfreeman@google.com>
src/internal/poll/copy_file_range_linux.go
src/internal/syscall/unix/kernel_version_freebsd.go
src/internal/syscall/unix/kernel_version_ge.go [new file with mode: 0644]
src/internal/syscall/unix/kernel_version_ge_test.go [new file with mode: 0644]
src/internal/syscall/unix/kernel_version_solaris.go
src/net/mptcpsock_linux.go
src/net/sock_linux.go
src/net/sock_linux_test.go
src/os/exec/lp_linux_test.go
src/runtime/pprof/pprof_test.go

index edaf60fe95c934c333d9610027cec0308372785e..d7cbd984651678303a66e75a2aa851f5c8779f63 100644 (file)
@@ -10,16 +10,11 @@ import (
        "syscall"
 )
 
-func supportCopyFileRange() bool {
-       return isKernelVersionGE53()
-}
-
-var isKernelVersionGE53 = sync.OnceValue(func() bool {
-       major, minor := unix.KernelVersion()
+var supportCopyFileRange = sync.OnceValue(func() bool {
        // copy_file_range(2) is broken in various ways on kernels older than 5.3,
        // see https://go.dev/issue/42400 and
        // https://man7.org/linux/man-pages/man2/copy_file_range.2.html#VERSIONS
-       return major > 5 || (major == 5 && minor >= 3)
+       return unix.KernelVersionGE(5, 3)
 })
 
 // For best performance, call copy_file_range() with the largest len value
index ef9ee136f3e8db2735ebd42e217dfa622aae61c3..db3519ddfb70f6ffbbe57876afa8fc43a607cc3c 100644 (file)
@@ -42,7 +42,9 @@ func KernelVersion() (major, minor int) {
 // This function will examine both the kernel version and the availability of the system call.
 var SupportCopyFileRange = sync.OnceValue(func() bool {
        // The copy_file_range() function first appeared in FreeBSD 13.0.
-       major, _ := KernelVersion()
+       if !KernelVersionGE(13, 0) {
+               return false
+       }
        _, err := CopyFileRange(0, nil, 0, nil, 0, 0)
-       return major >= 13 && err != syscall.ENOSYS
+       return err != syscall.ENOSYS
 })
diff --git a/src/internal/syscall/unix/kernel_version_ge.go b/src/internal/syscall/unix/kernel_version_ge.go
new file mode 100644 (file)
index 0000000..a142404
--- /dev/null
@@ -0,0 +1,13 @@
+// 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.
+
+package unix
+
+// KernelVersionGE checks if the running kernel version
+// is greater than or equal to the provided version.
+func KernelVersionGE(x, y int) bool {
+       xx, yy := KernelVersion()
+
+       return xx > x || (xx == x && yy >= y)
+}
diff --git a/src/internal/syscall/unix/kernel_version_ge_test.go b/src/internal/syscall/unix/kernel_version_ge_test.go
new file mode 100644 (file)
index 0000000..f4b1b23
--- /dev/null
@@ -0,0 +1,67 @@
+// 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.
+
+package unix_test
+
+import (
+       "internal/syscall/unix"
+       "testing"
+)
+
+func TestKernelVersionGE(t *testing.T) {
+       major, minor := unix.KernelVersion()
+       t.Logf("Running on kernel %d.%d", major, minor)
+
+       tests := []struct {
+               name string
+               x, y int
+               want bool
+       }{
+               {
+                       name: "current version equals itself",
+                       x:    major,
+                       y:    minor,
+                       want: true,
+               },
+               {
+                       name: "older major version",
+                       x:    major - 1,
+                       y:    0,
+                       want: true,
+               },
+               {
+                       name: "same major, older minor version",
+                       x:    major,
+                       y:    minor - 1,
+                       want: true,
+               },
+               {
+                       name: "newer major version",
+                       x:    major + 1,
+                       y:    0,
+                       want: false,
+               },
+               {
+                       name: "same major, newer minor version",
+                       x:    major,
+                       y:    minor + 1,
+                       want: false,
+               },
+               {
+                       name: "min version (0.0)",
+                       x:    0,
+                       y:    0,
+                       want: true,
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got := unix.KernelVersionGE(tt.x, tt.y)
+                       if got != tt.want {
+                               t.Errorf("KernelVersionGE(%d, %d): got %v, want %v", tt.x, tt.y, got, tt.want)
+                       }
+               })
+       }
+}
index 3f399411d76bdeed083b5b7e9cc43e4b5b15cd86..7904e1f4295707bcd4a531ecd22320ea611d2015 100644 (file)
@@ -77,11 +77,10 @@ var SupportSockNonblockCloexec = sync.OnceValue(func() bool {
        }
        if err != syscall.EPROTONOSUPPORT && err != syscall.EINVAL {
                // Something wrong with socket(), fall back to checking the kernel version.
-               major, minor := KernelVersion()
                if runtime.GOOS == "illumos" {
-                       return major > 5 || (major == 5 && minor >= 11) // minimal requirement is SunOS 5.11
+                       return KernelVersionGE(5, 11) // Minimal requirement is SunOS 5.11.
                }
-               return major > 11 || (major == 11 && minor >= 4)
+               return KernelVersionGE(11, 4)
        }
        return false
 })
@@ -101,6 +100,5 @@ var SupportAccept4 = sync.OnceValue(func() bool {
 // SupportTCPKeepAliveIdleIntvlCNT determines whether the TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT
 // are available by checking the kernel version for Solaris 11.4.
 var SupportTCPKeepAliveIdleIntvlCNT = sync.OnceValue(func() bool {
-       major, minor := KernelVersion()
-       return major > 11 || (major == 11 && minor >= 4)
+       return KernelVersionGE(11, 4)
 })
index 422309048543199646ea1944981a6273519efe08..5b58418653fbf51cdc68024b1302106bd6a9e38c 100644 (file)
@@ -53,9 +53,8 @@ func initMPTCPavailable() {
                mptcpAvailable = true
        }
 
-       major, minor := unix.KernelVersion()
-       // SOL_MPTCP only supported from kernel 5.16
-       hasSOLMPTCP = major > 5 || (major == 5 && minor >= 16)
+       // SOL_MPTCP only supported from kernel 5.16.
+       hasSOLMPTCP = unix.KernelVersionGE(5, 16)
 }
 
 func (sd *sysDialer) dialMPTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) {
index cffe9a236fc7f13da6d0085923dc58102582aa7b..380a8880d1a779702205747ccfc2b87a46fc0d9f 100644 (file)
@@ -18,9 +18,8 @@ import (
 //
 // See issue 5030 and 41470.
 func maxAckBacklog(n int) int {
-       major, minor := unix.KernelVersion()
        size := 16
-       if major > 4 || (major == 4 && minor >= 1) {
+       if unix.KernelVersionGE(4, 1) {
                size = 32
        }
 
index 11303cfff1e64670c098cc48e060a152dcd1bfa3..9696cddce9ff53fe51ab156f43d420e2a12e53f9 100644 (file)
@@ -11,13 +11,12 @@ import (
 
 func TestMaxAckBacklog(t *testing.T) {
        n := 196602
-       major, minor := unix.KernelVersion()
        backlog := maxAckBacklog(n)
        expected := 1<<16 - 1
-       if major > 4 || (major == 4 && minor >= 1) {
+       if unix.KernelVersionGE(4, 1) {
                expected = n
        }
        if backlog != expected {
-               t.Fatalf(`Kernel version: "%d.%d", sk_max_ack_backlog mismatch, got %d, want %d`, major, minor, backlog, expected)
+               t.Fatalf(`sk_max_ack_backlog mismatch, got %d, want %d`, backlog, expected)
        }
 }
index a7f9aa24b8cdbc2feaf200cc4bbb397c2a29247d..1436763038a73d456e4f3864074ddaafa24956cb 100644 (file)
@@ -19,7 +19,7 @@ func TestFindExecutableVsNoexec(t *testing.T) {
        t.Parallel()
 
        // This test case relies on faccessat2(2) syscall, which appeared in Linux v5.8.
-       if major, minor := unix.KernelVersion(); major < 5 || (major == 5 && minor < 8) {
+       if !unix.KernelVersionGE(5, 8) {
                t.Skip("requires Linux kernel v5.8 with faccessat2(2) syscall")
        }
 
index 99c5155806da63ba57dd4f96f1d541490b51589d..25a2f3b32415d701f4ac839137fe90d7fe88b338 100644 (file)
@@ -117,10 +117,6 @@ func TestCPUProfileMultithreadMagnitude(t *testing.T) {
                t.Skip("issue 35057 is only confirmed on Linux")
        }
 
-       // Linux [5.9,5.16) has a kernel bug that can break CPU timers on newly
-       // created threads, breaking our CPU accounting.
-       major, minor := unix.KernelVersion()
-       t.Logf("Running on Linux %d.%d", major, minor)
        defer func() {
                if t.Failed() {
                        t.Logf("Failure of this test may indicate that your system suffers from a known Linux kernel bug fixed on newer kernels. See https://golang.org/issue/49065.")
@@ -131,9 +127,9 @@ func TestCPUProfileMultithreadMagnitude(t *testing.T) {
        // it enabled to potentially warn users that they are on a broken
        // kernel.
        if testenv.Builder() != "" && (runtime.GOARCH == "386" || runtime.GOARCH == "amd64") {
-               have59 := major > 5 || (major == 5 && minor >= 9)
-               have516 := major > 5 || (major == 5 && minor >= 16)
-               if have59 && !have516 {
+               // Linux [5.9,5.16) has a kernel bug that can break CPU timers on newly
+               // created threads, breaking our CPU accounting.
+               if unix.KernelVersionGE(5, 9) && !unix.KernelVersionGE(5, 16) {
                        testenv.SkipFlaky(t, 49065)
                }
        }