1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-22 06:50:18 +03:00

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.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2016-03-14 20:15:21 -04:00
parent ce33fddad0
commit 00a8cf7763

View File

@ -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 */