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>
         * 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
 
        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)
 
 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);
 
        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;
                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)
                        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, §ors_defragged,
 
        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;
        /*
 
        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);
 
                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)
 
  */
 #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 {
 
        /*
         * 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];