&btrfs_zstd_compress,
 };
 
-static struct list_head *alloc_workspace(int type, unsigned int level)
+static struct list_head *alloc_workspace(int type, int level)
 {
        switch (type) {
        case BTRFS_COMPRESS_NONE: return alloc_heuristic_ws();
  * Preallocation makes a forward progress guarantees and we do not return
  * errors.
  */
-struct list_head *btrfs_get_workspace(int type, unsigned int level)
+struct list_head *btrfs_get_workspace(int type, int level)
 {
        struct workspace_manager *wsm;
        struct list_head *workspace;
  * Adjust @level according to the limits of the compression algorithm or
  * fallback to default
  */
-static unsigned int btrfs_compress_set_level(int type, unsigned level)
+static int btrfs_compress_set_level(unsigned int type, int level)
 {
        const struct btrfs_compress_op *ops = btrfs_compress_op[type];
 
        if (level == 0)
                level = ops->default_level;
        else
-               level = min(level, ops->max_level);
+               level = min(max(level, ops->min_level), ops->max_level);
 
        return level;
 }
  * @total_out is an in/out parameter, must be set to the input length and will
  * be also used to return the total number of compressed bytes
  */
-int btrfs_compress_folios(unsigned int type_level, struct address_space *mapping,
+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)
 {
-       int type = btrfs_compress_type(type_level);
-       int level = btrfs_compress_level(type_level);
        const unsigned long orig_len = *total_out;
        struct list_head *workspace;
        int ret;
 
 /*
  * Convert the compression suffix (eg. after "zlib" starting with ":") to
- * level, unrecognized string will set the default level
+ * level, unrecognized string will set the default level. Negative level
+ * numbers are allowed.
  */
-unsigned int btrfs_compress_str2level(unsigned int type, const char *str)
+int btrfs_compress_str2level(unsigned int type, const char *str)
 {
-       unsigned int level = 0;
+       int level = 0;
        int ret;
 
        if (!type)
                return 0;
 
        if (str[0] == ':') {
-               ret = kstrtouint(str + 1, 10, &level);
+               ret = kstrtoint(str + 1, 10, &level);
                if (ret)
                        level = 0;
        }
 
        struct btrfs_bio bbio;
 };
 
-static inline unsigned int btrfs_compress_type(unsigned int type_level)
-{
-       return (type_level & 0xF);
-}
-
-static inline unsigned int btrfs_compress_level(unsigned int type_level)
-{
-       return ((type_level & 0xF0) >> 4);
-}
-
 /* @range_end must be exclusive. */
 static inline u32 btrfs_calc_input_length(u64 range_end, u64 cur)
 {
 int __init btrfs_init_compress(void);
 void __cold btrfs_exit_compress(void);
 
-int btrfs_compress_folios(unsigned int type_level, struct address_space *mapping,
+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);
 int btrfs_decompress(int type, const u8 *data_in, struct folio *dest_folio,
                                   bool writeback);
 void btrfs_submit_compressed_read(struct btrfs_bio *bbio);
 
-unsigned int btrfs_compress_str2level(unsigned int type, const char *str);
+int btrfs_compress_str2level(unsigned int type, const char *str);
 
 struct folio *btrfs_alloc_compr_folio(void);
 void btrfs_free_compr_folio(struct folio *folio);
        wait_queue_head_t ws_wait;
 };
 
-struct list_head *btrfs_get_workspace(int type, unsigned int level);
+struct list_head *btrfs_get_workspace(int type, int level);
 void btrfs_put_workspace(int type, struct list_head *ws);
 
 struct btrfs_compress_op {
        struct workspace_manager *workspace_manager;
        /* Maximum level supported by the compression algorithm */
-       unsigned int max_level;
-       unsigned int default_level;
+       int min_level;
+       int max_level;
+       int default_level;
 };
 
 /* The heuristic workspaces are managed via the 0th workspace manager */
                size_t destlen);
 void zstd_init_workspace_manager(void);
 void zstd_cleanup_workspace_manager(void);
-struct list_head *zstd_alloc_workspace(unsigned int level);
+struct list_head *zstd_alloc_workspace(int level);
 void zstd_free_workspace(struct list_head *ws);
-struct list_head *zstd_get_workspace(unsigned int level);
+struct list_head *zstd_get_workspace(int level);
 void zstd_put_workspace(struct list_head *ws);
 
 #endif
 
        unsigned long long mount_opt;
 
        unsigned long compress_type:4;
-       unsigned int compress_level;
+       int compress_level;
        u32 commit_interval;
        /*
         * It is a suggestive number, the read side is safe even it gets a
 
                compress_type = inode->prop_compress;
 
        /* Compression level is applied here. */
-       ret = btrfs_compress_folios(compress_type | (fs_info->compress_level << 4),
+       ret = btrfs_compress_folios(compress_type, fs_info->compress_level,
                                    mapping, start, folios, &nr_folios, &total_in,
                                    &total_compressed);
        if (ret)
 
        u32 thread_pool_size;
        unsigned long long mount_opt;
        unsigned long compress_type:4;
-       unsigned int compress_level;
+       int compress_level;
        refcount_t refs;
 };
 
 
 
 const struct btrfs_compress_op btrfs_zlib_compress = {
        .workspace_manager      = &wsm,
+       .min_level              = 1,
        .max_level              = 9,
        .default_level          = BTRFS_ZLIB_DEFAULT_LEVEL,
 };
 
 #define ZSTD_BTRFS_MAX_WINDOWLOG 17
 #define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
 #define ZSTD_BTRFS_DEFAULT_LEVEL 3
+#define ZSTD_BTRFS_MIN_LEVEL -15
 #define ZSTD_BTRFS_MAX_LEVEL 15
 /* 307s to avoid pathologically clashing with transaction commit */
 #define ZSTD_BTRFS_RECLAIM_JIFFIES (307 * HZ)
 
-static zstd_parameters zstd_get_btrfs_parameters(unsigned int level,
+static zstd_parameters zstd_get_btrfs_parameters(int level,
                                                 size_t src_len)
 {
        zstd_parameters params = zstd_get_params(level, src_len);
        void *mem;
        size_t size;
        char *buf;
-       unsigned int level;
-       unsigned int req_level;
+       int level;
+       int req_level;
        unsigned long last_used; /* jiffies */
        struct list_head list;
        struct list_head lru_list;
        return container_of(list, struct workspace, list);
 }
 
-void zstd_free_workspace(struct list_head *ws);
-struct list_head *zstd_alloc_workspace(unsigned int level);
+static inline int clip_level(int level)
+{
+       return max(0, level - 1);
+}
 
 /*
  * Timer callback to free unused workspaces.
        list_for_each_prev_safe(pos, next, &wsm.lru_list) {
                struct workspace *victim = container_of(pos, struct workspace,
                                                        lru_list);
-               unsigned int level;
+               int level;
 
                if (time_after(victim->last_used, reclaim_threshold))
                        break;
                list_del(&victim->list);
                zstd_free_workspace(&victim->list);
 
-               if (list_empty(&wsm.idle_ws[level - 1]))
-                       clear_bit(level - 1, &wsm.active_map);
+               if (list_empty(&wsm.idle_ws[level]))
+                       clear_bit(level, &wsm.active_map);
 
        }
 
 static void zstd_calc_ws_mem_sizes(void)
 {
        size_t max_size = 0;
-       unsigned int level;
+       int level;
 
-       for (level = 1; level <= ZSTD_BTRFS_MAX_LEVEL; level++) {
+       for (level = ZSTD_BTRFS_MIN_LEVEL; level <= ZSTD_BTRFS_MAX_LEVEL; level++) {
+               if (level == 0)
+                       continue;
                zstd_parameters params =
                        zstd_get_btrfs_parameters(level, ZSTD_BTRFS_MAX_INPUT);
                size_t level_size =
                              zstd_dstream_workspace_bound(ZSTD_BTRFS_MAX_INPUT));
 
                max_size = max_t(size_t, max_size, level_size);
-               zstd_ws_mem_sizes[level - 1] = max_size;
+               /* Use level 1 workspace size for all the fast mode negative levels. */
+               zstd_ws_mem_sizes[clip_level(level)] = max_size;
        }
 }
 
  * offer the opportunity to reclaim the workspace in favor of allocating an
  * appropriately sized one in the future.
  */
-static struct list_head *zstd_find_workspace(unsigned int level)
+static struct list_head *zstd_find_workspace(int level)
 {
        struct list_head *ws;
        struct workspace *workspace;
-       int i = level - 1;
+       int i = clip_level(level);
 
        spin_lock_bh(&wsm.lock);
        for_each_set_bit_from(i, &wsm.active_map, ZSTD_BTRFS_MAX_LEVEL) {
                        list_del_init(ws);
                        /* keep its place if it's a lower level using this */
                        workspace->req_level = level;
-                       if (level == workspace->level)
+                       if (clip_level(level) == workspace->level)
                                list_del(&workspace->lru_list);
                        if (list_empty(&wsm.idle_ws[i]))
                                clear_bit(i, &wsm.active_map);
  * attempt to allocate a new workspace.  If we fail to allocate one due to
  * memory pressure, go to sleep waiting for the max level workspace to free up.
  */
-struct list_head *zstd_get_workspace(unsigned int level)
+struct list_head *zstd_get_workspace(int level)
 {
        struct list_head *ws;
        unsigned int nofs_flag;
        spin_lock_bh(&wsm.lock);
 
        /* A node is only taken off the lru if we are the corresponding level */
-       if (workspace->req_level == workspace->level) {
+       if (clip_level(workspace->req_level) == workspace->level) {
                /* Hide a max level workspace from reclaim */
                if (list_empty(&wsm.idle_ws[ZSTD_BTRFS_MAX_LEVEL - 1])) {
                        INIT_LIST_HEAD(&workspace->lru_list);
                }
        }
 
-       set_bit(workspace->level - 1, &wsm.active_map);
-       list_add(&workspace->list, &wsm.idle_ws[workspace->level - 1]);
+       set_bit(workspace->level, &wsm.active_map);
+       list_add(&workspace->list, &wsm.idle_ws[workspace->level]);
        workspace->req_level = 0;
 
        spin_unlock_bh(&wsm.lock);
 
-       if (workspace->level == ZSTD_BTRFS_MAX_LEVEL)
+       if (workspace->level == clip_level(ZSTD_BTRFS_MAX_LEVEL))
                cond_wake_up(&wsm.wait);
 }
 
        kfree(workspace);
 }
 
-struct list_head *zstd_alloc_workspace(unsigned int level)
+struct list_head *zstd_alloc_workspace(int level)
 {
        struct workspace *workspace;
 
        if (!workspace)
                return ERR_PTR(-ENOMEM);
 
-       workspace->size = zstd_ws_mem_sizes[level - 1];
-       workspace->level = level;
+       /* Use level 1 workspace size for all the fast mode negative levels. */
+       workspace->size = zstd_ws_mem_sizes[clip_level(level)];
+       workspace->level = clip_level(level);
        workspace->req_level = level;
        workspace->last_used = jiffies;
        workspace->mem = kvmalloc(workspace->size, GFP_KERNEL | __GFP_NOWARN);
 const struct btrfs_compress_op btrfs_zstd_compress = {
        /* ZSTD uses own workspace manager */
        .workspace_manager = NULL,
+       .min_level      = ZSTD_BTRFS_MIN_LEVEL,
        .max_level      = ZSTD_BTRFS_MAX_LEVEL,
        .default_level  = ZSTD_BTRFS_DEFAULT_LEVEL,
 };