Currently, there are 3 functions returning Linux kernel version numbers.
Two of them are identical:
- in net, initially added by commit
0a9dd47dd817904e;
- in internal/poll, initially added by commit
1c7650aa93bd53;
(both were later fixed by commit
66c02645062561a).
The third one is a more complex, regexp-based implementation in
runtime/pprof, which is only used for a test.
Instead of adding one more, let's consolidate existing ones.
Remove the complex implementation, and move the simple one into
internal/syscall/unix. Use it from all the three places mentioned above.
Change-Id: I4a34d9ca47257743c16def30e4dd634e36056091
Reviewed-on: https://go-review.googlesource.com/c/go/+/424896
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Run-TryBot: Kirill Kolyshkin <kolyshkin@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
const maxCopyFileRangeRound = 1 << 30
-func kernelVersion() (major int, minor int) {
- var uname syscall.Utsname
- if err := syscall.Uname(&uname); err != nil {
- return
- }
-
- rl := uname.Release
- var values [2]int
- vi := 0
- value := 0
- for _, c := range rl {
- if '0' <= c && c <= '9' {
- value = (value * 10) + int(c-'0')
- } else {
- // Note that we're assuming N.N.N here. If we see anything else we are likely to
- // mis-parse it.
- values[vi] = value
- vi++
- if vi >= len(values) {
- break
- }
- value = 0
- }
- }
- switch vi {
- case 0:
- return 0, 0
- case 1:
- return values[0], 0
- case 2:
- return values[0], values[1]
- }
- return
-}
-
// CopyFileRange copies at most remain bytes of data from src to dst, using
// the copy_file_range system call. dst and src must refer to regular files.
func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err error) {
kernelVersion53Once.Do(func() {
- major, minor := kernelVersion()
+ major, minor := unix.KernelVersion()
// copy_file_range(2) is broken in various ways on kernels older than 5.3,
// see issue #42400 and
// https://man7.org/linux/man-pages/man2/copy_file_range.2.html#VERSIONS
--- /dev/null
+// Copyright 2022 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
+
+import (
+ "syscall"
+)
+
+// KernelVersion returns major and minor kernel version numbers, parsed from
+// the syscall.Uname's Release field, or 0, 0 if the version can't be obtained
+// or parsed.
+//
+// Currently only implemented for Linux.
+func KernelVersion() (major int, minor int) {
+ var uname syscall.Utsname
+ if err := syscall.Uname(&uname); err != nil {
+ return
+ }
+
+ rl := uname.Release
+ var values [2]int
+ vi := 0
+ value := 0
+ for _, c := range rl {
+ if '0' <= c && c <= '9' {
+ value = (value * 10) + int(c-'0')
+ } else {
+ // Note that we're assuming N.N.N here.
+ // If we see anything else, we are likely to mis-parse it.
+ values[vi] = value
+ vi++
+ if vi >= len(values) {
+ break
+ }
+ value = 0
+ }
+ }
+ switch vi {
+ case 0:
+ return 0, 0
+ case 1:
+ return values[0], 0
+ case 2:
+ return values[0], values[1]
+ }
+ return
+}
--- /dev/null
+// Copyright 2022 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
+
+package unix
+
+func KernelVersion() (major int, minor int) {
+ return 0, 0
+}
package net
-import "syscall"
-
-func kernelVersion() (major int, minor int) {
- var uname syscall.Utsname
- if err := syscall.Uname(&uname); err != nil {
- return
- }
-
- rl := uname.Release
- var values [2]int
- vi := 0
- value := 0
- for _, c := range rl {
- if c >= '0' && c <= '9' {
- value = (value * 10) + int(c-'0')
- } else {
- // Note that we're assuming N.N.N here. If we see anything else we are likely to
- // mis-parse it.
- values[vi] = value
- vi++
- if vi >= len(values) {
- break
- }
- value = 0
- }
- }
- switch vi {
- case 0:
- return 0, 0
- case 1:
- return values[0], 0
- case 2:
- return values[0], values[1]
- }
- return
-}
+import (
+ "internal/syscall/unix"
+ "syscall"
+)
// Linux stores the backlog as:
//
//
// See issue 5030 and 41470.
func maxAckBacklog(n int) int {
- major, minor := kernelVersion()
+ major, minor := unix.KernelVersion()
size := 16
if major > 4 || (major == 4 && minor >= 1) {
size = 32
package net
import (
+ "internal/syscall/unix"
"testing"
)
func TestMaxAckBacklog(t *testing.T) {
n := 196602
- major, minor := kernelVersion()
+ major, minor := unix.KernelVersion()
backlog := maxAckBacklog(n)
expected := 1<<16 - 1
if major > 4 || (major == 4 && minor >= 1) {
"fmt"
"internal/abi"
"internal/profile"
+ "internal/syscall/unix"
"internal/testenv"
"io"
"math"
// Linux [5.9,5.16) has a kernel bug that can break CPU timers on newly
// created threads, breaking our CPU accounting.
- major, minor, patch, err := linuxKernelVersion()
- if err != nil {
- t.Errorf("Error determining kernel version: %v", err)
- }
- t.Logf("Running on Linux %d.%d.%d", major, minor, patch)
+ 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.")
+++ /dev/null
-// Copyright 2021 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
-
-package pprof
-
-import (
- "fmt"
- "regexp"
- "strconv"
- "syscall"
-)
-
-var versionRe = regexp.MustCompile(`^(\d+)(?:\.(\d+)(?:\.(\d+))).*$`)
-
-func linuxKernelVersion() (major, minor, patch int, err error) {
- var uname syscall.Utsname
- if err := syscall.Uname(&uname); err != nil {
- return 0, 0, 0, err
- }
-
- buf := make([]byte, 0, len(uname.Release))
- for _, b := range uname.Release {
- if b == 0 {
- break
- }
- buf = append(buf, byte(b))
- }
- rl := string(buf)
-
- m := versionRe.FindStringSubmatch(rl)
- if m == nil {
- return 0, 0, 0, fmt.Errorf("error matching version number in %q", rl)
- }
-
- v, err := strconv.ParseInt(m[1], 10, 64)
- if err != nil {
- return 0, 0, 0, fmt.Errorf("error parsing major version %q in %s: %w", m[1], rl, err)
- }
- major = int(v)
-
- if len(m) >= 3 {
- v, err := strconv.ParseInt(m[2], 10, 64)
- if err != nil {
- return 0, 0, 0, fmt.Errorf("error parsing minor version %q in %s: %w", m[2], rl, err)
- }
- minor = int(v)
- }
-
- if len(m) >= 4 {
- v, err := strconv.ParseInt(m[3], 10, 64)
- if err != nil {
- return 0, 0, 0, fmt.Errorf("error parsing patch version %q in %s: %w", m[3], rl, err)
- }
- patch = int(v)
- }
-
- return
-}
+++ /dev/null
-// Copyright 2021 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
-
-package pprof
-
-import (
- "errors"
-)
-
-func linuxKernelVersion() (major, minor, patch int, err error) {
- return 0, 0, 0, errors.New("not running on linux")
-}