All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/7] btrfs: Use percpu refcounting for block groups
       [not found] <20260112161549.2786827-1-martin@urbackup.org>
@ 2026-01-12 16:17 ` Martin Raiber
  2026-01-12 22:32   ` Boris Burkov
  2026-01-14  6:06   ` kernel test robot
  0 siblings, 2 replies; 4+ messages in thread
From: Martin Raiber @ 2026-01-12 16:17 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Martin Raiber

Use a percpu counter to keep track of the block group refs.
This prevents CPU synchronization completely as long as the main reference
is not freed via btrfs_remove_block_group, improving performance of
btrfs_put_block_group, btrfs_get_block_group significantly.

Signed-off-by: Martin Raiber <martin@urbackup.org>
---
 fs/btrfs/block-group.c | 111 +++++++++++++++++++++++------------------
 fs/btrfs/block-group.h |   2 +-
 2 files changed, 63 insertions(+), 50 deletions(-)

diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
index a1119f06b6d1..7569438ccbd5 100644
--- a/fs/btrfs/block-group.c
+++ b/fs/btrfs/block-group.c
@@ -153,37 +153,44 @@ u64 btrfs_get_alloc_profile(struct btrfs_fs_info *fs_info, u64 orig_flags)
 
 void btrfs_get_block_group(struct btrfs_block_group *cache)
 {
-	refcount_inc(&cache->refs);
+	percpu_ref_get(&cache->refs);
 }
 
-void btrfs_put_block_group(struct btrfs_block_group *cache)
+static void btrfs_free_block_group(struct percpu_ref *ref)
 {
-	if (refcount_dec_and_test(&cache->refs)) {
-		WARN_ON(cache->pinned > 0);
-		/*
-		 * If there was a failure to cleanup a log tree, very likely due
-		 * to an IO failure on a writeback attempt of one or more of its
-		 * extent buffers, we could not do proper (and cheap) unaccounting
-		 * of their reserved space, so don't warn on reserved > 0 in that
-		 * case.
-		 */
-		if (!(cache->flags & BTRFS_BLOCK_GROUP_METADATA) ||
-		    !BTRFS_FS_LOG_CLEANUP_ERROR(cache->fs_info))
-			WARN_ON(cache->reserved > 0);
+	struct btrfs_block_group *cache =
+		container_of(ref, struct btrfs_block_group, refs);
 
-		/*
-		 * A block_group shouldn't be on the discard_list anymore.
-		 * Remove the block_group from the discard_list to prevent us
-		 * from causing a panic due to NULL pointer dereference.
-		 */
-		if (WARN_ON(!list_empty(&cache->discard_list)))
-			btrfs_discard_cancel_work(&cache->fs_info->discard_ctl,
-						  cache);
+	WARN_ON(cache->pinned > 0);
+	/*
+	* If there was a failure to cleanup a log tree, very likely due
+	* to an IO failure on a writeback attempt of one or more of its
+	* extent buffers, we could not do proper (and cheap) unaccounting
+	* of their reserved space, so don't warn on reserved > 0 in that
+	* case.
+	*/
+	if (!(cache->flags & BTRFS_BLOCK_GROUP_METADATA) ||
+		!BTRFS_FS_LOG_CLEANUP_ERROR(cache->fs_info))
+		WARN_ON(cache->reserved > 0);
 
-		kfree(cache->free_space_ctl);
-		btrfs_free_chunk_map(cache->physical_map);
-		kfree(cache);
-	}
+	/*
+	* A block_group shouldn't be on the discard_list anymore.
+	* Remove the block_group from the discard_list to prevent us
+	* from causing a panic due to NULL pointer dereference.
+	*/
+	if (WARN_ON(!list_empty(&cache->discard_list)))
+		btrfs_discard_cancel_work(&cache->fs_info->discard_ctl,
+						cache);
+
+	percpu_ref_exit(&cache->refs);
+	kfree(cache->free_space_ctl);
+	btrfs_free_chunk_map(cache->physical_map);
+	kfree(cache);
+}
+
+void btrfs_put_block_group(struct btrfs_block_group *cache)
+{
+	percpu_ref_put(&cache->refs);
 }
 
 static int btrfs_bg_start_cmp(const struct rb_node *new,
@@ -406,8 +413,8 @@ void btrfs_wait_block_group_reservations(struct btrfs_block_group *bg)
 	 * on the groups' semaphore is held and decremented after releasing
 	 * the read access on that semaphore and creating the ordered extent.
 	 */
-	down_write(&space_info->groups_sem);
-	up_write(&space_info->groups_sem);
+	percpu_down_write(&space_info->groups_sem);
+	percpu_up_write(&space_info->groups_sem);
 
 	wait_var_event(&bg->reservations, !atomic_read(&bg->reservations));
 }
@@ -1012,7 +1019,7 @@ static void clear_incompat_bg_bits(struct btrfs_fs_info *fs_info, u64 flags)
 		struct btrfs_space_info *sinfo;
 
 		list_for_each_entry_rcu(sinfo, head, list) {
-			down_read(&sinfo->groups_sem);
+			percpu_down_read(&sinfo->groups_sem);
 			if (!list_empty(&sinfo->block_groups[BTRFS_RAID_RAID5]))
 				found_raid56 = true;
 			if (!list_empty(&sinfo->block_groups[BTRFS_RAID_RAID6]))
@@ -1021,7 +1028,7 @@ static void clear_incompat_bg_bits(struct btrfs_fs_info *fs_info, u64 flags)
 				found_raid1c34 = true;
 			if (!list_empty(&sinfo->block_groups[BTRFS_RAID_RAID1C4]))
 				found_raid1c34 = true;
-			up_read(&sinfo->groups_sem);
+			percpu_up_read(&sinfo->groups_sem);
 		}
 		if (!found_raid56)
 			btrfs_clear_fs_incompat(fs_info, RAID56);
@@ -1159,11 +1166,11 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
 	RB_CLEAR_NODE(&block_group->cache_node);
 
 	/* Once for the block groups rbtree */
-	btrfs_put_block_group(block_group);
+	percpu_ref_kill(&block_group->refs);
 
 	write_unlock(&fs_info->block_group_cache_lock);
 
-	down_write(&block_group->space_info->groups_sem);
+	percpu_down_write(&block_group->space_info->groups_sem);
 	/*
 	 * we must use list_del_init so people can check to see if they
 	 * are still on the list after taking the semaphore
@@ -1174,7 +1181,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
 		block_group->space_info->block_group_kobjs[index] = NULL;
 		clear_avail_alloc_bits(fs_info, block_group->flags);
 	}
-	up_write(&block_group->space_info->groups_sem);
+	percpu_up_write(&block_group->space_info->groups_sem);
 	clear_incompat_bg_bits(fs_info, block_group->flags);
 	if (kobj) {
 		kobject_del(kobj);
@@ -1544,7 +1551,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
 		btrfs_discard_cancel_work(&fs_info->discard_ctl, block_group);
 
 		/* Don't want to race with allocators so take the groups_sem */
-		down_write(&space_info->groups_sem);
+		percpu_down_write(&space_info->groups_sem);
 
 		/*
 		 * Async discard moves the final block group discard to be prior
@@ -1554,7 +1561,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
 		if (btrfs_test_opt(fs_info, DISCARD_ASYNC) &&
 		    !btrfs_is_free_space_trimmed(block_group)) {
 			trace_btrfs_skip_unused_block_group(block_group);
-			up_write(&space_info->groups_sem);
+			percpu_up_write(&space_info->groups_sem);
 			/* Requeue if we failed because of async discard */
 			btrfs_discard_queue_work(&fs_info->discard_ctl,
 						 block_group);
@@ -1581,7 +1588,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
 			trace_btrfs_skip_unused_block_group(block_group);
 			spin_unlock(&block_group->lock);
 			spin_unlock(&space_info->lock);
-			up_write(&space_info->groups_sem);
+			percpu_up_write(&space_info->groups_sem);
 			goto next;
 		}
 
@@ -1618,7 +1625,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
 			trace_btrfs_skip_unused_block_group(block_group);
 			spin_unlock(&block_group->lock);
 			spin_unlock(&space_info->lock);
-			up_write(&space_info->groups_sem);
+			percpu_up_write(&space_info->groups_sem);
 			goto next;
 		}
 
@@ -1627,7 +1634,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
 
 		/* We don't want to force the issue, only flip if it's ok. */
 		ret = inc_block_group_ro(block_group, 0);
-		up_write(&space_info->groups_sem);
+		percpu_up_write(&space_info->groups_sem);
 		if (ret < 0) {
 			ret = 0;
 			goto next;
@@ -1882,7 +1889,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work)
 		spin_unlock(&fs_info->unused_bgs_lock);
 
 		/* Don't race with allocators so take the groups_sem */
-		down_write(&space_info->groups_sem);
+		percpu_down_write(&space_info->groups_sem);
 
 		spin_lock(&space_info->lock);
 		spin_lock(&bg->lock);
@@ -1895,7 +1902,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work)
 			 */
 			spin_unlock(&bg->lock);
 			spin_unlock(&space_info->lock);
-			up_write(&space_info->groups_sem);
+			percpu_up_write(&space_info->groups_sem);
 			goto next;
 		}
 		if (bg->used == 0) {
@@ -1914,7 +1921,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work)
 				btrfs_mark_bg_unused(bg);
 			spin_unlock(&bg->lock);
 			spin_unlock(&space_info->lock);
-			up_write(&space_info->groups_sem);
+			percpu_up_write(&space_info->groups_sem);
 			goto next;
 
 		}
@@ -1931,7 +1938,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work)
 		if (!should_reclaim_block_group(bg, bg->length)) {
 			spin_unlock(&bg->lock);
 			spin_unlock(&space_info->lock);
-			up_write(&space_info->groups_sem);
+			percpu_up_write(&space_info->groups_sem);
 			goto next;
 		}
 
@@ -1947,12 +1954,12 @@ void btrfs_reclaim_bgs_work(struct work_struct *work)
 		 * never gets back to read-write to let us reclaim again.
 		 */
 		if (btrfs_need_cleaner_sleep(fs_info)) {
-			up_write(&space_info->groups_sem);
+			percpu_up_write(&space_info->groups_sem);
 			goto next;
 		}
 
 		ret = inc_block_group_ro(bg, 0);
-		up_write(&space_info->groups_sem);
+		percpu_up_write(&space_info->groups_sem);
 		if (ret < 0)
 			goto next;
 
@@ -2288,7 +2295,12 @@ static struct btrfs_block_group *btrfs_create_block_group(
 
 	cache->discard_index = BTRFS_DISCARD_INDEX_UNUSED;
 
-	refcount_set(&cache->refs, 1);
+	if (percpu_ref_init(&cache->refs, btrfs_free_block_group,
+			  0, GFP_NOFS)) {
+		kfree(cache->free_space_ctl);
+		kfree(cache);
+		return NULL;
+	}
 	spin_lock_init(&cache->lock);
 	init_rwsem(&cache->data_rwsem);
 	INIT_LIST_HEAD(&cache->list);
@@ -4550,9 +4562,9 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
 		RB_CLEAR_NODE(&block_group->cache_node);
 		write_unlock(&info->block_group_cache_lock);
 
-		down_write(&block_group->space_info->groups_sem);
+		percpu_down_write(&block_group->space_info->groups_sem);
 		list_del(&block_group->list);
-		up_write(&block_group->space_info->groups_sem);
+		percpu_up_write(&block_group->space_info->groups_sem);
 
 		/*
 		 * We haven't cached this block group, which means we could
@@ -4567,9 +4579,10 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
 		ASSERT(list_empty(&block_group->dirty_list));
 		ASSERT(list_empty(&block_group->io_list));
 		ASSERT(list_empty(&block_group->bg_list));
-		ASSERT(refcount_read(&block_group->refs) == 1);
+		ASSERT(!percpu_ref_is_zero(&block_group->refs));
 		ASSERT(block_group->swap_extents == 0);
-		btrfs_put_block_group(block_group);
+		percpu_ref_kill(&block_group->refs);
+		ASSERT(percpu_ref_is_zero(&block_group->refs));
 
 		write_lock(&info->block_group_cache_lock);
 	}
diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h
index 5f933455118c..d44675f9d601 100644
--- a/fs/btrfs/block-group.h
+++ b/fs/btrfs/block-group.h
@@ -178,7 +178,7 @@ struct btrfs_block_group {
 	/* For block groups in the same raid type */
 	struct list_head list;
 
-	refcount_t refs;
+	struct percpu_ref refs;
 
 	/*
 	 * List of struct btrfs_free_clusters for this block group.
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/7] btrfs: Use percpu refcounting for block groups
  2026-01-12 16:17 ` Martin Raiber
@ 2026-01-12 22:32   ` Boris Burkov
  2026-01-14  6:06   ` kernel test robot
  1 sibling, 0 replies; 4+ messages in thread
From: Boris Burkov @ 2026-01-12 22:32 UTC (permalink / raw)
  To: Martin Raiber; +Cc: linux-btrfs

On Mon, Jan 12, 2026 at 04:17:16PM +0000, Martin Raiber wrote:
> Use a percpu counter to keep track of the block group refs.
> This prevents CPU synchronization completely as long as the main reference
> is not freed via btrfs_remove_block_group, improving performance of
> btrfs_put_block_group, btrfs_get_block_group significantly.

Besides the potential mixup with patch 2, this looks like a nice
improvement to me, since we adhere to the pattern of having a "main"
refcount for the rb tree entry.

My only other complaint is that this seems to lose the helpful
refcount_t warning feature. But I think I can live with that.

> 
> Signed-off-by: Martin Raiber <martin@urbackup.org>
> ---
>  fs/btrfs/block-group.c | 111 +++++++++++++++++++++++------------------
>  fs/btrfs/block-group.h |   2 +-
>  2 files changed, 63 insertions(+), 50 deletions(-)
> 
> diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
> index a1119f06b6d1..7569438ccbd5 100644
> --- a/fs/btrfs/block-group.c
> +++ b/fs/btrfs/block-group.c
> @@ -153,37 +153,44 @@ u64 btrfs_get_alloc_profile(struct btrfs_fs_info *fs_info, u64 orig_flags)
>  
>  void btrfs_get_block_group(struct btrfs_block_group *cache)
>  {
> -	refcount_inc(&cache->refs);
> +	percpu_ref_get(&cache->refs);
>  }
>  
> -void btrfs_put_block_group(struct btrfs_block_group *cache)
> +static void btrfs_free_block_group(struct percpu_ref *ref)
>  {
> -	if (refcount_dec_and_test(&cache->refs)) {
> -		WARN_ON(cache->pinned > 0);
> -		/*
> -		 * If there was a failure to cleanup a log tree, very likely due
> -		 * to an IO failure on a writeback attempt of one or more of its
> -		 * extent buffers, we could not do proper (and cheap) unaccounting
> -		 * of their reserved space, so don't warn on reserved > 0 in that
> -		 * case.
> -		 */
> -		if (!(cache->flags & BTRFS_BLOCK_GROUP_METADATA) ||
> -		    !BTRFS_FS_LOG_CLEANUP_ERROR(cache->fs_info))
> -			WARN_ON(cache->reserved > 0);
> +	struct btrfs_block_group *cache =
> +		container_of(ref, struct btrfs_block_group, refs);

I think for most new code we call these 'block_group' rather than
'cache', so I think this is a good opportunity to update it.

>  
> -		/*
> -		 * A block_group shouldn't be on the discard_list anymore.
> -		 * Remove the block_group from the discard_list to prevent us
> -		 * from causing a panic due to NULL pointer dereference.
> -		 */
> -		if (WARN_ON(!list_empty(&cache->discard_list)))
> -			btrfs_discard_cancel_work(&cache->fs_info->discard_ctl,
> -						  cache);
> +	WARN_ON(cache->pinned > 0);
> +	/*
> +	* If there was a failure to cleanup a log tree, very likely due
> +	* to an IO failure on a writeback attempt of one or more of its
> +	* extent buffers, we could not do proper (and cheap) unaccounting
> +	* of their reserved space, so don't warn on reserved > 0 in that
> +	* case.
> +	*/
> +	if (!(cache->flags & BTRFS_BLOCK_GROUP_METADATA) ||
> +		!BTRFS_FS_LOG_CLEANUP_ERROR(cache->fs_info))
> +		WARN_ON(cache->reserved > 0);
>  
> -		kfree(cache->free_space_ctl);
> -		btrfs_free_chunk_map(cache->physical_map);
> -		kfree(cache);
> -	}
> +	/*
> +	* A block_group shouldn't be on the discard_list anymore.
> +	* Remove the block_group from the discard_list to prevent us
> +	* from causing a panic due to NULL pointer dereference.
> +	*/
> +	if (WARN_ON(!list_empty(&cache->discard_list)))
> +		btrfs_discard_cancel_work(&cache->fs_info->discard_ctl,
> +						cache);
> +
> +	percpu_ref_exit(&cache->refs);
> +	kfree(cache->free_space_ctl);
> +	btrfs_free_chunk_map(cache->physical_map);
> +	kfree(cache);
> +}
> +
> +void btrfs_put_block_group(struct btrfs_block_group *cache)
> +{
> +	percpu_ref_put(&cache->refs);
>  }
>  
>  static int btrfs_bg_start_cmp(const struct rb_node *new,
> @@ -406,8 +413,8 @@ void btrfs_wait_block_group_reservations(struct btrfs_block_group *bg)
>  	 * on the groups' semaphore is held and decremented after releasing
>  	 * the read access on that semaphore and creating the ordered extent.
>  	 */
> -	down_write(&space_info->groups_sem);
> -	up_write(&space_info->groups_sem);
> +	percpu_down_write(&space_info->groups_sem);
> +	percpu_up_write(&space_info->groups_sem);

Did this sneak out of patch 2 into this patch? (several other instances
in this patch that I won't note)

>  
>  	wait_var_event(&bg->reservations, !atomic_read(&bg->reservations));
>  }
> @@ -1012,7 +1019,7 @@ static void clear_incompat_bg_bits(struct btrfs_fs_info *fs_info, u64 flags)
>  		struct btrfs_space_info *sinfo;
>  
>  		list_for_each_entry_rcu(sinfo, head, list) {
> -			down_read(&sinfo->groups_sem);
> +			percpu_down_read(&sinfo->groups_sem);
>  			if (!list_empty(&sinfo->block_groups[BTRFS_RAID_RAID5]))
>  				found_raid56 = true;
>  			if (!list_empty(&sinfo->block_groups[BTRFS_RAID_RAID6]))
> @@ -1021,7 +1028,7 @@ static void clear_incompat_bg_bits(struct btrfs_fs_info *fs_info, u64 flags)
>  				found_raid1c34 = true;
>  			if (!list_empty(&sinfo->block_groups[BTRFS_RAID_RAID1C4]))
>  				found_raid1c34 = true;
> -			up_read(&sinfo->groups_sem);
> +			percpu_up_read(&sinfo->groups_sem);
>  		}
>  		if (!found_raid56)
>  			btrfs_clear_fs_incompat(fs_info, RAID56);
> @@ -1159,11 +1166,11 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
>  	RB_CLEAR_NODE(&block_group->cache_node);
>  
>  	/* Once for the block groups rbtree */
> -	btrfs_put_block_group(block_group);
> +	percpu_ref_kill(&block_group->refs);
>  
>  	write_unlock(&fs_info->block_group_cache_lock);
>  
> -	down_write(&block_group->space_info->groups_sem);
> +	percpu_down_write(&block_group->space_info->groups_sem);
>  	/*
>  	 * we must use list_del_init so people can check to see if they
>  	 * are still on the list after taking the semaphore
> @@ -1174,7 +1181,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
>  		block_group->space_info->block_group_kobjs[index] = NULL;
>  		clear_avail_alloc_bits(fs_info, block_group->flags);
>  	}
> -	up_write(&block_group->space_info->groups_sem);
> +	percpu_up_write(&block_group->space_info->groups_sem);
>  	clear_incompat_bg_bits(fs_info, block_group->flags);
>  	if (kobj) {
>  		kobject_del(kobj);
> @@ -1544,7 +1551,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
>  		btrfs_discard_cancel_work(&fs_info->discard_ctl, block_group);
>  
>  		/* Don't want to race with allocators so take the groups_sem */
> -		down_write(&space_info->groups_sem);
> +		percpu_down_write(&space_info->groups_sem);
>  
>  		/*
>  		 * Async discard moves the final block group discard to be prior
> @@ -1554,7 +1561,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
>  		if (btrfs_test_opt(fs_info, DISCARD_ASYNC) &&
>  		    !btrfs_is_free_space_trimmed(block_group)) {
>  			trace_btrfs_skip_unused_block_group(block_group);
> -			up_write(&space_info->groups_sem);
> +			percpu_up_write(&space_info->groups_sem);
>  			/* Requeue if we failed because of async discard */
>  			btrfs_discard_queue_work(&fs_info->discard_ctl,
>  						 block_group);
> @@ -1581,7 +1588,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
>  			trace_btrfs_skip_unused_block_group(block_group);
>  			spin_unlock(&block_group->lock);
>  			spin_unlock(&space_info->lock);
> -			up_write(&space_info->groups_sem);
> +			percpu_up_write(&space_info->groups_sem);
>  			goto next;
>  		}
>  
> @@ -1618,7 +1625,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
>  			trace_btrfs_skip_unused_block_group(block_group);
>  			spin_unlock(&block_group->lock);
>  			spin_unlock(&space_info->lock);
> -			up_write(&space_info->groups_sem);
> +			percpu_up_write(&space_info->groups_sem);
>  			goto next;
>  		}
>  
> @@ -1627,7 +1634,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
>  
>  		/* We don't want to force the issue, only flip if it's ok. */
>  		ret = inc_block_group_ro(block_group, 0);
> -		up_write(&space_info->groups_sem);
> +		percpu_up_write(&space_info->groups_sem);
>  		if (ret < 0) {
>  			ret = 0;
>  			goto next;
> @@ -1882,7 +1889,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work)
>  		spin_unlock(&fs_info->unused_bgs_lock);
>  
>  		/* Don't race with allocators so take the groups_sem */
> -		down_write(&space_info->groups_sem);
> +		percpu_down_write(&space_info->groups_sem);
>  
>  		spin_lock(&space_info->lock);
>  		spin_lock(&bg->lock);
> @@ -1895,7 +1902,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work)
>  			 */
>  			spin_unlock(&bg->lock);
>  			spin_unlock(&space_info->lock);
> -			up_write(&space_info->groups_sem);
> +			percpu_up_write(&space_info->groups_sem);
>  			goto next;
>  		}
>  		if (bg->used == 0) {
> @@ -1914,7 +1921,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work)
>  				btrfs_mark_bg_unused(bg);
>  			spin_unlock(&bg->lock);
>  			spin_unlock(&space_info->lock);
> -			up_write(&space_info->groups_sem);
> +			percpu_up_write(&space_info->groups_sem);
>  			goto next;
>  
>  		}
> @@ -1931,7 +1938,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work)
>  		if (!should_reclaim_block_group(bg, bg->length)) {
>  			spin_unlock(&bg->lock);
>  			spin_unlock(&space_info->lock);
> -			up_write(&space_info->groups_sem);
> +			percpu_up_write(&space_info->groups_sem);
>  			goto next;
>  		}
>  
> @@ -1947,12 +1954,12 @@ void btrfs_reclaim_bgs_work(struct work_struct *work)
>  		 * never gets back to read-write to let us reclaim again.
>  		 */
>  		if (btrfs_need_cleaner_sleep(fs_info)) {
> -			up_write(&space_info->groups_sem);
> +			percpu_up_write(&space_info->groups_sem);
>  			goto next;
>  		}
>  
>  		ret = inc_block_group_ro(bg, 0);
> -		up_write(&space_info->groups_sem);
> +		percpu_up_write(&space_info->groups_sem);
>  		if (ret < 0)
>  			goto next;
>  
> @@ -2288,7 +2295,12 @@ static struct btrfs_block_group *btrfs_create_block_group(
>  
>  	cache->discard_index = BTRFS_DISCARD_INDEX_UNUSED;
>  
> -	refcount_set(&cache->refs, 1);
> +	if (percpu_ref_init(&cache->refs, btrfs_free_block_group,
> +			  0, GFP_NOFS)) {
> +		kfree(cache->free_space_ctl);
> +		kfree(cache);
> +		return NULL;
> +	}
>  	spin_lock_init(&cache->lock);
>  	init_rwsem(&cache->data_rwsem);
>  	INIT_LIST_HEAD(&cache->list);
> @@ -4550,9 +4562,9 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
>  		RB_CLEAR_NODE(&block_group->cache_node);
>  		write_unlock(&info->block_group_cache_lock);
>  
> -		down_write(&block_group->space_info->groups_sem);
> +		percpu_down_write(&block_group->space_info->groups_sem);
>  		list_del(&block_group->list);
> -		up_write(&block_group->space_info->groups_sem);
> +		percpu_up_write(&block_group->space_info->groups_sem);
>  
>  		/*
>  		 * We haven't cached this block group, which means we could
> @@ -4567,9 +4579,10 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
>  		ASSERT(list_empty(&block_group->dirty_list));
>  		ASSERT(list_empty(&block_group->io_list));
>  		ASSERT(list_empty(&block_group->bg_list));
> -		ASSERT(refcount_read(&block_group->refs) == 1);
> +		ASSERT(!percpu_ref_is_zero(&block_group->refs));
>  		ASSERT(block_group->swap_extents == 0);
> -		btrfs_put_block_group(block_group);
> +		percpu_ref_kill(&block_group->refs);
> +		ASSERT(percpu_ref_is_zero(&block_group->refs));
>  
>  		write_lock(&info->block_group_cache_lock);
>  	}
> diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h
> index 5f933455118c..d44675f9d601 100644
> --- a/fs/btrfs/block-group.h
> +++ b/fs/btrfs/block-group.h
> @@ -178,7 +178,7 @@ struct btrfs_block_group {
>  	/* For block groups in the same raid type */
>  	struct list_head list;
>  
> -	refcount_t refs;
> +	struct percpu_ref refs;
>  
>  	/*
>  	 * List of struct btrfs_free_clusters for this block group.
> -- 
> 2.39.5
> 

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/7] btrfs: Use percpu refcounting for block groups
@ 2026-01-13 10:32 kernel test robot
  0 siblings, 0 replies; 4+ messages in thread
From: kernel test robot @ 2026-01-13 10:32 UTC (permalink / raw)
  To: oe-kbuild

:::::: 
:::::: Manual check reason: "high confidence checkpatch report"
:::::: 

BCC: lkp@intel.com
CC: oe-kbuild-all@lists.linux.dev
In-Reply-To: <0102019bb2ff56b7-9302e783-c17d-452d-b6a7-11f773776ae7-000000@eu-west-1.amazonses.com>
References: <0102019bb2ff56b7-9302e783-c17d-452d-b6a7-11f773776ae7-000000@eu-west-1.amazonses.com>
TO: Martin Raiber <martin@urbackup.org>
TO: linux-btrfs@vger.kernel.org

Hi Martin,

kernel test robot noticed the following build warnings:

[auto build test WARNING on kdave/for-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Martin-Raiber/btrfs-Use-percpu-semaphore-for-space-info-groups_sem/20260113-070107
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
patch link:    https://lore.kernel.org/r/0102019bb2ff56b7-9302e783-c17d-452d-b6a7-11f773776ae7-000000%40eu-west-1.amazonses.com
patch subject: [PATCH 1/7] btrfs: Use percpu refcounting for block groups
:::::: branch date: 12 hours ago
:::::: commit date: 12 hours ago
reproduce: (https://download.01.org/0day-ci/archive/20260113/202601131155.ulOGAl1n-lkp@intel.com/reproduce)

# many are suggestions rather than must-fix

WARNING:BLOCK_COMMENT_STYLE: Block comments should align the * on each line
#57: FILE: fs/btrfs/block-group.c:166:
+	/*
+	* If there was a failure to cleanup a log tree, very likely due

WARNING:BLOCK_COMMENT_STYLE: Block comments should align the * on each line
#72: FILE: fs/btrfs/block-group.c:177:
+	/*
+	* A block_group shouldn't be on the discard_list anymore.

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/7] btrfs: Use percpu refcounting for block groups
  2026-01-12 16:17 ` Martin Raiber
  2026-01-12 22:32   ` Boris Burkov
@ 2026-01-14  6:06   ` kernel test robot
  1 sibling, 0 replies; 4+ messages in thread
From: kernel test robot @ 2026-01-14  6:06 UTC (permalink / raw)
  To: Martin Raiber, linux-btrfs; +Cc: oe-kbuild-all, Martin Raiber

Hi Martin,

kernel test robot noticed the following build errors:

[auto build test ERROR on kdave/for-next]
[also build test ERROR on linus/master v6.19-rc5 next-20260113]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Martin-Raiber/btrfs-Use-percpu-semaphore-for-space-info-groups_sem/20260113-070107
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
patch link:    https://lore.kernel.org/r/0102019bb2ff56b7-9302e783-c17d-452d-b6a7-11f773776ae7-000000%40eu-west-1.amazonses.com
patch subject: [PATCH 1/7] btrfs: Use percpu refcounting for block groups
config: x86_64-rhel-9.4-ltp (https://download.01.org/0day-ci/archive/20260114/202601141316.7JnRkX9k-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260114/202601141316.7JnRkX9k-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601141316.7JnRkX9k-lkp@intel.com/

All errors (new ones prefixed by >>):

   fs/btrfs/block-group.c: In function 'btrfs_wait_block_group_reservations':
>> fs/btrfs/block-group.c:416:27: error: passing argument 1 of 'percpu_down_write' from incompatible pointer type [-Wincompatible-pointer-types]
     416 |         percpu_down_write(&space_info->groups_sem);
         |                           ^~~~~~~~~~~~~~~~~~~~~~~
         |                           |
         |                           struct rw_semaphore *
   In file included from include/linux/fs/super_types.h:13,
                    from include/linux/fs/super.h:5,
                    from include/linux/fs.h:5,
                    from include/linux/huge_mm.h:7,
                    from include/linux/mm.h:1268,
                    from fs/btrfs/misc.h:10,
                    from fs/btrfs/block-group.c:5:
   include/linux/percpu-rwsem.h:138:31: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     138 | extern void percpu_down_write(struct percpu_rw_semaphore *);
         |                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> fs/btrfs/block-group.c:417:25: error: passing argument 1 of 'percpu_up_write' from incompatible pointer type [-Wincompatible-pointer-types]
     417 |         percpu_up_write(&space_info->groups_sem);
         |                         ^~~~~~~~~~~~~~~~~~~~~~~
         |                         |
         |                         struct rw_semaphore *
   include/linux/percpu-rwsem.h:139:29: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     139 | extern void percpu_up_write(struct percpu_rw_semaphore *);
         |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c: In function 'clear_incompat_bg_bits':
>> fs/btrfs/block-group.c:1022:42: error: passing argument 1 of 'percpu_down_read' from incompatible pointer type [-Wincompatible-pointer-types]
    1022 |                         percpu_down_read(&sinfo->groups_sem);
         |                                          ^~~~~~~~~~~~~~~~~~
         |                                          |
         |                                          struct rw_semaphore *
   include/linux/percpu-rwsem.h:75:65: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
      75 | static inline void percpu_down_read(struct percpu_rw_semaphore *sem)
         |                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
>> fs/btrfs/block-group.c:1031:40: error: passing argument 1 of 'percpu_up_read' from incompatible pointer type [-Wincompatible-pointer-types]
    1031 |                         percpu_up_read(&sinfo->groups_sem);
         |                                        ^~~~~~~~~~~~~~~~~~
         |                                        |
         |                                        struct rw_semaphore *
   include/linux/percpu-rwsem.h:110:63: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     110 | static inline void percpu_up_read(struct percpu_rw_semaphore *sem)
         |                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
   fs/btrfs/block-group.c: In function 'btrfs_remove_block_group':
   fs/btrfs/block-group.c:1173:27: error: passing argument 1 of 'percpu_down_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1173 |         percpu_down_write(&block_group->space_info->groups_sem);
         |                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |                           |
         |                           struct rw_semaphore *
   include/linux/percpu-rwsem.h:138:31: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     138 | extern void percpu_down_write(struct percpu_rw_semaphore *);
         |                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c:1184:25: error: passing argument 1 of 'percpu_up_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1184 |         percpu_up_write(&block_group->space_info->groups_sem);
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |                         |
         |                         struct rw_semaphore *
   include/linux/percpu-rwsem.h:139:29: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     139 | extern void percpu_up_write(struct percpu_rw_semaphore *);
         |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c: In function 'btrfs_delete_unused_bgs':
   fs/btrfs/block-group.c:1554:35: error: passing argument 1 of 'percpu_down_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1554 |                 percpu_down_write(&space_info->groups_sem);
         |                                   ^~~~~~~~~~~~~~~~~~~~~~~
         |                                   |
         |                                   struct rw_semaphore *
   include/linux/percpu-rwsem.h:138:31: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     138 | extern void percpu_down_write(struct percpu_rw_semaphore *);
         |                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c:1564:41: error: passing argument 1 of 'percpu_up_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1564 |                         percpu_up_write(&space_info->groups_sem);
         |                                         ^~~~~~~~~~~~~~~~~~~~~~~
         |                                         |
         |                                         struct rw_semaphore *
   include/linux/percpu-rwsem.h:139:29: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     139 | extern void percpu_up_write(struct percpu_rw_semaphore *);
         |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c:1591:41: error: passing argument 1 of 'percpu_up_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1591 |                         percpu_up_write(&space_info->groups_sem);
         |                                         ^~~~~~~~~~~~~~~~~~~~~~~
         |                                         |
         |                                         struct rw_semaphore *
   include/linux/percpu-rwsem.h:139:29: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     139 | extern void percpu_up_write(struct percpu_rw_semaphore *);
         |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c:1628:41: error: passing argument 1 of 'percpu_up_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1628 |                         percpu_up_write(&space_info->groups_sem);
         |                                         ^~~~~~~~~~~~~~~~~~~~~~~
         |                                         |
         |                                         struct rw_semaphore *
   include/linux/percpu-rwsem.h:139:29: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     139 | extern void percpu_up_write(struct percpu_rw_semaphore *);
         |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c:1637:33: error: passing argument 1 of 'percpu_up_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1637 |                 percpu_up_write(&space_info->groups_sem);
         |                                 ^~~~~~~~~~~~~~~~~~~~~~~
         |                                 |
         |                                 struct rw_semaphore *
   include/linux/percpu-rwsem.h:139:29: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     139 | extern void percpu_up_write(struct percpu_rw_semaphore *);
         |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c: In function 'btrfs_reclaim_bgs_work':
   fs/btrfs/block-group.c:1892:35: error: passing argument 1 of 'percpu_down_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1892 |                 percpu_down_write(&space_info->groups_sem);
         |                                   ^~~~~~~~~~~~~~~~~~~~~~~
         |                                   |
         |                                   struct rw_semaphore *
   include/linux/percpu-rwsem.h:138:31: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     138 | extern void percpu_down_write(struct percpu_rw_semaphore *);
         |                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c:1905:41: error: passing argument 1 of 'percpu_up_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1905 |                         percpu_up_write(&space_info->groups_sem);
         |                                         ^~~~~~~~~~~~~~~~~~~~~~~
         |                                         |
         |                                         struct rw_semaphore *
   include/linux/percpu-rwsem.h:139:29: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     139 | extern void percpu_up_write(struct percpu_rw_semaphore *);
         |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c:1924:41: error: passing argument 1 of 'percpu_up_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1924 |                         percpu_up_write(&space_info->groups_sem);
         |                                         ^~~~~~~~~~~~~~~~~~~~~~~
         |                                         |
         |                                         struct rw_semaphore *
   include/linux/percpu-rwsem.h:139:29: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     139 | extern void percpu_up_write(struct percpu_rw_semaphore *);
         |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c:1941:41: error: passing argument 1 of 'percpu_up_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1941 |                         percpu_up_write(&space_info->groups_sem);
         |                                         ^~~~~~~~~~~~~~~~~~~~~~~
         |                                         |
         |                                         struct rw_semaphore *
   include/linux/percpu-rwsem.h:139:29: note: expected 'struct percpu_rw_semaphore *' but argument is of type 'struct rw_semaphore *'
     139 | extern void percpu_up_write(struct percpu_rw_semaphore *);
         |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/block-group.c:1957:41: error: passing argument 1 of 'percpu_up_write' from incompatible pointer type [-Wincompatible-pointer-types]
    1957 |                         percpu_up_write(&space_info->groups_sem);


vim +/percpu_down_write +416 fs/btrfs/block-group.c

   396	
   397	void btrfs_wait_block_group_reservations(struct btrfs_block_group *bg)
   398	{
   399		struct btrfs_space_info *space_info = bg->space_info;
   400	
   401		ASSERT(bg->ro);
   402	
   403		if (!(bg->flags & BTRFS_BLOCK_GROUP_DATA))
   404			return;
   405	
   406		/*
   407		 * Our block group is read only but before we set it to read only,
   408		 * some task might have had allocated an extent from it already, but it
   409		 * has not yet created a respective ordered extent (and added it to a
   410		 * root's list of ordered extents).
   411		 * Therefore wait for any task currently allocating extents, since the
   412		 * block group's reservations counter is incremented while a read lock
   413		 * on the groups' semaphore is held and decremented after releasing
   414		 * the read access on that semaphore and creating the ordered extent.
   415		 */
 > 416		percpu_down_write(&space_info->groups_sem);
 > 417		percpu_up_write(&space_info->groups_sem);
   418	
   419		wait_var_event(&bg->reservations, !atomic_read(&bg->reservations));
   420	}
   421	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-01-14  6:07 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-13 10:32 [PATCH 1/7] btrfs: Use percpu refcounting for block groups kernel test robot
     [not found] <20260112161549.2786827-1-martin@urbackup.org>
2026-01-12 16:17 ` Martin Raiber
2026-01-12 22:32   ` Boris Burkov
2026-01-14  6:06   ` kernel test robot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.