void btrfs_reclaim_bgs_work(struct work_struct *work)
{
	struct btrfs_fs_info *fs_info =
		container_of(work, struct btrfs_fs_info, reclaim_bgs_work);
	struct btrfs_block_group *bg;
	struct btrfs_space_info *space_info;
	LIST_HEAD(again_list);

	if (!test_bit(BTRFS_FS_OPEN, &fs_info->flags))
		return;

	if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_BALANCE))
		return;

	mutex_lock(&fs_info->reclaim_bgs_lock);
	spin_lock(&fs_info->unused_bgs_lock);
	while (!list_empty(&fs_info->reclaim_bgs)) {
		u64 zone_unusable;
		int ret = 0;

		bg = list_first_entry(&fs_info->reclaim_bgs,
				      struct btrfs_block_group,
				      bg_list);
		list_del_init(&bg->bg_list);

		space_info = bg->space_info;
		spin_unlock(&fs_info->unused_bgs_lock);

		/* Don't race with allocators so take the groups_sem */
		down_write(&space_info->groups_sem);

		spin_lock(&bg->lock);
		if (bg->reserved || bg->pinned || bg->ro) {
			/*
			 * We want to bail if we made new allocations or have
			 * outstanding allocations in this block group.  We do
			 * the ro check in case balance is currently acting on
			 * this block group.
			 */
			spin_unlock(&bg->lock);
			up_write(&space_info->groups_sem);
			goto next;
		}
		spin_unlock(&bg->lock);

		/* Get out fast, in case we're unmounting the filesystem */
		if (btrfs_fs_closing(fs_info)) {
			up_write(&space_info->groups_sem);
			goto next;
		}

		/*
		 * Cache the zone_unusable value before turning the block group
		 * to read only. As soon as the blog group is read only it's
		 * zone_unusable value gets moved to the block group's read-only
		 * bytes and isn't available for calculations anymore.
		 */
		zone_unusable = bg->zone_unusable;
		ret = inc_block_group_ro(bg, 0);
		up_write(&space_info->groups_sem);
		if (ret < 0)
			goto next;

		btrfs_info(fs_info,
			"reclaiming chunk %llu with %llu%% used %llu%% unusable",
				bg->start, div_u64(bg->used * 100, bg->length),
				div64_u64(zone_unusable * 100, bg->length));
		trace_btrfs_reclaim_block_group(bg);
		ret = btrfs_relocate_chunk(fs_info, bg->start);
		if (ret)
			btrfs_err(fs_info, "error relocating chunk %llu",
				  bg->start);

next:
		spin_lock(&fs_info->unused_bgs_lock);
		if (ret == -EAGAIN && list_empty(&bg->bg_list))
			list_add_tail(&bg->bg_list, &again_list);
		else
			btrfs_put_block_group(bg);
	}
	list_splice_tail(&again_list, &fs_info->reclaim_bgs);
	spin_unlock(&fs_info->unused_bgs_lock);
	mutex_unlock(&fs_info->reclaim_bgs_lock);
	btrfs_exclop_finish(fs_info);
}
void btrfs_reclaim_bgs_work(struct work_struct *work)
{
	struct btrfs_fs_info *fs_info =
		container_of(work, struct btrfs_fs_info, reclaim_bgs_work);
	struct btrfs_block_group *bg;
	struct btrfs_space_info *space_info;
	LIST_HEAD(again_list);

	if (!test_bit(BTRFS_FS_OPEN, &fs_info->flags))
		return;

	if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_BALANCE))
		return;

	mutex_lock(&fs_info->reclaim_bgs_lock);
	spin_lock(&fs_info->unused_bgs_lock);
	while (!list_empty(&fs_info->reclaim_bgs)) {
		u64 zone_unusable;
		int ret = 0;

		bg = list_first_entry(&fs_info->reclaim_bgs,
				      struct btrfs_block_group,
				      bg_list);
		list_del_init(&bg->bg_list);

		space_info = bg->space_info;
		spin_unlock(&fs_info->unused_bgs_lock);

		 
		down_write(&space_info->groups_sem);

		spin_lock(&bg->lock);
		if (bg->reserved || bg->pinned || bg->ro) {
			 
			spin_unlock(&bg->lock);
			up_write(&space_info->groups_sem);
			goto next;
		}
		spin_unlock(&bg->lock);

		 
		if (btrfs_fs_closing(fs_info)) {
			up_write(&space_info->groups_sem);
			goto next;
		}

		 
		zone_unusable = bg->zone_unusable;
		ret = inc_block_group_ro(bg, 0);
		up_write(&space_info->groups_sem);
		if (ret < 0)
			goto next;

		btrfs_info(fs_info,
			"reclaiming chunk %llu with %llu%% used %llu%% unusable",
				bg->start, div_u64(bg->used * 100, bg->length),
				div64_u64(zone_unusable * 100, bg->length));
		trace_btrfs_reclaim_block_group(bg);
		ret = btrfs_relocate_chunk(fs_info, bg->start);
		if (ret)
			btrfs_err(fs_info, "error relocating chunk %llu",
				  bg->start);

next:
		spin_lock(&fs_info->unused_bgs_lock);
		if (ret == -EAGAIN && list_empty(&bg->bg_list))
			list_add_tail(&bg->bg_list, &again_list);
		else
			btrfs_put_block_group(bg);
	}
	list_splice_tail(&again_list, &fs_info->reclaim_bgs);
	spin_unlock(&fs_info->unused_bgs_lock);
	mutex_unlock(&fs_info->reclaim_bgs_lock);
	btrfs_exclop_finish(fs_info);
}