From: Zorro Lang <zlang@redhat.com>
To: linux-xfs@vger.kernel.org
Subject: [PATCH v2] xfs_io: support splice data between two files
Date: Sun, 19 May 2019 23:00:26 +0800 [thread overview]
Message-ID: <20190519150026.24626-1-zlang@redhat.com> (raw)
Add splice command into xfs_io, by calling splice(2) system call.
Signed-off-by: Zorro Lang <zlang@redhat.com>
---
Hi,
Thanks the reviewing from Eric.
If 'length' or 'soffset' or 'length + soffset' out of source file
range, splice hanging there. V2 fix this issue.
Thanks,
Zorro
io/Makefile | 2 +-
io/init.c | 1 +
io/io.h | 1 +
io/splice.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++
man/man8/xfs_io.8 | 26 +++++++
5 files changed, 223 insertions(+), 1 deletion(-)
create mode 100644 io/splice.c
diff --git a/io/Makefile b/io/Makefile
index 484e2b5a..06d21dd5 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -12,7 +12,7 @@ CFILES = init.c \
attr.c bmap.c crc32cselftest.c cowextsize.c encrypt.c file.c freeze.c \
fsync.c getrusage.c imap.c inject.c label.c link.c mmap.c open.c \
parent.c pread.c prealloc.c pwrite.c reflink.c resblks.c scrub.c \
- seek.c shutdown.c stat.c swapext.c sync.c truncate.c utimes.c
+ seek.c shutdown.c splice.c stat.c swapext.c sync.c truncate.c utimes.c
LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD)
LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG)
diff --git a/io/init.c b/io/init.c
index 83f08f2d..fc191aa7 100644
--- a/io/init.c
+++ b/io/init.c
@@ -79,6 +79,7 @@ init_commands(void)
seek_init();
sendfile_init();
shutdown_init();
+ splice_init();
stat_init();
swapext_init();
sync_init();
diff --git a/io/io.h b/io/io.h
index 6469179e..9a0b71f0 100644
--- a/io/io.h
+++ b/io/io.h
@@ -110,6 +110,7 @@ extern void quit_init(void);
extern void resblks_init(void);
extern void seek_init(void);
extern void shutdown_init(void);
+extern void splice_init(void);
extern void stat_init(void);
extern void swapext_init(void);
extern void sync_init(void);
diff --git a/io/splice.c b/io/splice.c
new file mode 100644
index 00000000..3c6f55e6
--- /dev/null
+++ b/io/splice.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Red Hat, Inc.
+ * All Rights Reserved.
+ */
+
+#include "command.h"
+#include "input.h"
+#include <fcntl.h>
+#include "init.h"
+#include "io.h"
+
+static cmdinfo_t splice_cmd;
+
+static void
+splice_help(void)
+{
+ printf(_(
+"\n"
+" Splice a range of bytes from the given offset between files through pipe\n"
+"\n"
+" Example:\n"
+" 'splice filename 0 4096 32768' - splice 32768 bytes from filename at offset\n"
+" 0 into the open file at position 4096\n"
+" 'splice filename' - splice all bytes from filename into the open file at\n"
+" ' position 0\n"
+"\n"
+" Copies data between one file and another. Because this copying is done\n"
+" within the kernel, sendfile does not need to transfer data to and from user\n"
+" space.\n"
+" -m -- SPLICE_F_MOVE flag, attempt to move pages instead of copying.\n"
+" Offset and length in the source/destination file can be optionally specified.\n"
+"\n"));
+}
+
+static uint64_t
+splice_file(
+ int fd,
+ off64_t soffset,
+ off64_t doffset,
+ size_t length,
+ unsigned int flag,
+ int *ops)
+{
+ off64_t soff = soffset;
+ off64_t doff = doffset;
+ ssize_t rc = 0;
+ size_t len = length;
+ uint64_t total = 0;
+ int filedes[2];
+
+ if (pipe(filedes) < 0) {
+ perror("pipe");
+ return -1;
+ }
+
+ *ops = 0;
+ while (len > 0 || !*ops) {
+ /* move to pipe buffer */
+ rc = splice(fd, &soff, filedes[1], NULL, len, flag);
+ if (rc < 0) {
+ perror("splice to pipe");
+ goto out_close;
+ }
+ /* move from pipe buffer to dst file */
+ rc = splice(filedes[0], NULL, file->fd, &doff, len, flag);
+ if (rc < 0) {
+ perror("splice from pipe");
+ goto out_close;
+ }
+ (*ops)++;
+ len -= rc;
+ total += rc;
+ }
+
+out_close:
+ close(filedes[0]);
+ close(filedes[1]);
+ return total;
+}
+
+static int
+splice_f(
+ int argc,
+ char **argv)
+{
+ off64_t soffset, doffset;
+ long long count, total;
+ size_t blocksize, sectsize;
+ struct timeval t1, t2;
+ char *infile = NULL;
+ int Cflag, qflag;
+ int splice_flag = 0;
+ int c, fd = -1;
+ int ops = 0;
+ struct stat stat;
+
+ Cflag = qflag = 0;
+ soffset = doffset=0;
+ init_cvtnum(&blocksize, §size);
+
+ while ((c = getopt(argc, argv, "Cqm")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'm':
+ splice_flag |= SPLICE_F_MOVE;
+ break;
+ default:
+ return command_usage(&splice_cmd);
+ }
+ }
+
+ if (optind != argc - 4 && optind != argc - 1)
+ return command_usage(&splice_cmd);
+
+ infile = argv[optind];
+ if ((fd = openfile(infile, NULL, IO_READONLY, 0, NULL)) < 0)
+ return 0;
+ optind++;
+
+ if (fstat(fd, &stat) < 0) {
+ perror("fstat");
+ goto done;
+ }
+
+ if (optind == argc - 3) {
+ soffset = cvtnum(blocksize, sectsize, argv[optind]);
+ if (soffset < 0 || soffset > stat.st_size) {
+ printf(_("invalid src offset argument -- %s\n"), \
+ argv[optind]);
+ return 0;
+ }
+ optind++;
+ doffset = cvtnum(blocksize, sectsize, argv[optind]);
+ if (doffset < 0) {
+ printf(_("invalid dest offset argument -- %s\n"), \
+ argv[optind]);
+ return 0;
+ }
+ optind++;
+ count = cvtnum(blocksize, sectsize, argv[optind]);
+ if (count < 0 || (soffset + count) > stat.st_size) {
+ printf(_("invalid length argument -- %s\n"), \
+ argv[optind]);
+ return 0;
+ }
+ } else {
+ /*
+ * splice whole file to another, if doesn't specify src and dst
+ * offset and length
+ */
+ count = stat.st_size;
+ soffset = 0;
+ doffset = 0;
+ }
+
+ gettimeofday(&t1, NULL);
+ total = splice_file(fd, soffset, doffset, count, splice_flag, &ops);
+ if (ops == 0 || qflag)
+ goto done;
+ gettimeofday(&t2, NULL);
+ t2 = tsub(t2, t1);
+
+ report_io_times("spliced", &t2, (long long)doffset, count, total, ops, \
+ Cflag);
+
+done:
+ if (infile)
+ close(fd);
+ return 0;
+}
+
+void
+splice_init(void)
+{
+ splice_cmd.name = "splice";
+ splice_cmd.altname = "spl";
+ splice_cmd.cfunc = splice_f;
+ splice_cmd.argmin = 1;
+ splice_cmd.argmax = -1;
+ splice_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK | CMD_FLAG_ONESHOT;;
+ splice_cmd.args =
+ _("[-m] infile [src_off dst_off len]");
+ splice_cmd.oneline =
+ _("Splice an entire file, or a number of bytes at a specified offset");
+ splice_cmd.help = splice_help;
+
+ add_command(&splice_cmd);
+}
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 980dcfd3..066a72e7 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -830,6 +830,32 @@ verbose output will be printed.
.RE
.PD
.TP
+.BI "splice [ \-C ] [ \-q ] [\-m] infile [src_offset dst_offset length]"
+On filesystems that support the
+.BR splice (2)
+system call, splice data from the
+.I infile
+into the open file. If
+.IR src_offset ,
+.IR dst_offset ,
+and
+.I length
+are omitted the contents of infile will be copied to the beginning of the
+open file, overwriting any data already there.
+.RS 1.0i
+.PD 0
+.TP 0.4i
+.B \-C
+Print timing statistics in a condensed format.
+.TP
+.B \-q
+Do not print timing statistics at all.
+.TP
+.B \-m
+Enable SPLICE_F_MOVE flag, attempt to move pages instead of copying.
+.RE
+.PD
+.TP
.BI utimes " atime_sec atime_nsec mtime_sec mtime_nsec"
The utimes command changes the atime and mtime of the current file.
sec uses UNIX timestamp notation and is the seconds elapsed since
--
2.17.2
next reply other threads:[~2019-05-19 17:30 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-05-19 15:00 Zorro Lang [this message]
2019-09-03 22:24 ` [PATCH v2] xfs_io: support splice data between two files Darrick J. Wong
2019-09-04 5:20 ` Amir Goldstein
2019-09-04 5:47 ` Zorro Lang
2019-09-04 11:14 ` Amir Goldstein
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190519150026.24626-1-zlang@redhat.com \
--to=zlang@redhat.com \
--cc=linux-xfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.