cp(1): fix performance issue for large non-sparse file copies

PR252358 reported a serious performance problem when
copying a large non-sparse file on a UFS file system.
This problem seems to have been caused by a large
number of SEEK_HOLE operations, with one done
for each copy_file_range(2) call.

This patch modifies cp(1) to use a large (SSIZE_MAX)
len argument, reducing the number of system calls
and resolving the performance issue.

While here, convert the type of the "rcount" from "int"
to "ssize_t" so that it is consistent with that returned
by both read(2) and copy_file_range(2).

PR:	252358
Reviewed by:	asomers
Differential Revision:	https://reviews.freebsd.org/D27937
This commit is contained in:
Rick Macklem 2021-01-02 16:58:43 -08:00
parent 9c43bd646e
commit c98a764c68

View file

@ -74,11 +74,10 @@ __FBSDID("$FreeBSD$");
*/ */
#define BUFSIZE_SMALL (MAXPHYS) #define BUFSIZE_SMALL (MAXPHYS)
static int static ssize_t
copy_fallback(int from_fd, int to_fd, char *buf, size_t bufsize) copy_fallback(int from_fd, int to_fd, char *buf, size_t bufsize)
{ {
int rcount; ssize_t rcount, wresid, wcount = 0;
ssize_t wresid, wcount = 0;
char *bufp; char *bufp;
rcount = read(from_fd, buf, bufsize); rcount = read(from_fd, buf, bufsize);
@ -100,10 +99,10 @@ copy_file(const FTSENT *entp, int dne)
static char *buf = NULL; static char *buf = NULL;
static size_t bufsize; static size_t bufsize;
struct stat *fs; struct stat *fs;
ssize_t wcount; ssize_t rcount, wcount;
size_t wresid; size_t wresid;
off_t wtotal; off_t wtotal;
int ch, checkch, from_fd, rcount, rval, to_fd; int ch, checkch, from_fd, rval, to_fd;
char *bufp; char *bufp;
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
char *p; char *p;
@ -236,7 +235,7 @@ copy_file(const FTSENT *entp, int dne)
do { do {
if (use_copy_file_range) { if (use_copy_file_range) {
rcount = copy_file_range(from_fd, NULL, rcount = copy_file_range(from_fd, NULL,
to_fd, NULL, bufsize, 0); to_fd, NULL, SSIZE_MAX, 0);
if (rcount < 0 && errno == EINVAL) { if (rcount < 0 && errno == EINVAL) {
/* Prob a non-seekable FD */ /* Prob a non-seekable FD */
use_copy_file_range = 0; use_copy_file_range = 0;