From a73506cff560c4d95ac604f4f76d58386ca29ed9 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 18 Aug 2022 13:43:47 -0700 Subject: [PATCH] internal/syscall/unix: consolidate kernelVersion implementations 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 Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor Reviewed-by: Bryan Mills Run-TryBot: Kirill Kolyshkin TryBot-Result: Gopher Robot --- src/internal/poll/copy_file_range_linux.go | 37 +---------- .../syscall/unix/kernel_version_linux.go | 49 +++++++++++++++ .../syscall/unix/kernel_version_other.go | 11 ++++ src/net/sock_linux.go | 42 ++----------- src/net/sock_linux_test.go | 3 +- src/runtime/pprof/pprof_test.go | 8 +-- src/runtime/pprof/uname_linux_test.go | 61 ------------------- src/runtime/pprof/uname_other_test.go | 15 ----- 8 files changed, 71 insertions(+), 155 deletions(-) create mode 100644 src/internal/syscall/unix/kernel_version_linux.go create mode 100644 src/internal/syscall/unix/kernel_version_other.go delete mode 100644 src/runtime/pprof/uname_linux_test.go delete mode 100644 src/runtime/pprof/uname_other_test.go diff --git a/src/internal/poll/copy_file_range_linux.go b/src/internal/poll/copy_file_range_linux.go index c2347ba7f2f689..ba33f5145d4896 100644 --- a/src/internal/poll/copy_file_range_linux.go +++ b/src/internal/poll/copy_file_range_linux.go @@ -17,46 +17,11 @@ var ( 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 diff --git a/src/internal/syscall/unix/kernel_version_linux.go b/src/internal/syscall/unix/kernel_version_linux.go new file mode 100644 index 00000000000000..bb1d4de3a6c892 --- /dev/null +++ b/src/internal/syscall/unix/kernel_version_linux.go @@ -0,0 +1,49 @@ +// 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 +} diff --git a/src/internal/syscall/unix/kernel_version_other.go b/src/internal/syscall/unix/kernel_version_other.go new file mode 100644 index 00000000000000..00af9f2ba01841 --- /dev/null +++ b/src/internal/syscall/unix/kernel_version_other.go @@ -0,0 +1,11 @@ +// 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 +} diff --git a/src/net/sock_linux.go b/src/net/sock_linux.go index 2513f9ba7b9779..cffe9a236fc7f1 100644 --- a/src/net/sock_linux.go +++ b/src/net/sock_linux.go @@ -4,42 +4,10 @@ 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: // @@ -50,7 +18,7 @@ func kernelVersion() (major int, minor int) { // // 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 diff --git a/src/net/sock_linux_test.go b/src/net/sock_linux_test.go index 5df02935c3ce69..11303cfff1e646 100644 --- a/src/net/sock_linux_test.go +++ b/src/net/sock_linux_test.go @@ -5,12 +5,13 @@ 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) { diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go index 28b8f4319e68c6..31a4024be819df 100644 --- a/src/runtime/pprof/pprof_test.go +++ b/src/runtime/pprof/pprof_test.go @@ -12,6 +12,7 @@ import ( "fmt" "internal/abi" "internal/profile" + "internal/syscall/unix" "internal/testenv" "io" "math" @@ -116,11 +117,8 @@ func TestCPUProfileMultithreadMagnitude(t *testing.T) { // 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.") diff --git a/src/runtime/pprof/uname_linux_test.go b/src/runtime/pprof/uname_linux_test.go deleted file mode 100644 index 8374c83f740af7..00000000000000 --- a/src/runtime/pprof/uname_linux_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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 -} diff --git a/src/runtime/pprof/uname_other_test.go b/src/runtime/pprof/uname_other_test.go deleted file mode 100644 index 327640755bd4dd..00000000000000 --- a/src/runtime/pprof/uname_other_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// 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") -}