-
Notifications
You must be signed in to change notification settings - Fork 18.3k
Description
What version of Go are you using (go version
)?
$ go version go version go1.18.3 linux/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/root/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/root/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.18.3" GCCGO="gccgo" GOAMD64="v1" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/dev/null" GOWORK="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build3873644549=/tmp/go-build -gno-record-gcc-switches"
What did you do?
package main
import (
"fmt"
"io/fs"
"io/ioutil"
"os"
)
func verify(fpath string) {
bs, err := fs.ReadFile(os.DirFS(""), fpath)
if err != nil {
panic(err)
}
fmt.Printf("fs.ReadFile %q: %q\n", fpath, bs)
f, err := os.Open("/" + fpath)
if err != nil {
panic(err)
}
defer f.Close()
bs, err = ioutil.ReadAll(f)
if err != nil {
panic(err)
}
fmt.Printf("ReadAll %q: %q\n", fpath, bs)
}
func main() {
verify("sys/fs/cgroup/cpuset.cpus.effective")
verify("sys/devices/system/cpu/cpu0/topology/thread_siblings_list")
}
What did you expect to see?
I'd expect thread_sibling_list
file to be read correctly:
fs.ReadFile "sys/fs/cgroup/cpuset.cpus.effective": "0\n"
ReadAll "sys/fs/cgroup/cpuset.cpus.effective": "0\n"
fs.ReadFile "sys/devices/system/cpu/cpu0/topology/thread_siblings_list": "0\n"
ReadAll "sys/devices/system/cpu/cpu0/topology/thread_siblings_list": "0\n"
There are no problems with thread_sibling_list
on Ubuntu 20.04 / kernel 5.4.0:
Linux i4u 5.4.0-73-generic #82-Ubuntu SMP Wed Apr 14 17:39:42 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
What did you see instead?
When code is run on Ubuntu 22.04 on Linux 5.15.0 the following output is produced:
fs.ReadFile "sys/fs/cgroup/cpuset.cpus.effective": "0\n"
ReadAll "sys/fs/cgroup/cpuset.cpus.effective": "0\n"
fs.ReadFile "sys/devices/system/cpu/cpu0/topology/thread_siblings_list": ""
ReadAll "sys/devices/system/cpu/cpu0/topology/thread_siblings_list": "0\n"
Note that for thread_siblings_list
, the empty string is returned.
I tested it on my own machine with Ubuntu 22.04 and also on a Digital Ocean instance with Ubuntu 22.04:
Linux rmmenow 5.15.0-25-generic #25-Ubuntu SMP Wed Mar 30 15:54:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
Additional notes
The relevant part of strace
looks like this:
[pid 3185] openat(AT_FDCWD, "/sys/fs/cgroup/cpuset.cpus.effective", O_RDONLY|O_CLOEXEC) = 3
[pid 3185] epoll_create1(EPOLL_CLOEXEC) = 4
[pid 3185] pipe2([5, 6], O_NONBLOCK|O_CLOEXEC) = 0
[pid 3185] epoll_ctl(4, EPOLL_CTL_ADD, 5, {events=EPOLLIN, data={u32=5665744, u64=5665744}}) = 0
[pid 3185] epoll_ctl(4, EPOLL_CTL_ADD, 3, {events=EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, data={u32=3003887392, u64=140633118060320}}) = 0
[pid 3185] fcntl(3, F_GETFL) = 0x8000 (flags O_RDONLY|O_LARGEFILE)
[pid 3185] fcntl(3, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
[pid 3185] fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
[pid 3185] read(3, "0", 1) = 1
[pid 3185] read(3, "\n", 7) = 1
[pid 3185] read(3, "", 6) = 0
[pid 3185] epoll_ctl(4, EPOLL_CTL_DEL, 3, 0xc000066ca4) = 0
[pid 3185] close(3) = 0
[pid 3185] write(1, "fs.ReadFile \"sys/fs/cgroup/cpuse"..., 57fs.ReadFile "sys/fs/cgroup/cpuset.cpus.effective": "0\n"
) = 57
[pid 3185] openat(AT_FDCWD, "/sys/fs/cgroup/cpuset.cpus.effective", O_RDONLY|O_CLOEXEC) = 3
[pid 3185] epoll_ctl(4, EPOLL_CTL_ADD, 3, {events=EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, data={u32=3003887392, u64=140633118060320}}) = 0
[pid 3188] futex(0xc000038948, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
[pid 3186] <... nanosleep resumed>NULL) = 0
[pid 3185] fcntl(3, F_GETFL) = 0x8000 (flags O_RDONLY|O_LARGEFILE)
[pid 3185] fcntl(3, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
[pid 3185] read(3, "0\n", 512) = 2
[pid 3185] read(3, "", 510) = 0
[pid 3185] write(1, "ReadAll \"sys/fs/cgroup/cpuset.cp"..., 53ReadAll "sys/fs/cgroup/cpuset.cpus.effective": "0\n"
) = 53
[pid 3185] epoll_ctl(4, EPOLL_CTL_DEL, 3, 0xc000066d8c) = 0
[pid 3185] close(3) = 0
[pid 3185] openat(AT_FDCWD, "/sys/devices/system/cpu/cpu0/topology/thread_siblings_list", O_RDONLY|O_CLOEXEC) = 3
[pid 3185] epoll_ctl(4, EPOLL_CTL_ADD, 3, {events=EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, data={u32=3003887392, u64=140633118060320}}) = 0
[pid 3185] fcntl(3, F_GETFL) = 0x8000 (flags O_RDONLY|O_LARGEFILE)
[pid 3185] fcntl(3, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
[pid 3185] fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
[pid 3185] read(3, "", 1) = 0
[pid 3185] epoll_ctl(4, EPOLL_CTL_DEL, 3, 0xc000066ca4) = 0
[pid 3185] close(3) = 0
[pid 3185] write(1, "fs.ReadFile \"sys/devices/system/"..., 76fs.ReadFile "sys/devices/system/cpu/cpu0/topology/thread_siblings_list": ""
) = 76
[pid 3185] openat(AT_FDCWD, "/sys/devices/system/cpu/cpu0/topology/thread_siblings_list", O_RDONLY|O_CLOEXEC) = 3
[pid 3185] epoll_ctl(4, EPOLL_CTL_ADD, 3, {events=EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, data={u32=3003887392, u64=140633118060320}}) = 0
[pid 3185] fcntl(3, F_GETFL) = 0x8000 (flags O_RDONLY|O_LARGEFILE)
[pid 3185] fcntl(3, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
[pid 3185] read(3, "0\n", 512) = 2
[pid 3185] read(3, "", 510) = 0
[pid 3185] write(1, "ReadAll \"sys/devices/system/cpu/"..., 75ReadAll "sys/devices/system/cpu/cpu0/topology/thread_siblings_list": "0\n"
) = 75
[pid 3185] epoll_ctl(4, EPOLL_CTL_DEL, 3, 0xc000066d8c) = 0
[pid 3185] close(3) = 0
[pid 3185] exit_group(0) = ?
[pid 3187] <... futex resumed>) = ?
[pid 3187] +++ exited with 0 +++
[pid 3186] +++ exited with 0 +++
[pid 3188] <... futex resumed>) = ?
[pid 3188] +++ exited with 0 +++
+++ exited with 0 +++
I suspect that the culprit is probably O_NONBLOCK
combined that the fact that seeing the file size of 0
in sysfs
Lines 43 to 49 in 5c1a13e
var size int | |
if info, err := file.Stat(); err == nil { | |
size64 := info.Size() | |
if int64(int(size64)) == size64 { | |
size = int(size64) | |
} | |
} |
fs.ReadFile
tries to read just one byte from itLines 51 to 57 in 5c1a13e
data := make([]byte, 0, size+1) | |
for { | |
if len(data) >= cap(data) { | |
d := append(data[:cap(data)], 0) | |
data = d[:len(data)] | |
} | |
n, err := file.Read(data[len(data):cap(data)]) |
The resulting
read()
syscall returns 0
(perhaps due to O_NONBLOCK
?)
read(3, "", 1) = 0
and file.Read()
returns EOF
error.
This is quite possibly a Linux kernel bug, nevertheless, it may affect many Go programs, as it's quite tempting to use io/fs
to make sysfs-dependent code testable.