From 2afac833a07d0422626695d7702da4394bf8c9f9 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 10 Nov 2015 11:20:09 +1100 Subject: [PATCH] io: update reflink/dedupe ioctl definitions The committed version was a pre-review version of the ioctl interface. Update it to match post-review version. [dchinner: I've just aplied the latest patches over the top of what I originally committed, so the diff here isn't exactly what Darrick originally sent. ] Signed-off-by: Darrick J. Wong Signed-off-by: Dave Chinner --- io/Makefile | 2 +- io/dedupe.c | 190 ------------------------------ io/init.c | 1 - io/io.h | 1 - io/reflink.c | 289 ++++++++++++++++++++++++++++++++++------------ libxfs/xfs_fs.h | 25 ++-- man/man8/xfs_io.8 | 29 ++--- 7 files changed, 243 insertions(+), 294 deletions(-) delete mode 100644 io/dedupe.c diff --git a/io/Makefile b/io/Makefile index a5f2ac929..0b53f4187 100644 --- a/io/Makefile +++ b/io/Makefile @@ -11,7 +11,7 @@ HFILES = init.h io.h CFILES = init.c \ attr.c bmap.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 seek.c shutdown.c \ - sync.c truncate.c reflink.c dedupe.c + sync.c truncate.c reflink.c LLDLIBS = $(LIBXCMD) $(LIBHANDLE) LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) diff --git a/io/dedupe.c b/io/dedupe.c deleted file mode 100644 index 59c3d0f55..000000000 --- a/io/dedupe.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2015 Oracle, 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 -#include -#include "command.h" -#include "input.h" -#include "init.h" -#include "io.h" - -static cmdinfo_t dedupe_cmd; - -static void -dedupe_help(void) -{ - printf(_( -"\n" -" Links a range of bytes (in block size increments) from a file into a range \n" -" of bytes in the open file. The contents of both file ranges must match.\n" -"\n" -" Example:\n" -" 'dedupe some_file 0 4096 32768' - links 32768 bytes from some_file at \n" -" offset 0 to into the open file at \n" -" position 4096\n" -"\n" -" Reflink a range of blocks from a given input file to the open file. Both\n" -" files share the same range of physical disk blocks; a write to the shared\n" -" range of either file should result in the write landing in a new block and\n" -" that range of the file being remapped (i.e. copy-on-write). Both files\n" -" must reside on the same filesystem, and the contents of both ranges must\n" -" match.\n" -" -w -- call fdatasync(2) at the end (included in timing results)\n" -" -W -- call fsync(2) at the end (included in timing results)\n" -"\n")); -} - -static int -dedupe_f( - int argc, - char **argv) -{ - off64_t soffset, doffset; - long long count, total; - char s1[64], s2[64], ts[64]; - char *infile; - int Cflag, qflag, wflag, Wflag; - struct xfs_ioctl_file_extent_same_args *args = NULL; - struct xfs_ioctl_file_extent_same_info *info; - size_t fsblocksize, fssectsize; - struct timeval t1, t2; - int c, fd = -1; - - Cflag = qflag = wflag = Wflag = 0; - init_cvtnum(&fsblocksize, &fssectsize); - - while ((c = getopt(argc, argv, "CqwW")) != EOF) { - switch (c) { - case 'C': - Cflag = 1; - break; - case 'q': - qflag = 1; - break; - case 'w': - wflag = 1; - break; - case 'W': - Wflag = 1; - break; - default: - return command_usage(&dedupe_cmd); - } - } - if (optind != argc - 4) - return command_usage(&dedupe_cmd); - infile = argv[optind]; - optind++; - soffset = cvtnum(fsblocksize, fssectsize, argv[optind]); - if (soffset < 0) { - printf(_("non-numeric src offset argument -- %s\n"), argv[optind]); - return 0; - } - optind++; - doffset = cvtnum(fsblocksize, fssectsize, argv[optind]); - if (doffset < 0) { - printf(_("non-numeric dest offset argument -- %s\n"), argv[optind]); - return 0; - } - optind++; - count = cvtnum(fsblocksize, fssectsize, argv[optind]); - if (count < 1) { - printf(_("non-positive length argument -- %s\n"), argv[optind]); - return 0; - } - - c = IO_READONLY; - fd = openfile(infile, NULL, c, 0); - if (fd < 0) - return 0; - - gettimeofday(&t1, NULL); - args = calloc(1, sizeof(struct xfs_ioctl_file_extent_same_args) + - sizeof(struct xfs_ioctl_file_extent_same_info)); - if (!args) - goto done; - info = (struct xfs_ioctl_file_extent_same_info *)(args + 1); - args->logical_offset = soffset; - args->length = count; - args->dest_count = 1; - info->fd = file->fd; - info->logical_offset = doffset; - do { - c = ioctl(fd, XFS_IOC_FILE_EXTENT_SAME, args); - if (c) - break; - args->logical_offset += info->bytes_deduped; - info->logical_offset += info->bytes_deduped; - args->length -= info->bytes_deduped; - } while (c == 0 && info->status == 0 && info->bytes_deduped > 0); - if (c) - perror(_("dedupe ioctl")); - if (info->status < 0) - printf("dedupe: %s\n", _(strerror(-info->status))); - if (info->status == XFS_SAME_DATA_DIFFERS) - printf(_("Extents did not match.\n")); - if (c != 0 || info->status != 0) - goto done; - total = info->bytes_deduped; - c = 1; - if (Wflag) - fsync(file->fd); - if (wflag) - fdatasync(file->fd); - if (qflag) - goto done; - gettimeofday(&t2, NULL); - t2 = tsub(t2, t1); - - /* Finally, report back -- -C gives a parsable format */ - timestr(&t2, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0); - if (!Cflag) { - cvtstr((double)total, s1, sizeof(s1)); - cvtstr(tdiv((double)total, t2), s2, sizeof(s2)); - printf(_("linked %lld/%lld bytes at offset %lld\n"), - total, count, (long long)doffset); - printf(_("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n"), - s1, c, ts, s2, tdiv((double)c, t2)); - } else {/* bytes,ops,time,bytes/sec,ops/sec */ - printf("%lld,%d,%s,%.3f,%.3f\n", - total, c, ts, - tdiv((double)total, t2), tdiv((double)c, t2)); - } -done: - free(args); - close(fd); - return 0; -} - -void -dedupe_init(void) -{ - dedupe_cmd.name = "dedupe"; - dedupe_cmd.altname = "dd"; - dedupe_cmd.cfunc = dedupe_f; - dedupe_cmd.argmin = 4; - dedupe_cmd.argmax = -1; - dedupe_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; - dedupe_cmd.args = -_("infile src_off dst_off len"); - dedupe_cmd.oneline = - _("dedupes a number of bytes at a specified offset"); - dedupe_cmd.help = dedupe_help; - - add_command(&dedupe_cmd); -} diff --git a/io/init.c b/io/init.c index 739371e0f..51f1f5c96 100644 --- a/io/init.c +++ b/io/init.c @@ -84,7 +84,6 @@ init_commands(void) sync_range_init(); truncate_init(); reflink_init(); - dedupe_init(); } static int diff --git a/io/io.h b/io/io.h index ec8a53089..172b1f847 100644 --- a/io/io.h +++ b/io/io.h @@ -163,4 +163,3 @@ extern void readdir_init(void); #endif extern void reflink_init(void); -extern void dedupe_init(void); diff --git a/io/reflink.c b/io/reflink.c index 0433537e8..5ba1c93ad 100644 --- a/io/reflink.c +++ b/io/reflink.c @@ -23,32 +23,201 @@ #include "init.h" #include "io.h" +static cmdinfo_t dedupe_cmd; static cmdinfo_t reflink_cmd; +static void +dedupe_help(void) +{ + printf(_("\n\ + Links a range of bytes (in block size increments) from a file into a range\n\ + of bytes in the open file. The contents of both file ranges must match.\n\ +\n\ + Example:\n\ + 'dedupe some_file 0 4096 32768' - links 32768 bytes from some_file at\n\ + offset 0 to into the open file at\n\ + position 4096\n\ +\n\ + Reflink a range of blocks from a given input file to the open file. Both\n\ + files share the same range of physical disk blocks; a write to the shared\n\ + range of either file should result in the write landing in a new block and\n\ + that range of the file being remapped (i.e. copy-on-write). Both files\n\ + must reside on the same filesystem, and the contents of both ranges must\n\ + match.\n\ +")); +} + +static uint64_t +dedupe_ioctl( + int fd, + uint64_t soffset, + uint64_t doffset, + uint64_t len, + int *ops) +{ + struct xfs_extent_data *args; + struct xfs_extent_data_info *info; + int error; + uint64_t deduped = 0; + + args = calloc(1, sizeof(struct xfs_extent_data) + + sizeof(struct xfs_extent_data_info)); + if (!args) + goto done; + info = (struct xfs_extent_data_info *)(args + 1); + args->logical_offset = soffset; + args->length = len; + args->dest_count = 1; + info->fd = file->fd; + info->logical_offset = doffset; + + while (args->length > 0) { + error = ioctl(fd, XFS_IOC_FILE_EXTENT_SAME, args); + if (error) { + perror("XFS_IOC_FILE_EXTENT_SAME"); + goto done; + } + if (info->status < 0) { + printf("dedupe: %s\n", _(strerror(-info->status))); + goto done; + } + if (info->status == XFS_EXTENT_DATA_DIFFERS) { + printf(_("Extents did not match.\n")); + goto done; + } + if (info->bytes_deduped == 0 || + info->bytes_deduped > args->length) + break; + + (*ops)++; + args->logical_offset += info->bytes_deduped; + info->logical_offset += info->bytes_deduped; + args->length -= info->bytes_deduped; + deduped += info->bytes_deduped; + } +done: + free(args); + return deduped; +} + +static int +dedupe_f( + int argc, + char **argv) +{ + off64_t soffset, doffset; + long long count, total; + char *infile; + int condensed, quiet_flag; + size_t fsblocksize, fssectsize; + struct timeval t1, t2; + int c, ops = 0, fd = -1; + + condensed = quiet_flag = 0; + init_cvtnum(&fsblocksize, &fssectsize); + + while ((c = getopt(argc, argv, "Cq")) != EOF) { + switch (c) { + case 'C': + condensed = 1; + break; + case 'q': + quiet_flag = 1; + break; + default: + return command_usage(&dedupe_cmd); + } + } + if (optind != argc - 4) + return command_usage(&dedupe_cmd); + infile = argv[optind]; + optind++; + soffset = cvtnum(fsblocksize, fssectsize, argv[optind]); + if (soffset < 0) { + printf(_("non-numeric src offset argument -- %s\n"), argv[optind]); + return 0; + } + optind++; + doffset = cvtnum(fsblocksize, fssectsize, argv[optind]); + if (doffset < 0) { + printf(_("non-numeric dest offset argument -- %s\n"), argv[optind]); + return 0; + } + optind++; + count = cvtnum(fsblocksize, fssectsize, argv[optind]); + if (count < 1) { + printf(_("non-positive length argument -- %s\n"), argv[optind]); + return 0; + } + + fd = openfile(infile, NULL, IO_READONLY, 0); + if (fd < 0) + return 0; + + gettimeofday(&t1, NULL); + total = dedupe_ioctl(fd, soffset, doffset, count, &ops); + if (ops == 0 || quiet_flag) + goto done; + gettimeofday(&t2, NULL); + t2 = tsub(t2, t1); + + report_io_times("deduped", &t2, (long long)doffset, count, total, ops, + condensed); +done: + close(fd); + return 0; +} + static void reflink_help(void) { - printf(_( -"\n" -" Links a range of bytes (in block size increments) from a file into a range \n" -" of bytes in the open file. The two extent ranges need not contain identical\n" -" data. \n" -"\n" -" Example:\n" -" 'reflink some_file 0 4096 32768' - links 32768 bytes from some_file at \n" -" offset 0 to into the open file at \n" -" position 4096\n" -" 'reflink some_file' - links all bytes from some_file into the open file\n" -" at position 0\n" -"\n" -" Reflink a range of blocks from a given input file to the open file. Both\n" -" files share the same range of physical disk blocks; a write to the shared\n" -" range of either file should result in the write landing in a new block and\n" -" that range of the file being remapped (i.e. copy-on-write). Both files\n" -" must reside on the same filesystem.\n" -" -w -- call fdatasync(2) at the end (included in timing results)\n" -" -W -- call fsync(2) at the end (included in timing results)\n" -"\n")); + printf(_("\n\ + Links a range of bytes (in block size increments) from a file into a range\n\ + of bytes in the open file. The two extent ranges need not contain identical\n\ + data.\n\ +\n\ + Example:\n\ + 'reflink some_file 0 4096 32768' - links 32768 bytes from some_file at\n\ + offset 0 to into the open file at\n\ + position 4096\n\ + 'reflink some_file' - links all bytes from some_file into the open file\n\ + at position 0\n\ +\n\ + Reflink a range of blocks from a given input file to the open file. Both\n\ + files share the same range of physical disk blocks; a write to the shared\n\ + range of either file should result in the write landing in a new block and\n\ + that range of the file being remapped (i.e. copy-on-write). Both files\n\ + must reside on the same filesystem.\n\ +")); +} + +static uint64_t +reflink_ioctl( + int fd, + uint64_t soffset, + uint64_t doffset, + uint64_t len, + int *ops) +{ + struct xfs_clone_args args; + int error; + + if (len) { + args.src_fd = fd; + args.src_offset = soffset; + args.src_length = len; + args.dest_offset = doffset; + error = ioctl(file->fd, XFS_IOC_CLONE_RANGE, &args); + if (error) + perror("XFS_IOC_CLONE_RANGE"); + } else { + error = ioctl(file->fd, XFS_IOC_CLONE, fd); + if (error) + perror("XFS_IOC_CLONE"); + } + if (!error) + (*ops)++; + return error ? 0 : len; } static int @@ -56,32 +225,25 @@ reflink_f( int argc, char **argv) { - off64_t soffset = 0, doffset = 0; + off64_t soffset, doffset; long long count = 0, total; - char s1[64], s2[64], ts[64]; char *infile = NULL; - int Cflag, qflag, wflag, Wflag; - struct xfs_ioctl_clone_range_args args; + int condensed, quiet_flag; size_t fsblocksize, fssectsize; struct timeval t1, t2; - int c, fd = -1; + int c, ops = 0, fd = -1; - Cflag = qflag = wflag = Wflag = 0; + condensed = quiet_flag = 0; + doffset = soffset = 0; init_cvtnum(&fsblocksize, &fssectsize); - while ((c = getopt(argc, argv, "CqwW")) != EOF) { + while ((c = getopt(argc, argv, "Cq")) != EOF) { switch (c) { case 'C': - Cflag = 1; + condensed = 1; break; case 'q': - qflag = 1; - break; - case 'w': - wflag = 1; - break; - case 'W': - Wflag = 1; + quiet_flag = 1; break; default: return command_usage(&reflink_cmd); @@ -112,50 +274,19 @@ reflink_f( } clone_all: - c = IO_READONLY; - fd = openfile(infile, NULL, c, 0); + fd = openfile(infile, NULL, IO_READONLY, 0); if (fd < 0) return 0; gettimeofday(&t1, NULL); - if (count) { - args.src_fd = fd; - args.src_offset = soffset; - args.src_length = count; - args.dest_offset = doffset; - c = ioctl(file->fd, XFS_IOC_CLONE_RANGE, &args); - } else { - c = ioctl(file->fd, XFS_IOC_CLONE, fd); - } - if (c < 0) { - perror(_("reflink")); - goto done; - } - total = count; - c = 1; - if (Wflag) - fsync(file->fd); - if (wflag) - fdatasync(file->fd); - if (qflag) + total = reflink_ioctl(fd, soffset, doffset, count, &ops); + if (ops == 0 || quiet_flag) goto done; gettimeofday(&t2, NULL); t2 = tsub(t2, t1); - /* Finally, report back -- -C gives a parsable format */ - timestr(&t2, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0); - if (!Cflag) { - cvtstr((double)total, s1, sizeof(s1)); - cvtstr(tdiv((double)total, t2), s2, sizeof(s2)); - printf(_("linked %lld/%lld bytes at offset %lld\n"), - total, count, (long long)doffset); - printf(_("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n"), - s1, c, ts, s2, tdiv((double)c, t2)); - } else {/* bytes,ops,time,bytes/sec,ops/sec */ - printf("%lld,%d,%s,%.3f,%.3f\n", - total, c, ts, - tdiv((double)total, t2), tdiv((double)c, t2)); - } + report_io_times("linked", &t2, (long long)doffset, count, total, ops, + condensed); done: close(fd); return 0; @@ -177,4 +308,18 @@ _("infile src_off dst_off len"); reflink_cmd.help = reflink_help; add_command(&reflink_cmd); + + dedupe_cmd.name = "dedupe"; + dedupe_cmd.altname = "dd"; + dedupe_cmd.cfunc = dedupe_f; + dedupe_cmd.argmin = 4; + dedupe_cmd.argmax = -1; + dedupe_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; + dedupe_cmd.args = +_("infile src_off dst_off len"); + dedupe_cmd.oneline = + _("dedupes a number of bytes at a specified offset"); + dedupe_cmd.help = dedupe_help; + + add_command(&dedupe_cmd); } diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h index c14903ed9..d8b733ac4 100644 --- a/libxfs/xfs_fs.h +++ b/libxfs/xfs_fs.h @@ -569,41 +569,46 @@ typedef struct xfs_swapext /* XFS_IOC_GETFSUUID ---------- deprecated 140 */ /* reflink ioctls; these MUST match the btrfs ioctl definitions */ -struct xfs_ioctl_clone_range_args { +/* from struct btrfs_ioctl_clone_range_args */ +struct xfs_clone_args { __s64 src_fd; __u64 src_offset; __u64 src_length; __u64 dest_offset; }; -#define XFS_SAME_DATA_DIFFERS 1 -/* For extent-same ioctl */ -struct xfs_ioctl_file_extent_same_info { +/* extent-same (dedupe) ioctls; these MUST match the btrfs ioctl definitions */ +#define XFS_EXTENT_DATA_SAME 0 +#define XFS_EXTENT_DATA_DIFFERS 1 + +/* from struct btrfs_ioctl_file_extent_same_info */ +struct xfs_extent_data_info { __s64 fd; /* in - destination file */ __u64 logical_offset; /* in - start of extent in destination */ __u64 bytes_deduped; /* out - total # of bytes we were able * to dedupe from this file */ /* status of this dedupe operation: - * 0 if dedup succeeds * < 0 for error - * == XFS_SAME_DATA_DIFFERS if data differs + * == XFS_EXTENT_DATA_SAME if dedupe succeeds + * == XFS_EXTENT_DATA_DIFFERS if data differs */ __s32 status; /* out - see above description */ __u32 reserved; }; -struct xfs_ioctl_file_extent_same_args { +/* from struct btrfs_ioctl_file_extent_same_args */ +struct xfs_extent_data { __u64 logical_offset; /* in - start of extent in source */ __u64 length; /* in - length of extent */ __u16 dest_count; /* in - total elements in info array */ __u16 reserved1; __u32 reserved2; - struct xfs_ioctl_file_extent_same_info info[0]; + struct xfs_extent_data_info info[0]; }; #define XFS_IOC_CLONE _IOW (0x94, 9, int) -#define XFS_IOC_CLONE_RANGE _IOW (0x94, 13, struct xfs_ioctl_clone_range_args) -#define XFS_IOC_FILE_EXTENT_SAME _IOWR(0x94, 54, struct xfs_ioctl_file_extent_same_args) +#define XFS_IOC_CLONE_RANGE _IOW (0x94, 13, struct xfs_clone_args) +#define XFS_IOC_FILE_EXTENT_SAME _IOWR(0x94, 54, struct xfs_extent_data) #ifndef HAVE_BBMACROS /* diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8 index 305335c52..7fd747810 100644 --- a/man/man8/xfs_io.8 +++ b/man/man8/xfs_io.8 @@ -494,7 +494,7 @@ both data and holes are displayed together or performing a recusively display. .PD .TP .TP -.BI "reflink [ \-w ] [ \-W ] src_file [src_offset dst_offset length]" +.BI "reflink [ \-C ] [ \-q ] src_file [src_offset dst_offset length]" On filesystems that support the .B XFS_IOC_CLONE_RANGE or @@ -515,20 +515,16 @@ are omitted, all contents of src_file will be reflinked into the open file. .RS 1.0i .PD 0 .TP 0.4i -.B \-w -Call -.BR fdatasync (2) -after executing the ioctl. +.B \-C +Print timing statistics in a condensed format. .TP -.B \-W -Call -.BR fsync (2) -after executing the command. +.B \-q +Do not print timing statistics at all. .RE .PD .TP .TP -.BI "dedupe [ \-w ] [ \-W ] src_file src_offset dst_offset length" +.BI "dedupe [ \-C ] [ \-q ] src_file src_offset dst_offset length" On filesystems that support the .B XFS_IOC_FILE_EXTENT_SAME or @@ -548,16 +544,11 @@ on write") in the affected file, leaving the other file(s) unchanged. .RS 1.0i .PD 0 .TP 0.4i -.B \-w -Call -.BR fdatasync (2) -after executing the ioctl. -.TP -.B \-W -Call -.BR fsync (2) -after executing the command. +.B \-C +Print timing statistics in a condensed format. .TP +.B \-q +Do not print timing statistics at all. .SH MEMORY MAPPED I/O COMMANDS .TP -- 2.47.2