diff mbox series

[v2,2/2] btrfs-progs: cmds/filesystem: add --usage-ratio and --wasted-bytes for defrag

Message ID 90994d9b0f0453baf45256e2c2d3290041537fc7.1710214834.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs-progs: cmds/filesystem: add --usage-ratio and --wasted-bytes options | expand

Commit Message

Qu Wenruo March 12, 2024, 3:57 a.m. UTC
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(-)
diff mbox series

Patch

diff --git a/Documentation/btrfs-filesystem.rst b/Documentation/btrfs-filesystem.rst
index 83207a5a263e..213be6ff4a14 100644
--- a/Documentation/btrfs-filesystem.rst
+++ b/Documentation/btrfs-filesystem.rst
@@ -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
 
diff --git a/cmds/filesystem.c b/cmds/filesystem.c
index c9930a02d879..9cc224454e4c 100644
--- a/cmds/filesystem.c
+++ b/cmds/filesystem.c
@@ -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