diff mbox

[v2] xfs_io: support a basic extent swap command

Message ID 20180208155719.17095-1-bfoster@redhat.com (mailing list archive)
State Accepted
Headers show

Commit Message

Brian Foster Feb. 8, 2018, 3:57 p.m. UTC
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

Comments

Darrick J. Wong Feb. 8, 2018, 4:54 p.m. UTC | #1
On Thu, Feb 08, 2018 at 10:57:19AM -0500, Brian Foster wrote:
> 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>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Dave Chinner Feb. 8, 2018, 9:13 p.m. UTC | #2
On Thu, Feb 08, 2018 at 10:57:19AM -0500, Brian Foster wrote:
> 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

Looks good. I'm no longer confused :)

Reviewed-by: Dave Chinner <dchinner@redhat.com>
diff mbox

Patch

diff --git a/io/Makefile b/io/Makefile
index 6725936d..2f9249e8 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -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)
diff --git a/io/init.c b/io/init.c
index 20d5f80d..2ade03f4 100644
--- a/io/init.c
+++ b/io/init.c
@@ -88,6 +88,7 @@  init_commands(void)
 	sendfile_init();
 	shutdown_init();
 	stat_init();
+	swapext_init();
 	sync_init();
 	sync_range_init();
 	truncate_init();
diff --git a/io/io.h b/io/io.h
index 3862985f..258311f1 100644
--- a/io/io.h
+++ b/io/io.h
@@ -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);
diff --git a/io/swapext.c b/io/swapext.c
new file mode 100644
index 00000000..5e161d69
--- /dev/null
+++ b/io/swapext.c
@@ -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);
+}
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 9bf1a478..1e40aa64 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -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