diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2016-03-14 20:15:21 -0400 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2016-03-15 14:54:27 -0400 |
commit | 00a8cf7763ec5e132efd4c974fbc6530c82240d0 (patch) | |
tree | f03f8725f75ac6cce03f4b073adb04e2ee522998 | |
parent | ce33fddad0806f859ff7324527ee0525dd10455e (diff) |
basic/copy: use sendfile smarter
We called sendfile with 16kb (a.k.a. COPY_BUFFER_SIZE) as the maximum
number of bytes to copy. This seems rather inefficient, especially with
large files. Instead, call sendfile with a "large" maximum.
What "large" max means is a bit tricky: current file offset + max
must fit in loff_t. This means that as we call sendfile more than once,
we have to lower the max size.
With this patch, test-copy calls sendfile twice, e.g.:
sendfile(4, 3, NULL, 9223372036854775807) = 738760
sendfile(4, 3, NULL, 9223372036854037047) = 0
The second call is necessary to determine EOF.
-rw-r--r-- | src/basic/copy.c | 25 |
1 files changed, 14 insertions, 11 deletions
diff --git a/src/basic/copy.c b/src/basic/copy.c index 519b412941..dbbb1d0fd2 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -46,11 +46,12 @@ #include "umask-util.h" #include "xattr-util.h" -#define COPY_BUFFER_SIZE (16*1024) +#define COPY_BUFFER_SIZE (16*1024u) int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { bool try_sendfile = true, try_splice = true; int r; + size_t m = SSIZE_MAX; /* that the maximum that sendfile accepts */ assert(fdf >= 0); assert(fdt >= 0); @@ -67,11 +68,9 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { } for (;;) { - size_t m = COPY_BUFFER_SIZE; ssize_t n; if (max_bytes != (uint64_t) -1) { - if (max_bytes <= 0) return 1; /* return > 0 if we hit the max_bytes limit */ @@ -81,42 +80,41 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { /* First try sendfile(), unless we already tried */ if (try_sendfile) { - n = sendfile(fdt, fdf, NULL, m); if (n < 0) { - if (errno != EINVAL && errno != ENOSYS) + if (!IN_SET(errno, EINVAL, ENOSYS)) return -errno; try_sendfile = false; /* use fallback below */ } else if (n == 0) /* EOF */ break; - else if (n > 0) + else /* Success! */ goto next; } - /* The try splice, unless we already tried */ + /* Then try splice, unless we already tried */ if (try_splice) { n = splice(fdf, NULL, fdt, NULL, m, 0); if (n < 0) { - if (errno != EINVAL && errno != ENOSYS) + if (!IN_SET(errno, EINVAL, ENOSYS)) return -errno; try_splice = false; /* use fallback below */ } else if (n == 0) /* EOF */ break; - else if (n > 0) + else /* Success! */ goto next; } /* As a fallback just copy bits by hand */ { - uint8_t buf[m]; + uint8_t buf[MIN(m, COPY_BUFFER_SIZE)]; - n = read(fdf, buf, m); + n = read(fdf, buf, sizeof buf); if (n < 0) return -errno; if (n == 0) /* EOF */ @@ -132,6 +130,11 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { assert(max_bytes >= (uint64_t) n); max_bytes -= n; } + /* sendfile accepts at most SSIZE_MAX-offset bytes to copy, + * so reduce our maximum by the amount we already copied, + * but don't go below our copy buffer size, unless we are + * close the the limit of bytes we are allowed to copy. */ + m = MAX(MIN(COPY_BUFFER_SIZE, max_bytes), m - n); } return 0; /* return 0 if we hit EOF earlier than the size limit */ |