"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
// 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
})
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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)
+ }
+ })
+ }
+}
}
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
})
// 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)
})
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) {
//
// 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
}
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)
}
}
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")
}
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.")
// 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)
}
}