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: Qu Wenruo <wqu@suse.com> Reviewed-by: Su Yue <suy.fnst@cn.fujitsu.com> Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
		
							parent
							
								
									b4bd745d12
								
							
						
					
					
						commit
						d06e3bb690
					
				| @ -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; | ||||
| 					block_group = cluster_bg; | ||||
| 				} | ||||
| 				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; | ||||
| 			} | ||||
| 
 | ||||
| 			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; | ||||
| 			} else if (ret > 0) { | ||||
| 				goto loop; | ||||
| 			} | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * 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); | ||||
| 			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 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user