@@ -11,7 +11,8 @@ HFILES = init.h io.h
CFILES = init.c \
attr.c bmap.c cowextsize.c encrypt.c file.c freeze.c fsync.c \
getrusage.c imap.c link.c mmap.c open.c parent.c pread.c prealloc.c \
- pwrite.c reflink.c seek.c shutdown.c stat.c sync.c truncate.c utimes.c
+ pwrite.c reflink.c seek.c shutdown.c stat.c swapext.c sync.c \
+ truncate.c utimes.c
LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBPTHREAD)
LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE)
@@ -88,6 +88,7 @@ init_commands(void)
sendfile_init();
shutdown_init();
stat_init();
+ swapext_init();
sync_init();
sync_range_init();
truncate_init();
@@ -118,6 +118,7 @@ extern void quit_init(void);
extern void seek_init(void);
extern void shutdown_init(void);
extern void stat_init(void);
+extern void swapext_init(void);
extern void sync_init(void);
extern void truncate_init(void);
extern void utimes_init(void);
new file mode 100644
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2018 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "command.h"
+#include "input.h"
+#include "init.h"
+#include "io.h"
+
+static cmdinfo_t swapext_cmd;
+
+static void
+swapext_help(void)
+{
+ printf(_(
+"\n"
+" Swaps extents between the open file descriptor and the supplied filename.\n"
+"\n"));
+}
+
+static int
+xfs_bulkstat_single(
+ int fd,
+ xfs_ino_t *lastip,
+ struct xfs_bstat *ubuffer)
+{
+ struct xfs_fsop_bulkreq bulkreq;
+
+ bulkreq.lastip = (__u64 *)lastip;
+ bulkreq.icount = 1;
+ bulkreq.ubuffer = ubuffer;
+ bulkreq.ocount = NULL;
+ return ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq);
+}
+
+static int
+swapext_f(
+ int argc,
+ char **argv)
+{
+ int fd;
+ int error;
+ struct xfs_swapext sx;
+ struct stat stat;
+
+ /* open the donor file */
+ fd = openfile(argv[1], NULL, 0, 0, NULL);
+ if (fd < 0)
+ return 0;
+
+ /*
+ * stat the target file to get the inode number and use the latter to
+ * get the bulkstat info for the swapext cmd.
+ */
+ error = fstat(file->fd, &stat);
+ if (error) {
+ perror("fstat");
+ goto out;
+ }
+
+ error = xfs_bulkstat_single(file->fd, &stat.st_ino, &sx.sx_stat);
+ if (error) {
+ perror("bulkstat");
+ goto out;
+ }
+ sx.sx_version = XFS_SX_VERSION;
+ sx.sx_fdtarget = file->fd;
+ sx.sx_fdtmp = fd;
+ sx.sx_offset = 0;
+ sx.sx_length = stat.st_size;
+ error = ioctl(file->fd, XFS_IOC_SWAPEXT, &sx);
+ if (error)
+ perror("swapext");
+
+out:
+ close(fd);
+ return 0;
+}
+
+void
+swapext_init(void)
+{
+ swapext_cmd.name = "swapext";
+ swapext_cmd.cfunc = swapext_f;
+ swapext_cmd.argmin = 1;
+ swapext_cmd.argmax = 1;
+ swapext_cmd.flags = CMD_NOMAP_OK;
+ swapext_cmd.args = _("<donorfile>");
+ swapext_cmd.oneline = _("Swap extents between files.");
+ swapext_cmd.help = swapext_help;
+
+ add_command(&swapext_cmd);
+}
@@ -761,6 +761,11 @@ sec uses UNIX timestamp notation and is the seconds elapsed since
nsec is the nanoseconds since the sec. This value needs to be in
the range 0-999999999 with UTIME_NOW and UTIME_OMIT being exceptions.
Each (sec, nsec) pair constitutes a single timestamp value.
+.TP
+.BI swapext " 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)).
.SH MEMORY MAPPED I/O COMMANDS
.TP
Extent swap is a low level mechanism exported by XFS to facilitate filesystem defragmentation. It is typically invoked by xfs_fsr under conditions that will atomically adjust inode extent state without loss of file data. While xfs_fsr provides some debug capability to tailor its behavior, it is not flexible enough to facilitate low level tests of the extent swap mechanism. For example, xfs_fsr may skip swaps between inodes that consist solely of preallocated extents because it considers such files already 100% defragmented. Further, xfs_fsr copies data between files where doing so may be unnecessary and thus inefficient for lower level tests. Add a basic swapext command to xfs_io that allows userspace invocation of the command under more controlled conditions. This facilites targeted tests without interference from xfs_fsr policy, such as using files with only preallocated extents, known/expected failure cases, etc. This command makes no effort to retain data across the operation. As such, it is for testing purposes only. Signed-off-by: Brian Foster <bfoster@redhat.com> --- v2: - Update xfs_io man page. - Fix up commit log description. v1: https://marc.info/?l=linux-xfs&m=151792224511355&w=2 io/Makefile | 3 +- io/init.c | 1 + io/io.h | 1 + io/swapext.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ man/man8/xfs_io.8 | 5 +++ 5 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 io/swapext.c