@@ -144,6 +144,27 @@ defragment [options] <file>|<dir> [<file>|<dir>...]
The range is default (the whole file) or given by *-s* and *-l*, split into
the steps or done in one go if the step is larger. Minimum range size is 256KiB.
+ --usage-ratio <ratio>
+ If a file extent which is not a defrag target (e.g. no mergeable
+ adjacent extent), but has a usage ratio lower than (ratio / 100)
+ of the on-disk extent size, it would also be defraged
+ for its potential to free up the on-disk extent.
+
+ Valid values are in the range [0, 100].
+
+ --wasted-bytes <bytes>[kKmMgGgtTpPeE]
+ If a file extent which is not a defrag target (e.g. no mergeable
+ adjacent extent), but has more "wasted" bytes (the difference
+ between the file extent and the on-disk extent size) than this
+ value, it would also be defragged for its potential to free up
+ on-disk extent.
+
+ Valid values are in the range [0, U32_MAX], but any value larger than 128K
+ would make no sense for compressed file extents, and any value larger than
+ 128M would make no sense for regular file extents.
+ As the largest file extent supported is 128K for compressed extent and 128M
+ otherwise, thus it's impossible to free up more bytes than those limits.
+
-v
(deprecated) alias for global *-v* option
@@ -914,6 +914,8 @@ static const char * const cmd_filesystem_defrag_usage[] = {
OPTLINE("-l len", "defragment only up to len bytes"),
OPTLINE("-t size", "target extent size hint (default: 32M)"),
OPTLINE("--step SIZE", "process the range in given steps, flush after each one"),
+ OPTLINE("--usage-ratio RATIO", "defrag extents with utilization ratio lower than RATIO%"),
+ OPTLINE("--wasted-bytes SIZE", "defrag extents which can free up more bytes than SIZE"),
OPTLINE("-v", "deprecated, alias for global -v option"),
HELPINFO_INSERT_GLOBALS,
HELPINFO_INSERT_VERBOSE,
@@ -929,13 +931,32 @@ static struct btrfs_ioctl_defrag_range_args defrag_global_range;
static int defrag_global_errors;
static u64 defrag_global_step;
+static int defrag_ioctl(int fd, struct btrfs_ioctl_defrag_range_args *range)
+{
+ int ret = 0;
+
+ ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, range);
+ if (ret < 0 && ((errno == EOPNOTSUPP) | (errno == ENOTTY)) &&
+ range->flags & (BTRFS_DEFRAG_RANGE_USAGE_RATIO |
+ BTRFS_DEFRAG_RANGE_WASTED_BYTES)) {
+ /*
+ * Older kernel, no ratio/wasted bytes fine-tunning support.
+ * Retry with ratio/wasted bytes fine-tunning disabled.
+ */
+ range->flags &= ~(BTRFS_DEFRAG_RANGE_USAGE_RATIO |
+ BTRFS_DEFRAG_RANGE_WASTED_BYTES);
+ ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, range);
+ }
+ return ret;
+}
+
static int defrag_range_in_steps(int fd, const struct stat *st) {
int ret = 0;
u64 end;
struct btrfs_ioctl_defrag_range_args range;
if (defrag_global_step == 0)
- return ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &defrag_global_range);
+ return defrag_ioctl(fd, &defrag_global_range);
/*
* If start is set but length is not within or beyond the u64 range,
@@ -946,13 +967,17 @@ static int defrag_range_in_steps(int fd, const struct stat *st) {
range = defrag_global_range;
range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
+ range.flags |= (BTRFS_DEFRAG_RANGE_USAGE_RATIO |
+ BTRFS_DEFRAG_RANGE_WASTED_BYTES);
+ range.usage_ratio = defrag_global_range.usage_ratio;
+ range.wasted_bytes = defrag_global_range.wasted_bytes;
while (range.start < end) {
u64 start;
range.len = defrag_global_step;
pr_verbose(LOG_VERBOSE, "defrag range step: start=%llu len=%llu step=%llu\n",
range.start, range.len, defrag_global_step);
- ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &range);
+ ret = defrag_ioctl(fd, &range);
if (ret < 0)
return ret;
if (check_add_overflow(range.start, defrag_global_step, &start))
@@ -1013,6 +1038,8 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
bool recursive = false;
int ret = 0;
int compress_type = BTRFS_COMPRESS_NONE;
+ u32 usage_ratio = 5;
+ u32 wasted_bytes = SZ_16M;
/*
* Kernel 4.19+ supports defragmention of files open read-only,
@@ -1046,9 +1073,14 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
defrag_global_errors = 0;
optind = 0;
while(1) {
- enum { GETOPT_VAL_STEP = GETOPT_VAL_FIRST };
+ enum { GETOPT_VAL_STEP = GETOPT_VAL_FIRST,
+ GETOPT_VAL_USAGE_RATIO, GETOPT_VAL_WASTED_BYTES };
static const struct option long_options[] = {
{ "step", required_argument, NULL, GETOPT_VAL_STEP },
+ { "usage-ratio", required_argument, NULL,
+ GETOPT_VAL_USAGE_RATIO },
+ { "wasted-bytes", required_argument, NULL,
+ GETOPT_VAL_WASTED_BYTES },
{ NULL, 0, NULL, 0 }
};
int c;
@@ -1098,6 +1130,17 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
defrag_global_step = SZ_256K;
}
break;
+ case GETOPT_VAL_USAGE_RATIO:
+ usage_ratio = arg_strtou64(optarg);
+ if (usage_ratio > 100) {
+ error("invalid ratio, has %u expect [0, 100]",
+ usage_ratio);
+ exit(1);
+ }
+ break;
+ case GETOPT_VAL_WASTED_BYTES:
+ wasted_bytes = arg_strtou64_with_suffix(optarg);
+ break;
default:
usage_unknown_option(cmd, argv);
}
@@ -1116,6 +1159,10 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
}
if (flush)
defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
+ defrag_global_range.usage_ratio = usage_ratio;
+ defrag_global_range.wasted_bytes = wasted_bytes;
+ defrag_global_range.flags |= (BTRFS_DEFRAG_RANGE_USAGE_RATIO |
+ BTRFS_DEFRAG_RANGE_WASTED_BYTES);
/*
* Look for directory arguments and warn if the recursive mode is not
This allows defrag subcommand to accept new "--usage-ratio" and "--wasted-bytes" options. These two options allows defrag to handle extents which have no adjacent mergable extents but has low usage ratio or high wasted bytes against its on-disk extent. The default behavior is, defrag would go with 5% as the usage_ratio and 16M as the wasted bytes on the first try. If the kernel doesn't support it, then retry without the fine tunning flags. Signed-off-by: Qu Wenruo <wqu@suse.com> --- Documentation/btrfs-filesystem.rst | 21 ++++++++++++ cmds/filesystem.c | 53 ++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 3 deletions(-)