btrfs: defrag: extend ioctl to accept compression levels
authorDaniel Vacek <neelx@suse.com>
Thu, 6 Mar 2025 13:15:35 +0000 (14:15 +0100)
committerDavid Sterba <dsterba@suse.com>
Tue, 18 Mar 2025 19:35:50 +0000 (20:35 +0100)
The zstd and zlib compression types support setting compression levels.
Enhance the defrag interface to specify the levels as well. For zstd the
negative (realtime) levels are also accepted.

Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Daniel Vacek <neelx@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/btrfs_inode.h
fs/btrfs/compression.c
fs/btrfs/compression.h
fs/btrfs/defrag.c
fs/btrfs/fs.h
fs/btrfs/inode.c
include/uapi/linux/btrfs.h

index ca1cd600f5d21a399cde6d28257942845c1abb3d..cd5c8b595ebb62a996e9b3f4f34dd36c5c13745f 100644 (file)
@@ -145,6 +145,7 @@ struct btrfs_inode {
         * different from prop_compress and takes precedence if set.
         */
        u8 defrag_compress;
+       s8 defrag_compress_level;
 
        /*
         * Lock for counters and all fields used to determine if the inode is in
index 1fe154e7cc0283bb6033922207f85df0b7910143..e7f8ee5d48a4f166533a01dfa3bd9d047c1de2e9 100644 (file)
@@ -980,6 +980,16 @@ static int btrfs_compress_set_level(unsigned int type, int level)
        return level;
 }
 
+/*
+ * Check whether the @level is within the valid range for the given type.
+ */
+bool btrfs_compress_level_valid(unsigned int type, int level)
+{
+       const struct btrfs_compress_op *ops = btrfs_compress_op[type];
+
+       return ops->min_level <= level && level <= ops->max_level;
+}
+
 /* Wrapper around find_get_page(), with extra error message. */
 int btrfs_compress_filemap_get_folio(struct address_space *mapping, u64 start,
                                     struct folio **in_folio_ret)
index 933178f03d8f8bc01a080bc2580ada04939c6fbf..df198623cc08435e3f954857839dcdbf553e3520 100644 (file)
@@ -83,6 +83,7 @@ static inline u32 btrfs_calc_input_length(u64 range_end, u64 cur)
 int __init btrfs_init_compress(void);
 void __cold btrfs_exit_compress(void);
 
+bool btrfs_compress_level_valid(unsigned int type, int level);
 int btrfs_compress_folios(unsigned int type, int level, struct address_space *mapping,
                          u64 start, struct folio **folios, unsigned long *out_folios,
                         unsigned long *total_in, unsigned long *total_out);
index 18f0704263f321f93c9eb62b4c9e76cd819cea0b..ae0b92b96345607bd8abb87834850f649835c09a 100644 (file)
@@ -1363,6 +1363,7 @@ int btrfs_defrag_file(struct btrfs_inode *inode, struct file_ra_state *ra,
        u64 last_byte;
        bool do_compress = (range->flags & BTRFS_DEFRAG_RANGE_COMPRESS);
        int compress_type = BTRFS_COMPRESS_ZLIB;
+       int compress_level = 0;
        int ret = 0;
        u32 extent_thresh = range->extent_thresh;
        pgoff_t start_index;
@@ -1376,10 +1377,21 @@ int btrfs_defrag_file(struct btrfs_inode *inode, struct file_ra_state *ra,
                return -EINVAL;
 
        if (do_compress) {
-               if (range->compress_type >= BTRFS_NR_COMPRESS_TYPES)
-                       return -EINVAL;
-               if (range->compress_type)
-                       compress_type = range->compress_type;
+               if (range->flags & BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL) {
+                       if (range->compress.type >= BTRFS_NR_COMPRESS_TYPES)
+                               return -EINVAL;
+                       if (range->compress.type) {
+                               compress_type  = range->compress.type;
+                               compress_level = range->compress.level;
+                               if (!btrfs_compress_level_valid(compress_type, compress_level))
+                                       return -EINVAL;
+                       }
+               } else {
+                       if (range->compress_type >= BTRFS_NR_COMPRESS_TYPES)
+                               return -EINVAL;
+                       if (range->compress_type)
+                               compress_type = range->compress_type;
+               }
        }
 
        if (extent_thresh == 0)
@@ -1430,8 +1442,10 @@ int btrfs_defrag_file(struct btrfs_inode *inode, struct file_ra_state *ra,
                        btrfs_inode_unlock(inode, 0);
                        break;
                }
-               if (do_compress)
+               if (do_compress) {
                        inode->defrag_compress = compress_type;
+                       inode->defrag_compress_level = compress_level;
+               }
                ret = defrag_one_cluster(inode, ra, cur,
                                cluster_end + 1 - cur, extent_thresh,
                                newer_than, do_compress, &sectors_defragged,
index 6710da812a380fe5ddb4f9157583014b48b7c653..c76d432168447c2b95f85f8eb0c5f3271470f756 100644 (file)
@@ -497,7 +497,7 @@ struct btrfs_fs_info {
        u64 last_trans_log_full_commit;
        unsigned long long mount_opt;
 
-       unsigned long compress_type:4;
+       int compress_type;
        int compress_level;
        u32 commit_interval;
        /*
index 84a6f764f28bc461047fd3e5bc75e96ea8317f0c..3a8e0858803c147e93a3e4b2f1ed864fd708d5fa 100644 (file)
@@ -877,6 +877,7 @@ static void compress_file_range(struct btrfs_work *work)
        unsigned int poff;
        int i;
        int compress_type = fs_info->compress_type;
+       int compress_level = fs_info->compress_level;
 
        inode_should_defrag(inode, start, end, end - start + 1, SZ_16K);
 
@@ -959,13 +960,15 @@ again:
                goto cleanup_and_bail_uncompressed;
        }
 
-       if (inode->defrag_compress)
+       if (inode->defrag_compress) {
                compress_type = inode->defrag_compress;
-       else if (inode->prop_compress)
+               compress_level = inode->defrag_compress_level;
+       } else if (inode->prop_compress) {
                compress_type = inode->prop_compress;
+       }
 
        /* Compression level is applied here. */
-       ret = btrfs_compress_folios(compress_type, fs_info->compress_level,
+       ret = btrfs_compress_folios(compress_type, compress_level,
                                    mapping, start, folios, &nr_folios, &total_in,
                                    &total_compressed);
        if (ret)
index d3b222d7af24075b7e8eaa0ccdd1769086dc3047..dd02160015b2bad78614d52b7ddfc81ba05c988c 100644 (file)
@@ -615,7 +615,9 @@ struct btrfs_ioctl_clone_range_args {
  */
 #define BTRFS_DEFRAG_RANGE_COMPRESS 1
 #define BTRFS_DEFRAG_RANGE_START_IO 2
+#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
 #define BTRFS_DEFRAG_RANGE_FLAGS_SUPP  (BTRFS_DEFRAG_RANGE_COMPRESS |          \
+                                        BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL |    \
                                         BTRFS_DEFRAG_RANGE_START_IO)
 
 struct btrfs_ioctl_defrag_range_args {
@@ -640,10 +642,18 @@ struct btrfs_ioctl_defrag_range_args {
 
        /*
         * which compression method to use if turning on compression
-        * for this defrag operation.  If unspecified, zlib will
-        * be used
+        * for this defrag operation. If unspecified, zlib will be
+        * used. If compression level is also being specified, set the
+        * BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL flag and fill the compress
+        * member structure instead of the compress_type field.
         */
-       __u32 compress_type;
+       union {
+               __u32 compress_type;
+               struct {
+                       __u8 type;
+                       __s8 level;
+               } compress;
+       };
 
        /* spare for later */
        __u32 unused[4];