[PATCH 19/20] xfs_io: enhance swapext to take advantage of new api

Darrick J. Wong
2023-12-31T22:32:01Z

Enhance the swapext command so that we can take advantage of the new
API's features and print some timing information.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
io/swapext.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++----
man/man8/xfs_io.8 | 54 ++++++++++++++++++
2 files changed, 197 insertions(+), 13 deletions(-)

diff --git a/io/swapext.c b/io/swapext.c
index 15ed3559398..22476ec7563 100644
--- a/io/swapext.c
+++ b/io/swapext.c
@@ -20,7 +20,36 @@ swapext_help(void)
printf(_(
"\n"
" Swaps extents between the open file descriptor and the supplied filename.\n"
-"\n"));
+"\n"
+" -a -- Use atomic extent swapping\n"
+" -C -- Print timing information in a condensed format\n"
+" -d N -- Start swapping extents at this offset in the open file\n"
+" -e -- Swap extents to the ends of both files, including the file sizes\n"
+" -f -- Flush changed file data and metadata to disk\n"
+" -h -- Only swap written ranges in the supplied file\n"
+" -l N -- Swap this many bytes between the two files\n"
+" -n -- Dry run; do all the parameter validation but do not change anything.\n"
+" -s N -- Start swapping extents at this offset in the supplied file\n"
+" -t -- Print timing information\n"
+" -u -- Do not compare the open file's timestamps\n"
+" -v -- 'swapext' for XFS_IOC_SWAPEXT, or 'exchrange' for XFS_IOC_EXCHANGE_RANGE\n"));
+}
+
+static void
+set_xfd_flags(
+ struct xfs_fd *xfd,
+ int api_ver)
+{
+ switch (api_ver) {
+ case 0:
+ xfd->flags |= XFROG_FLAG_FORCE_SWAPEXT;
+ break;
+ case 1:
+ xfd->flags |= XFROG_FLAG_FORCE_EXCH_RANGE;
+ break;
+ default:
+ break;
+ }
}

static int
@@ -31,13 +60,101 @@ swapext_f(
struct xfs_fd xfd = XFS_FD_INIT(file->fd);
struct xfs_exch_range fxr;
struct stat stat;
- uint64_t flags = XFS_EXCH_RANGE_FILE2_FRESH |
+ struct timeval t1, t2;
+ uint64_t flags = XFS_EXCH_RANGE_NONATOMIC |
+ XFS_EXCH_RANGE_FILE2_FRESH |
XFS_EXCH_RANGE_FULL_FILES;
+ int64_t src_offset = 0;
+ int64_t dest_offset = 0;
+ int64_t length = -1;
+ size_t fsblocksize, fssectsize;
+ int condensed = 0, quiet_flag = 1;
+ int api_ver = -1;
+ int c;
int fd;
int ret;

+ init_cvtnum(&fsblocksize, &fssectsize);
+ while ((c = getopt(argc, argv, "Cad:efhl:ns:tuv:")) != -1) {
+ switch (c) {
+ case 'C':
+ condensed = 1;
+ break;
+ case 'a':
+ flags &= ~XFS_EXCH_RANGE_NONATOMIC;
+ break;
+ case 'd':
+ dest_offset = cvtnum(fsblocksize, fssectsize, optarg);
+ if (dest_offset < 0) {
+ printf(
+ _("non-numeric open file offset argument -- %s\n"),
+ optarg);
+ return 0;
+ }
+ flags &= ~XFS_EXCH_RANGE_FULL_FILES;
+ break;
+ case 'e':
+ flags |= XFS_EXCH_RANGE_TO_EOF;
+ flags &= ~XFS_EXCH_RANGE_FULL_FILES;
+ break;
+ case 'f':
+ flags |= XFS_EXCH_RANGE_FSYNC;
+ break;
+ case 'h':
+ flags |= XFS_EXCH_RANGE_FILE1_WRITTEN;
+ break;
+ case 'l':
+ length = cvtnum(fsblocksize, fssectsize, optarg);
+ if (length < 0) {
+ printf(
+ _("non-numeric length argument -- %s\n"),
+ optarg);
+ return 0;
+ }
+ flags &= ~XFS_EXCH_RANGE_FULL_FILES;
+ break;
+ case 'n':
+ flags |= XFS_EXCH_RANGE_DRY_RUN;
+ break;
+ case 's':
+ src_offset = cvtnum(fsblocksize, fssectsize, optarg);
+ if (src_offset < 0) {
+ printf(
+ _("non-numeric supplied file offset argument -- %s\n"),
+ optarg);
+ return 0;
+ }
+ flags &= ~XFS_EXCH_RANGE_FULL_FILES;
+ break;
+ case 't':
+ quiet_flag = 0;
+ break;
+ case 'u':
+ flags &= ~XFS_EXCH_RANGE_FILE2_FRESH;
+ break;
+ case 'v':
+ if (!strcmp(optarg, "swapext"))
+ api_ver = 0;
+ else if (!strcmp(optarg, "exchrange"))
+ api_ver = 1;
+ else {
+ fprintf(stderr,
+ _("version must be 'swapext' or 'exchrange'.\n"));
+ return 1;
+ }
+ break;
+ default:
+ swapext_help();
+ return 0;
+ }
+ }
+ if (optind != argc - 1) {
+ swapext_help();
+ return 0;
+ }
+
/* open the donor file */
- fd = openfile(argv[1], NULL, 0, 0, NULL);
+ fd = openfile(argv[optind], NULL, 0, 0, NULL);
if (fd < 0)
return 0;

@@ -48,27 +165,42 @@ swapext_f(
goto out;
}

- ret = fstat(file->fd, &stat);
- if (ret) {
- perror("fstat");
- exitcode = 1;
- goto out;
+ if (length < 0) {
+ ret = fstat(file->fd, &stat);
+ if (ret) {
+ perror("fstat");
+ exitcode = 1;
+ goto out;
+ }
+
+ length = stat.st_size;
}

- ret = xfrog_file_exchange_prep(&xfd, flags, 0, fd, 0, stat.st_size,
- &fxr);
+ ret = xfrog_file_exchange_prep(&xfd, flags, dest_offset, fd, src_offset,
+ length, &fxr);
if (ret) {
xfrog_perror(ret, "xfrog_file_exchange_prep");
exitcode = 1;
goto out;
}

+ set_xfd_flags(&xfd, api_ver);
+
+ gettimeofday(&t1, NULL);
ret = xfrog_file_exchange(&xfd, &fxr);
if (ret) {
xfrog_perror(ret, "swapext");
exitcode = 1;
goto out;
}
+ if (quiet_flag)
+ goto out;
+
+ gettimeofday(&t2, NULL);
+ t2 = tsub(t2, t1);
+
+ report_io_times("swapext", &t2, dest_offset, length, length, 1,
+ condensed);
out:
close(fd);
return 0;
@@ -80,9 +212,9 @@ swapext_init(void)
swapext_cmd.name = "swapext";
swapext_cmd.cfunc = swapext_f;
swapext_cmd.argmin = 1;
- swapext_cmd.argmax = 1;
+ swapext_cmd.argmax = -1;
swapext_cmd.flags = CMD_NOMAP_OK;
- swapext_cmd.args = _("<donorfile>");
+ swapext_cmd.args = _("[-a] [-e] [-f] [-u] [-d dest_offset] [-s src_offset] [-l length] [-v swapext|exchrange] <donorfile>");
swapext_cmd.oneline = _("Swap extents between files.");
swapext_cmd.help = swapext_help;

diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 56abe000f23..34f9ffe9433 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -708,10 +708,62 @@ bytes of data.
.RE
.PD
.TP
-.BI swapext " donor_file "
+.BI "swapext [OPTIONS]" " donor_file "
Swaps extent forks between files. The current open file is the target. The donor
file is specified by path. Note that file data is not copied (file content moves
with the fork(s)).
+Options include:
+.RS 1.0i
+.PD 0
+.TP 0.4i
+.B \-a
+Swap extent forks atomically.
+The filesystem must be able to complete the operation even if the system goes
+down.
+.TP
+.B \-C
+Print timing information in a condensed format.
+.TP
+.BI \-d " dest_offset"
+Swap extents with open file beginning at
+.IR dest_offset .
+.TP
+.B \-e
+Swap extents to the ends of both files, including the file sizes.
+.TP
+.B \-f
+Flush changed file data and file metadata to disk.
+.TP
+.B \-h
+Only swap written ranges in the supplied file.
+.TP
+.BI \-l " length"
+Swap up to
+.I length
+bytes of data.
+.TP
+.B \-n
+Perform all the parameter validation checks but don't change anything.
+.TP
+.BI \-s " src_offset"
+Swap extents with donor file beginning at
+.IR src_offset .
+.TP
+.B \-t
+Print timing information.
+.TP
+.B \-u
+Do not snapshot and compare the open file's timestamps.
+.TP
+.B \-v
+Use a particular version of the kernel interface.
+Currently supported values are
+.I xfs
+for the old XFS_IOC_SWAPEXT ioctl, and
+.I vfs
+for the new XFS_IOC_EXCHANGE_RANGE ioctl.
+.RE
+.PD
.TP
.BI "set_encpolicy [ \-c " mode " ] [ \-n " mode " ] [ \-f " flags " ] [ \-s " log2_dusize " ] [ \-v " version " ] [ " keyspec " ]"
On filesystems that support encryption, assign an encryption policy to the