summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2016-03-15 19:26:34 -0400
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2016-03-17 13:02:18 -0400
commita44202e98b638024c45e50ad404c7069c7835c04 (patch)
tree1b82ca988d199923f37315d2f935626abdb2a8a3
parent1d4b557d1b5cd200b27562808c690653658ffadf (diff)
basic/copy: use copy_file_range()
For btrfs, c_f_r() is like BTRFS_IOC_CLONE which we already used, but also works when max_bytes is set. We do call copy_bytes in coredump code with max_bytes set, and for large files, so we might see some benefit from using c_f_r() on btrfs. For other filesystems, c_f_r() falls back to do_splice_direct(), the same as sendfile, which we already call, so there shouldn't be much difference. Tested with test-copy and systemd-coredump on Linux 4.3 (w/o c_f_r) and 4.5 (w/ c_f_r).
-rw-r--r--TODO2
-rw-r--r--src/basic/copy.c40
2 files changed, 38 insertions, 4 deletions
diff --git a/TODO b/TODO
index 08b74083d3..b2840ba4ab 100644
--- a/TODO
+++ b/TODO
@@ -49,8 +49,6 @@ Features:
* cache sd_event_now() result from before the first iteration...
-* support for the new copy_file_range() syscall
-
* add systemctl stop --job-mode=triggering that follows TRIGGERED_BY deps and adds them to the same transaction
* Maybe add a way how users can "pin" units into memory, so that they are not subject to automatic GC?
diff --git a/src/basic/copy.c b/src/basic/copy.c
index dbbb1d0fd2..41dc8ca79a 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -40,6 +40,7 @@
#include "fs-util.h"
#include "io-util.h"
#include "macro.h"
+#include "missing.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@@ -48,10 +49,29 @@
#define COPY_BUFFER_SIZE (16*1024u)
+static ssize_t try_copy_file_range(int fd_in, loff_t *off_in,
+ int fd_out, loff_t *off_out,
+ size_t len,
+ unsigned int flags) {
+ static int have = -1;
+ ssize_t r;
+
+ if (have == false)
+ return -ENOSYS;
+
+ r = copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
+ if (_unlikely_(have < 0))
+ have = r >= 0 || errno != ENOSYS;
+ if (r >= 0)
+ return r;
+ else
+ return -errno;
+}
+
int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
- bool try_sendfile = true, try_splice = true;
+ bool try_cfr = true, try_sendfile = true, try_splice = true;
int r;
- size_t m = SSIZE_MAX; /* that the maximum that sendfile accepts */
+ size_t m = SSIZE_MAX; /* that the maximum that sendfile and c_f_r accept */
assert(fdf >= 0);
assert(fdt >= 0);
@@ -78,6 +98,22 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
m = (size_t) max_bytes;
}
+ /* First try copy_file_range(), unless we already tried */
+ if (try_cfr) {
+ n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
+ if (n < 0) {
+ if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV))
+ return n;
+
+ try_cfr = false;
+ /* use fallback below */
+ } else if (n == 0) /* EOF */
+ break;
+ else
+ /* Success! */
+ goto next;
+ }
+
/* First try sendfile(), unless we already tried */
if (try_sendfile) {
n = sendfile(fdt, fdf, NULL, m);