Re: [PATCH] btrfs: zstd: keep the last max level workspace out of reclaim

Qu Wenruo <[email protected]>
Newsgroups org.kernel.vger.linux-btrfs,org.kernel.vger.linux-kernel
Message-ID <[email protected]>

在 2026/8/22 22:15, FAN YE via B4 Relay 写道:
> From: FAN YE <[email protected]>
> 
> zstd_put_workspace() makes the "hide this workspace from the reclaim timer"
> decision only when the workspace is returned at its own level.
> Decompression always asks for level 0, so a max level workspace borrowed by
> a read skips the whole block and the test for being the last max level
> workspace is never made: it goes back to idle_ws[] still linked on the lru.
> A read borrowing one while a write holds the other is enough to leave every
> max level workspace on the lru, where the reclaim timer can then free them
> all and clear the level bit.
> 
> Once no max level workspace is left, zstd_put_workspace() never reaches
> cond_wake_up() and a task sleeping in zstd_get_workspace() after a failed
> allocation has no possible waker.  Make the decision on every put of a max
> level workspace and unlink it from the lru when it is the last one;
> list_del_init() in zstd_find_workspace() keeps the entry usable for that.
> The test also no longer hides workspaces of other levels, which it did
> whenever no max level workspace happened to be idle.
> 
> Fixes: 3f93aef535c8 ("btrfs: add zstd compression level support")
> Assisted-by: Claude:claude-opus-5
> Signed-off-by: FAN YE <[email protected]>

Sashiko found a high level problem:

https://sashiko.dev/#/patchset/20260822-btrfs-zstd-max-level-reclaim-v1-1-0eb13c127480%40gmail.com
> ---
> Reproduced under QEMU/TCG.  Both arms are the same kernel with the reclaim
> interval shortened to 100ms and a module param picking the old or the new
> zstd_put_workspace(); the workload is compress-force=zstd:15, three rounds
> of four concurrent writers followed by drop_caches, four readers and two
> writers.  "unprotected" counts puts of a max level workspace after which
> nothing left in idle_ws[] is off the lru; "borrowed" counts a max level
> workspace taken and returned by a lower level request, the path this patch
> changes.
> 
>                      borrowed   unprotected   timer cleared the level bit
>    current code       245/339       118/216                          1/0
>    this patch         300/379           0/0                          0/0
> 
> borrowed is of the same order in both arms, so the zeroes are not "the code
> was never reached".  The last column needs the reclaim timer to tick inside
> the window, so it is a coincidence rather than the criterion.
> 
> Compile-tested (W=1, x86_64 defconfig + CONFIG_BTRFS_FS=y).  Independent of
> and applies without my lost wakeup fix for zstd_get_workspace(),
> [email protected].
> ---
>   fs/btrfs/zstd.c | 25 ++++++++++++-------------
>   1 file changed, 12 insertions(+), 13 deletions(-)
> 
> diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c
> index 86919293fd54..8abc4e456f32 100644
> --- a/fs/btrfs/zstd.c
> +++ b/fs/btrfs/zstd.c
> @@ -260,7 +260,7 @@ static struct list_head *zstd_find_workspace(struct btrfs_fs_info *fs_info, int
>   			/* keep its place if it's a lower level using this */
>   			workspace->req_level = level;
>   			if (clip_level(level) == workspace->level)
> -				list_del(&workspace->lru_list);
> +				list_del_init(&workspace->lru_list);
>   			if (list_empty(&zwsm->idle_ws[i]))
>   				clear_bit(i, &zwsm->active_map);
>   			spin_unlock_bh(&zwsm->lock);
> @@ -335,18 +335,17 @@ void zstd_put_workspace(struct btrfs_fs_info *fs_info, struct list_head *ws)
>   	ASSERT(zwsm);
>   	spin_lock_bh(&zwsm->lock);
>   
> -	/* A node is only taken off the lru if we are the corresponding level */
> -	if (clip_level(workspace->req_level) == workspace->level) {
> -		/* Hide a max level workspace from reclaim */
> -		if (list_empty(&zwsm->idle_ws[ZSTD_BTRFS_MAX_LEVEL - 1])) {
> -			INIT_LIST_HEAD(&workspace->lru_list);
> -		} else {
> -			workspace->last_used = jiffies;
> -			list_add(&workspace->lru_list, &zwsm->lru_list);
> -			if (!timer_pending(&zwsm->timer))
> -				mod_timer(&zwsm->timer,
> -					  jiffies + ZSTD_BTRFS_RECLAIM_JIFFIES);
> -		}
> +	/* Forward progress depends on always keeping one max level workspace */
> +	if (workspace->level == clip_level(ZSTD_BTRFS_MAX_LEVEL) &&
> +	    list_empty(&zwsm->idle_ws[ZSTD_BTRFS_MAX_LEVEL - 1])) {
> +		list_del_init(&workspace->lru_list);
> +	} else if (clip_level(workspace->req_level) == workspace->level) {
> +		/* A node is only taken off the lru if we are the corresponding level */
> +		workspace->last_used = jiffies;
> +		list_add(&workspace->lru_list, &zwsm->lru_list);
> +		if (!timer_pending(&zwsm->timer))
> +			mod_timer(&zwsm->timer,
> +				  jiffies + ZSTD_BTRFS_RECLAIM_JIFFIES);
>   	}
>   
>   	set_bit(workspace->level, &zwsm->active_map);
> 
> ---
> base-commit: 26260251022fbc2f248a3d747a9b2b961b18d2d8
> change-id: 20260822-btrfs-zstd-max-level-reclaim-5ab59a71f83e
> 
> Best regards,
> --
> FAN YE <[email protected]>
> 
> 
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.