btrfs: add workspace manager initialization for zstd

This involves:

- Add zstd_alloc_workspace_manager() and zstd_free_workspace_manager()
  Those two functions will accept an fs_info pointer, and alloc/free
  fs_info->compr_wsm[BTRFS_COMPRESS_ZSTD] pointer.

- Add btrfs_alloc_compress_wsm() and btrfs_free_compress_wsm()
  Those are helpers allocating the workspace managers for all
  algorithms.
  For now only zstd is supported, and the timing is a little unusual,
  the btrfs_alloc_compress_wsm() should only be called after the
  sectorsize being initialized.

  Meanwhile btrfs_free_fs_info_compress() is called in
  btrfs_free_fs_info().

- Move the definition of btrfs_compression_type to "fs.h"
  The reason is that "compression.h" has already included "fs.h", thus
  we can not just include "compression.h" to get the definition of
  BTRFS_NR_COMPRESS_TYPES to define fs_info::compr_wsm[].

For now the per-fs zstd workspace manager won't really have any effect,
and all compression is still going through the global workspace manager.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Qu Wenruo 2025-08-13 15:05:20 +09:30 committed by David Sterba
parent 2c5cca03c1
commit 330f02b136
5 changed files with 93 additions and 10 deletions

View File

@ -1097,6 +1097,24 @@ int btrfs_decompress(int type, const u8 *data_in, struct folio *dest_folio,
return ret;
}
int btrfs_alloc_compress_wsm(struct btrfs_fs_info *fs_info)
{
int ret;
ret = zstd_alloc_workspace_manager(fs_info);
if (ret < 0)
goto error;
return 0;
error:
btrfs_free_compress_wsm(fs_info);
return ret;
}
void btrfs_free_compress_wsm(struct btrfs_fs_info *fs_info)
{
zstd_free_workspace_manager(fs_info);
}
int __init btrfs_init_compress(void)
{
if (bioset_init(&btrfs_compressed_bioset, BIO_POOL_SIZE,

View File

@ -89,6 +89,9 @@ static inline u32 btrfs_calc_input_length(struct folio *folio, u64 range_end, u6
return min(range_end, folio_end(folio)) - cur;
}
int btrfs_alloc_compress_wsm(struct btrfs_fs_info *fs_info);
void btrfs_free_compress_wsm(struct btrfs_fs_info *fs_info);
int __init btrfs_init_compress(void);
void __cold btrfs_exit_compress(void);
@ -112,16 +115,6 @@ int btrfs_compress_str2level(unsigned int type, const char *str, int *level_ret)
struct folio *btrfs_alloc_compr_folio(void);
void btrfs_free_compr_folio(struct folio *folio);
enum btrfs_compression_type {
BTRFS_COMPRESS_NONE = 0,
BTRFS_COMPRESS_ZLIB = 1,
BTRFS_COMPRESS_LZO = 2,
BTRFS_COMPRESS_ZSTD = 3,
BTRFS_NR_COMPRESS_TYPES = 4,
BTRFS_DEFRAG_DONT_COMPRESS,
};
struct workspace_manager {
struct list_head idle_ws;
spinlock_t ws_lock;
@ -188,6 +181,8 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb);
int zstd_decompress(struct list_head *ws, const u8 *data_in,
struct folio *dest_folio, unsigned long dest_pgoff, size_t srclen,
size_t destlen);
int zstd_alloc_workspace_manager(struct btrfs_fs_info *fs_info);
void zstd_free_workspace_manager(struct btrfs_fs_info *fs_info);
void zstd_init_workspace_manager(struct btrfs_fs_info *fs_info);
void zstd_cleanup_workspace_manager(void);
struct list_head *zstd_alloc_workspace(struct btrfs_fs_info *fs_info, int level);

View File

@ -1248,6 +1248,7 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info)
if (fs_info->fs_devices)
btrfs_close_devices(fs_info->fs_devices);
btrfs_free_compress_wsm(fs_info);
percpu_counter_destroy(&fs_info->stats_read_blocks);
percpu_counter_destroy(&fs_info->dirty_metadata_bytes);
percpu_counter_destroy(&fs_info->delalloc_bytes);
@ -3407,6 +3408,9 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
*/
fs_info->max_inline = min_t(u64, fs_info->max_inline, fs_info->sectorsize);
ret = btrfs_alloc_compress_wsm(fs_info);
if (ret)
goto fail_sb_buffer;
ret = btrfs_init_workqueues(fs_info);
if (ret)
goto fail_sb_buffer;

View File

@ -306,6 +306,16 @@ enum {
#define BTRFS_WARNING_COMMIT_INTERVAL (300)
#define BTRFS_DEFAULT_MAX_INLINE (2048)
enum btrfs_compression_type {
BTRFS_COMPRESS_NONE = 0,
BTRFS_COMPRESS_ZLIB = 1,
BTRFS_COMPRESS_LZO = 2,
BTRFS_COMPRESS_ZSTD = 3,
BTRFS_NR_COMPRESS_TYPES = 4,
BTRFS_DEFRAG_DONT_COMPRESS,
};
struct btrfs_dev_replace {
/* See #define above */
u64 replace_state;
@ -508,6 +518,9 @@ struct btrfs_fs_info {
u64 last_trans_log_full_commit;
unsigned long long mount_opt;
/* Compress related structures. */
void *compr_wsm[BTRFS_NR_COMPRESS_TYPES];
int compress_type;
int compress_level;
u32 commit_interval;

View File

@ -182,6 +182,36 @@ static void zstd_calc_ws_mem_sizes(void)
}
}
int zstd_alloc_workspace_manager(struct btrfs_fs_info *fs_info)
{
struct zstd_workspace_manager *zwsm;
struct list_head *ws;
ASSERT(fs_info->compr_wsm[BTRFS_COMPRESS_ZSTD] == NULL);
zwsm = kzalloc(sizeof(*zwsm), GFP_KERNEL);
if (!zwsm)
return -ENOMEM;
zstd_calc_ws_mem_sizes();
zwsm->ops = &btrfs_zstd_compress;
spin_lock_init(&zwsm->lock);
init_waitqueue_head(&zwsm->wait);
timer_setup(&zwsm->timer, zstd_reclaim_timer_fn, 0);
INIT_LIST_HEAD(&zwsm->lru_list);
for (int i = 0; i < ZSTD_BTRFS_MAX_LEVEL; i++)
INIT_LIST_HEAD(&zwsm->idle_ws[i]);
fs_info->compr_wsm[BTRFS_COMPRESS_ZSTD] = zwsm;
ws = zstd_alloc_workspace(fs_info, ZSTD_BTRFS_MAX_LEVEL);
if (IS_ERR(ws)) {
btrfs_warn(NULL, "cannot preallocate zstd compression workspace");
} else {
set_bit(ZSTD_BTRFS_MAX_LEVEL - 1, &zwsm->active_map);
list_add(ws, &zwsm->idle_ws[ZSTD_BTRFS_MAX_LEVEL - 1]);
}
return 0;
}
void zstd_init_workspace_manager(struct btrfs_fs_info *fs_info)
{
struct list_head *ws;
@ -207,6 +237,29 @@ void zstd_init_workspace_manager(struct btrfs_fs_info *fs_info)
}
}
void zstd_free_workspace_manager(struct btrfs_fs_info *fs_info)
{
struct zstd_workspace_manager *zwsm = fs_info->compr_wsm[BTRFS_COMPRESS_ZSTD];
struct workspace *workspace;
if (!zwsm)
return;
fs_info->compr_wsm[BTRFS_COMPRESS_ZSTD] = NULL;
spin_lock_bh(&zwsm->lock);
for (int i = 0; i < ZSTD_BTRFS_MAX_LEVEL; i++) {
while (!list_empty(&zwsm->idle_ws[i])) {
workspace = container_of(zwsm->idle_ws[i].next,
struct workspace, list);
list_del(&workspace->list);
list_del(&workspace->lru_list);
zstd_free_workspace(&workspace->list);
}
}
spin_unlock_bh(&zwsm->lock);
timer_delete_sync(&zwsm->timer);
kfree(zwsm);
}
void zstd_cleanup_workspace_manager(void)
{
struct workspace *workspace;