Thanks for the note. I'm not a kernel developer, but to me this sounds like a kernel bug. It seems particularly unfortunate that copy_file_range returns 0 in this case. From the perspective of the Go standard library, what we would need is some mechanism to detect when the copy_file_range system call will not or did not work correctly. As the biggest hammer, we currently only call copy_file_range on kernel versions 5.3 and newer. We can bump that requirement if necessary. Please feel free to open a bug about this at https://golang.org/issue, but we'll need guidance as to what we should do to avoid the problem. Thanks. Ian On Sun, Jan 24, 2021 at 11:54 PM Nicolas Boichat <drinkcat@xxxxxxxxxxxx> wrote: > > Hi copy_file_range experts, > > We hit this interesting issue when upgrading Go compiler from 1.13 to > 1.15 [1]. Basically we use Go's `io.Copy` to copy the content of > `/sys/kernel/debug/tracing/trace` to a temporary file. > > Under the hood, Go now uses `copy_file_range` syscall to optimize the > copy operation. However, that fails to copy any content when the input > file is from sysfs/tracefs, with an apparent size of 0 (but there is > still content when you `cat` it, of course). > > A repro case is available in comment7 (adapted from the man page), > also copied below [2]. > > Output looks like this (on kernels 5.4.89 (chromeos), 5.7.17 and > 5.10.3 (chromeos)) > $ ./copyfrom /sys/kernel/debug/tracing/trace x > 0 bytes copied > $ cat x > $ cat /sys/kernel/debug/tracing/trace > # tracer: nop > # > # entries-in-buffer/entries-written: 0/0 #P:8 > # > # _-----=> irqs-off > # / _----=> need-resched > # | / _---=> hardirq/softirq > # || / _--=> preempt-depth > # ||| / delay > # TASK-PID CPU# |||| TIMESTAMP FUNCTION > # | | | |||| | | > > I can try to dig further, but thought you'd like to get a bug report > as soon as possible. > > Thanks, > > Nicolas > > [1] http://issuetracker.google.com/issues/178332739 > [2] > #define _GNU_SOURCE > #include <fcntl.h> > #include <stdio.h> > #include <stdlib.h> > #include <sys/stat.h> > #include <sys/syscall.h> > #include <unistd.h> > > int > main(int argc, char **argv) > { > int fd_in, fd_out; > loff_t ret; > > if (argc != 3) { > fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]); > exit(EXIT_FAILURE); > } > > fd_in = open(argv[1], O_RDONLY); > if (fd_in == -1) { > perror("open (argv[1])"); > exit(EXIT_FAILURE); > } > > fd_out = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644); > if (fd_out == -1) { > perror("open (argv[2])"); > exit(EXIT_FAILURE); > } > > ret = copy_file_range(fd_in, NULL, fd_out, NULL, 1024, 0); > if (ret == -1) { > perror("copy_file_range"); > exit(EXIT_FAILURE); > } > printf("%d bytes copied\n", (int)ret); > > close(fd_in); > close(fd_out); > exit(EXIT_SUCCESS); > }