int btrfs_inc_block_group_ro(struct btrfs_block_group *cache,
			     bool do_chunk_alloc)
{
	struct btrfs_fs_info *fs_info = cache->fs_info;
	struct btrfs_trans_handle *trans;
	u64 alloc_flags;
	int ret;
	bool dirty_bg_running;

	do {
		trans = btrfs_join_transaction(fs_info->extent_root);
		if (IS_ERR(trans))
			return PTR_ERR(trans);

		dirty_bg_running = false;

		/*
		 * We're not allowed to set block groups readonly after the dirty
		 * block group cache has started writing.  If it already started,
		 * back off and let this transaction commit.
		 */
		mutex_lock(&fs_info->ro_block_group_mutex);
		if (test_bit(BTRFS_TRANS_DIRTY_BG_RUN, &trans->transaction->flags)) {
			u64 transid = trans->transid;

			mutex_unlock(&fs_info->ro_block_group_mutex);
			btrfs_end_transaction(trans);

			ret = btrfs_wait_for_commit(fs_info, transid);
			if (ret)
				return ret;
			dirty_bg_running = true;
		}
	} while (dirty_bg_running);

	if (do_chunk_alloc) {
		/*
		 * If we are changing raid levels, try to allocate a
		 * corresponding block group with the new raid level.
		 */
		alloc_flags = btrfs_get_alloc_profile(fs_info, cache->flags);
		if (alloc_flags != cache->flags) {
			ret = btrfs_chunk_alloc(trans, alloc_flags,
						CHUNK_ALLOC_FORCE);
			/*
			 * ENOSPC is allowed here, we may have enough space
			 * already allocated at the new raid level to carry on
			 */
			if (ret == -ENOSPC)
				ret = 0;
			if (ret < 0)
				goto out;
		}
	}

	ret = inc_block_group_ro(cache, 0);
	if (!do_chunk_alloc || ret == -ETXTBSY)
		goto unlock_out;
	if (!ret)
		goto out;
	alloc_flags = btrfs_get_alloc_profile(fs_info, cache->space_info->flags);
	ret = btrfs_chunk_alloc(trans, alloc_flags, CHUNK_ALLOC_FORCE);
	if (ret < 0)
		goto out;
	ret = inc_block_group_ro(cache, 0);
	if (ret == -ETXTBSY)
		goto unlock_out;
out:
	if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) {
		alloc_flags = btrfs_get_alloc_profile(fs_info, cache->flags);
		mutex_lock(&fs_info->chunk_mutex);
		check_system_chunk(trans, alloc_flags);
		mutex_unlock(&fs_info->chunk_mutex);
	}
unlock_out:
	mutex_unlock(&fs_info->ro_block_group_mutex);

	btrfs_end_transaction(trans);
	return ret;
}
int btrfs_inc_block_group_ro(struct btrfs_block_group *cache,
			     bool do_chunk_alloc)
{
	struct btrfs_fs_info *fs_info = cache->fs_info;
	struct btrfs_trans_handle *trans;
	u64 alloc_flags;
	int ret;
	bool dirty_bg_running;

	do {
		trans = btrfs_join_transaction(fs_info->extent_root);
		if (IS_ERR(trans))
			return PTR_ERR(trans);

		dirty_bg_running = false;

		 
		mutex_lock(&fs_info->ro_block_group_mutex);
		if (test_bit(BTRFS_TRANS_DIRTY_BG_RUN, &trans->transaction->flags)) {
			u64 transid = trans->transid;

			mutex_unlock(&fs_info->ro_block_group_mutex);
			btrfs_end_transaction(trans);

			ret = btrfs_wait_for_commit(fs_info, transid);
			if (ret)
				return ret;
			dirty_bg_running = true;
		}
	} while (dirty_bg_running);

	if (do_chunk_alloc) {
		 
		alloc_flags = btrfs_get_alloc_profile(fs_info, cache->flags);
		if (alloc_flags != cache->flags) {
			ret = btrfs_chunk_alloc(trans, alloc_flags,
						CHUNK_ALLOC_FORCE);
			 
			if (ret == -ENOSPC)
				ret = 0;
			if (ret < 0)
				goto out;
		}
	}

	ret = inc_block_group_ro(cache, 0);
	if (!do_chunk_alloc || ret == -ETXTBSY)
		goto unlock_out;
	if (!ret)
		goto out;
	alloc_flags = btrfs_get_alloc_profile(fs_info, cache->space_info->flags);
	ret = btrfs_chunk_alloc(trans, alloc_flags, CHUNK_ALLOC_FORCE);
	if (ret < 0)
		goto out;
	ret = inc_block_group_ro(cache, 0);
	if (ret == -ETXTBSY)
		goto unlock_out;
out:
	if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) {
		alloc_flags = btrfs_get_alloc_profile(fs_info, cache->flags);
		mutex_lock(&fs_info->chunk_mutex);
		check_system_chunk(trans, alloc_flags);
		mutex_unlock(&fs_info->chunk_mutex);
	}
unlock_out:
	mutex_unlock(&fs_info->ro_block_group_mutex);

	btrfs_end_transaction(trans);
	return ret;
}