@@ -144,6 +144,24 @@ 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.
+ --lone-ratio <ratio>
+ For a lone file extent (which has no adjacent file exctent), if its utilizing
+ less than (ratio / 65536) 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, 65536].
+
+ --lone-wasted-bytes <bytes>[kKmMgGgtTpPeE]
+ For a lone file extent (which has no adjacent file exctent), if defragging
+ the file extent can free up more than <bytes> (excluding the file extent
+ size), the file extent would be defragged.
+
+ 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
@@ -915,6 +915,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("--lone-ratio RATIO", "defrag lone extents with utilization ratio lower than RATIO/65536"),
+ OPTLINE("--lone-wasted-bytes SIZE", "defrag lone extents which can free up more bytes than SIZE"),
OPTLINE("-v", "deprecated, alias for global -v option"),
HELPINFO_INSERT_GLOBALS,
HELPINFO_INSERT_VERBOSE,
@@ -930,13 +932,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_LONE_RATIO |
+ BTRFS_DEFRAG_RANGE_LONE_WASTED_BYTES)) {
+ /*
+ * Older kernel, no lone extent handling support.
+ * Retry with lone extent fine-tunning disabled.
+ */
+ range->flags &= ~(BTRFS_DEFRAG_RANGE_LONE_RATIO |
+ BTRFS_DEFRAG_RANGE_LONE_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,
@@ -947,13 +968,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_LONE_RATIO |
+ BTRFS_DEFRAG_RANGE_LONE_WASTED_BYTES);
+ range.lone_ratio = defrag_global_range.lone_ratio;
+ range.lone_wasted_bytes = defrag_global_range.lone_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))
@@ -1014,6 +1039,8 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
bool recursive = false;
int ret = 0;
int compress_type = BTRFS_COMPRESS_NONE;
+ u32 lone_ratio = SZ_64K / 16;
+ u32 lone_wasted_bytes = SZ_16M;
DIR *dirstream;
/*
@@ -1048,9 +1075,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_LONE_RATIO, GETOPT_VAL_LONE_WASTED_BYTES };
static const struct option long_options[] = {
{ "step", required_argument, NULL, GETOPT_VAL_STEP },
+ { "lone-ratio", required_argument, NULL,
+ GETOPT_VAL_LONE_RATIO },
+ { "lone-wasted-bytes", required_argument, NULL,
+ GETOPT_VAL_LONE_WASTED_BYTES },
{ NULL, 0, NULL, 0 }
};
int c;
@@ -1100,6 +1132,17 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
defrag_global_step = SZ_256K;
}
break;
+ case GETOPT_VAL_LONE_RATIO:
+ lone_ratio = arg_strtou64(optarg);
+ if (lone_ratio > 65536) {
+ error("invalid ratio, has %u expect [0, 65536]",
+ lone_ratio);
+ exit(1);
+ }
+ break;
+ case GETOPT_VAL_LONE_WASTED_BYTES:
+ lone_wasted_bytes = arg_strtou64_with_suffix(optarg);
+ break;
default:
usage_unknown_option(cmd, argv);
}
@@ -1118,6 +1161,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.lone_ratio = lone_ratio;
+ defrag_global_range.lone_wasted_bytes = lone_wasted_bytes;
+ defrag_global_range.flags |= (BTRFS_DEFRAG_RANGE_LONE_RATIO |
+ BTRFS_DEFRAG_RANGE_LONE_WASTED_BYTES);
/*
* Look for directory arguments and warn if the recursive mode is not
This allows defrag to fine-tune long file extents (which have no adjacent file extents) with new "--lone-ratio" and "--lone-wasted-bytes" options. The default behavior is, defrag would go with 4096 as the ratio (1/16), and 16M as the wasted bytes on the first try. If the kernel doesn't support it, then retry without lone file extents handling. Signed-off-by: Qu Wenruo <wqu@suse.com> --- Documentation/btrfs-filesystem.rst | 18 ++++++++++ cmds/filesystem.c | 53 ++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-)