+++ /dev/null
-/*
- * 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 <sys/uio.h>
-#include <xfs/xfs.h>
-#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);
-}
#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
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);
}
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;
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);
}
/* 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
/*