Commit d06e3bb6 authored by Qu Wenruo's avatar Qu Wenruo Committed by David Sterba
Browse files

btrfs: Refactor clustered extent allocation into find_free_extent_clustered



We have two main methods to find free extents inside a block group:

1) clustered allocation
2) unclustered allocation

This patch will extract the clustered allocation into
find_free_extent_clustered() to make it a little easier to read.

Instead of jumping between different labels in find_free_extent(), the
helper function will use return value to indicate different behavior.

Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Reviewed-by: default avatarSu Yue <suy.fnst@cn.fujitsu.com>
Reviewed-by: default avatarJosef Bacik <josef@toxicpanda.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent b4bd745d
Loading
Loading
Loading
Loading
+122 −123
Original line number Diff line number Diff line
@@ -7305,6 +7305,116 @@ struct find_free_extent_ctl {
	u64 found_offset;
};


/*
 * Helper function for find_free_extent().
 *
 * Return -ENOENT to inform caller that we need fallback to unclustered mode.
 * Return -EAGAIN to inform caller that we need to re-search this block group
 * Return >0 to inform caller that we find nothing
 * Return 0 means we have found a location and set ffe_ctl->found_offset.
 */
static int find_free_extent_clustered(struct btrfs_block_group_cache *bg,
		struct btrfs_free_cluster *last_ptr,
		struct find_free_extent_ctl *ffe_ctl,
		struct btrfs_block_group_cache **cluster_bg_ret)
{
	struct btrfs_fs_info *fs_info = bg->fs_info;
	struct btrfs_block_group_cache *cluster_bg;
	u64 aligned_cluster;
	u64 offset;
	int ret;

	cluster_bg = btrfs_lock_cluster(bg, last_ptr, ffe_ctl->delalloc);
	if (!cluster_bg)
		goto refill_cluster;
	if (cluster_bg != bg && (cluster_bg->ro ||
	    !block_group_bits(cluster_bg, ffe_ctl->flags)))
		goto release_cluster;

	offset = btrfs_alloc_from_cluster(cluster_bg, last_ptr,
			ffe_ctl->num_bytes, cluster_bg->key.objectid,
			&ffe_ctl->max_extent_size);
	if (offset) {
		/* We have a block, we're done */
		spin_unlock(&last_ptr->refill_lock);
		trace_btrfs_reserve_extent_cluster(cluster_bg,
				ffe_ctl->search_start, ffe_ctl->num_bytes);
		*cluster_bg_ret = cluster_bg;
		ffe_ctl->found_offset = offset;
		return 0;
	}
	WARN_ON(last_ptr->block_group != cluster_bg);

release_cluster:
	/*
	 * If we are on LOOP_NO_EMPTY_SIZE, we can't set up a new clusters, so
	 * lets just skip it and let the allocator find whatever block it can
	 * find. If we reach this point, we will have tried the cluster
	 * allocator plenty of times and not have found anything, so we are
	 * likely way too fragmented for the clustering stuff to find anything.
	 *
	 * However, if the cluster is taken from the current block group,
	 * release the cluster first, so that we stand a better chance of
	 * succeeding in the unclustered allocation.
	 */
	if (ffe_ctl->loop >= LOOP_NO_EMPTY_SIZE && cluster_bg != bg) {
		spin_unlock(&last_ptr->refill_lock);
		btrfs_release_block_group(cluster_bg, ffe_ctl->delalloc);
		return -ENOENT;
	}

	/* This cluster didn't work out, free it and start over */
	btrfs_return_cluster_to_free_space(NULL, last_ptr);

	if (cluster_bg != bg)
		btrfs_release_block_group(cluster_bg, ffe_ctl->delalloc);

refill_cluster:
	if (ffe_ctl->loop >= LOOP_NO_EMPTY_SIZE) {
		spin_unlock(&last_ptr->refill_lock);
		return -ENOENT;
	}

	aligned_cluster = max_t(u64,
			ffe_ctl->empty_cluster + ffe_ctl->empty_size,
			bg->full_stripe_len);
	ret = btrfs_find_space_cluster(fs_info, bg, last_ptr,
			ffe_ctl->search_start, ffe_ctl->num_bytes,
			aligned_cluster);
	if (ret == 0) {
		/* Now pull our allocation out of this cluster */
		offset = btrfs_alloc_from_cluster(bg, last_ptr,
				ffe_ctl->num_bytes, ffe_ctl->search_start,
				&ffe_ctl->max_extent_size);
		if (offset) {
			/* We found one, proceed */
			spin_unlock(&last_ptr->refill_lock);
			trace_btrfs_reserve_extent_cluster(bg,
					ffe_ctl->search_start,
					ffe_ctl->num_bytes);
			ffe_ctl->found_offset = offset;
			return 0;
		}
	} else if (!ffe_ctl->cached && ffe_ctl->loop > LOOP_CACHING_NOWAIT &&
		   !ffe_ctl->retry_clustered) {
		spin_unlock(&last_ptr->refill_lock);

		ffe_ctl->retry_clustered = true;
		wait_block_group_cache_progress(bg, ffe_ctl->num_bytes +
				ffe_ctl->empty_cluster + ffe_ctl->empty_size);
		return -EAGAIN;
	}
	/*
	 * At this point we either didn't find a cluster or we weren't able to
	 * allocate a block from our cluster.  Free the cluster we've been
	 * trying to use, and go to the next block group.
	 */
	btrfs_return_cluster_to_free_space(NULL, last_ptr);
	spin_unlock(&last_ptr->refill_lock);
	return 1;
}

/*
 * walks the btree of allocated extents and find a hole of a given size.
 * The key ins is changed to record the hole:
@@ -7487,137 +7597,26 @@ have_block_group:
		 * lets look there
		 */
		if (last_ptr && use_cluster) {
			struct btrfs_block_group_cache *used_block_group;
			unsigned long aligned_cluster;
			/*
			 * the refill lock keeps out other
			 * people trying to start a new cluster
			 */
			used_block_group = btrfs_lock_cluster(block_group,
							      last_ptr,
							      delalloc);
			if (!used_block_group)
				goto refill_cluster;
			struct btrfs_block_group_cache *cluster_bg = NULL;

			if (used_block_group != block_group &&
			    (used_block_group->ro ||
			     !block_group_bits(used_block_group,
					       ffe_ctl.flags)))
				goto release_cluster;
			ret = find_free_extent_clustered(block_group, last_ptr,
							 &ffe_ctl, &cluster_bg);

			ffe_ctl.found_offset = btrfs_alloc_from_cluster(
						used_block_group,
						last_ptr,
						num_bytes,
						used_block_group->key.objectid,
						&ffe_ctl.max_extent_size);
			if (ffe_ctl.found_offset) {
				/* we have a block, we're done */
				spin_unlock(&last_ptr->refill_lock);
				trace_btrfs_reserve_extent_cluster(
						used_block_group,
						ffe_ctl.search_start,
						num_bytes);
				if (used_block_group != block_group) {
			if (ret == 0) {
				if (cluster_bg && cluster_bg != block_group) {
					btrfs_release_block_group(block_group,
								  delalloc);
					block_group = used_block_group;
				}
				goto checks;
			}

			WARN_ON(last_ptr->block_group != used_block_group);
release_cluster:
			/* If we are on LOOP_NO_EMPTY_SIZE, we can't
			 * set up a new clusters, so lets just skip it
			 * and let the allocator find whatever block
			 * it can find.  If we reach this point, we
			 * will have tried the cluster allocator
			 * plenty of times and not have found
			 * anything, so we are likely way too
			 * fragmented for the clustering stuff to find
			 * anything.
			 *
			 * However, if the cluster is taken from the
			 * current block group, release the cluster
			 * first, so that we stand a better chance of
			 * succeeding in the unclustered
			 * allocation.  */
			if (ffe_ctl.loop >= LOOP_NO_EMPTY_SIZE &&
			    used_block_group != block_group) {
				spin_unlock(&last_ptr->refill_lock);
				btrfs_release_block_group(used_block_group,
							  delalloc);
				goto unclustered_alloc;
			}

			/*
			 * this cluster didn't work out, free it and
			 * start over
			 */
			btrfs_return_cluster_to_free_space(NULL, last_ptr);

			if (used_block_group != block_group)
				btrfs_release_block_group(used_block_group,
							  delalloc);
refill_cluster:
			if (ffe_ctl.loop >= LOOP_NO_EMPTY_SIZE) {
				spin_unlock(&last_ptr->refill_lock);
				goto unclustered_alloc;
					block_group = cluster_bg;
				}

			aligned_cluster = max_t(unsigned long,
					ffe_ctl.empty_cluster + empty_size,
					block_group->full_stripe_len);

			/* allocate a cluster in this block group */
			ret = btrfs_find_space_cluster(fs_info, block_group,
						       last_ptr,
						       ffe_ctl.search_start,
						       num_bytes,
						       aligned_cluster);
			if (ret == 0) {
				/*
				 * now pull our allocation out of this
				 * cluster
				 */
				ffe_ctl.found_offset = btrfs_alloc_from_cluster(
						block_group, last_ptr,
						num_bytes, ffe_ctl.search_start,
						&ffe_ctl.max_extent_size);
				if (ffe_ctl.found_offset) {
					/* we found one, proceed */
					spin_unlock(&last_ptr->refill_lock);
					trace_btrfs_reserve_extent_cluster(
						block_group,
						ffe_ctl.search_start,
						num_bytes);
				goto checks;
				}
			} else if (!ffe_ctl.cached &&
				   ffe_ctl.loop > LOOP_CACHING_NOWAIT &&
				   !ffe_ctl.retry_clustered) {
				spin_unlock(&last_ptr->refill_lock);

				ffe_ctl.retry_clustered = true;
				wait_block_group_cache_progress(block_group,
				       num_bytes + ffe_ctl.empty_cluster +
				       empty_size);
			} else if (ret == -EAGAIN) {
				goto have_block_group;
			}

			/*
			 * at this point we either didn't find a cluster
			 * or we weren't able to allocate a block from our
			 * cluster.  Free the cluster we've been trying
			 * to use, and go to the next block group
			 */
			btrfs_return_cluster_to_free_space(NULL, last_ptr);
			spin_unlock(&last_ptr->refill_lock);
			} else if (ret > 0) {
				goto loop;
			}
			/* ret == -ENOENT case falls through */
		}

unclustered_alloc:
		/*
		 * We are doing an unclustered alloc, set the fragmented flag so
		 * we don't bother trying to setup a cluster again until we get