Commit 0301dae7 authored by Hans Holmberg's avatar Hans Holmberg Committed by Carlos Maiolino
Browse files

xfs: refactor hint based zone allocation



Replace the co-location code with a matrix that makes it more clear
on how the decisions are made.

The matrix contains scores for zone/file hint combinations. A "GOOD"
score for an open zone will result in immediate co-location while "OK"
combinations will only be picked if we cannot open a new zone.

Signed-off-by: default avatarHans Holmberg <hans.holmberg@wdc.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarCarlos Maiolino <cem@kernel.org>
parent 94deac97
Loading
Loading
Loading
Loading
+62 −60
Original line number Diff line number Diff line
@@ -493,64 +493,64 @@ xfs_try_open_zone(
	return oz;
}

enum xfs_zone_alloc_score {
	/* Any open zone will do it, we're desperate */
	XFS_ZONE_ALLOC_ANY	= 0,

	/* It better fit somehow */
	XFS_ZONE_ALLOC_OK	= 1,

	/* Only reuse a zone if it fits really well. */
	XFS_ZONE_ALLOC_GOOD	= 2,
};

/*
 * For data with short or medium lifetime, try to colocated it into an
 * already open zone with a matching temperature.
 * Life time hint co-location matrix.  Fields not set default to 0
 * aka XFS_ZONE_ALLOC_ANY.
 */
static bool
xfs_colocate_eagerly(
	enum rw_hint		file_hint)
{
	switch (file_hint) {
	case WRITE_LIFE_MEDIUM:
	case WRITE_LIFE_SHORT:
	case WRITE_LIFE_NONE:
		return true;
	default:
		return false;
	}
}

static bool
xfs_good_hint_match(
	struct xfs_open_zone	*oz,
	enum rw_hint		file_hint)
{
	switch (oz->oz_write_hint) {
	case WRITE_LIFE_LONG:
	case WRITE_LIFE_EXTREME:
		/* colocate long and extreme */
		if (file_hint == WRITE_LIFE_LONG ||
		    file_hint == WRITE_LIFE_EXTREME)
			return true;
		break;
	case WRITE_LIFE_MEDIUM:
		/* colocate medium with medium */
		if (file_hint == WRITE_LIFE_MEDIUM)
			return true;
		break;
	case WRITE_LIFE_SHORT:
	case WRITE_LIFE_NONE:
	case WRITE_LIFE_NOT_SET:
		/* colocate short and none */
		if (file_hint <= WRITE_LIFE_SHORT)
			return true;
		break;
	}
	return false;
}
static const unsigned int
xfs_zoned_hint_score[WRITE_LIFE_HINT_NR][WRITE_LIFE_HINT_NR] = {
	[WRITE_LIFE_NOT_SET]	= {
		[WRITE_LIFE_NOT_SET]	= XFS_ZONE_ALLOC_OK,
		[WRITE_LIFE_NONE]	= XFS_ZONE_ALLOC_OK,
		[WRITE_LIFE_SHORT]	= XFS_ZONE_ALLOC_OK,
	},
	[WRITE_LIFE_NONE]	= {
		[WRITE_LIFE_NOT_SET]	= XFS_ZONE_ALLOC_OK,
		[WRITE_LIFE_NONE]	= XFS_ZONE_ALLOC_GOOD,
		[WRITE_LIFE_SHORT]	= XFS_ZONE_ALLOC_GOOD,
	},
	[WRITE_LIFE_SHORT]	= {
		[WRITE_LIFE_NOT_SET]	= XFS_ZONE_ALLOC_GOOD,
		[WRITE_LIFE_NONE]	= XFS_ZONE_ALLOC_GOOD,
		[WRITE_LIFE_SHORT]	= XFS_ZONE_ALLOC_GOOD,
	},
	[WRITE_LIFE_MEDIUM]	= {
		[WRITE_LIFE_MEDIUM]	= XFS_ZONE_ALLOC_GOOD,
	},
	[WRITE_LIFE_LONG]	= {
		[WRITE_LIFE_LONG]	= XFS_ZONE_ALLOC_OK,
		[WRITE_LIFE_EXTREME]	= XFS_ZONE_ALLOC_OK,
	},
	[WRITE_LIFE_EXTREME]	= {
		[WRITE_LIFE_LONG]	= XFS_ZONE_ALLOC_OK,
		[WRITE_LIFE_EXTREME]	= XFS_ZONE_ALLOC_OK,
	},
};

static bool
xfs_try_use_zone(
	struct xfs_zone_info	*zi,
	enum rw_hint		file_hint,
	struct xfs_open_zone	*oz,
	bool			lowspace)
	unsigned int		goodness)
{
	if (oz->oz_allocated == rtg_blocks(oz->oz_rtg))
		return false;
	if (!lowspace && !xfs_good_hint_match(oz, file_hint))

	if (xfs_zoned_hint_score[oz->oz_write_hint][file_hint] < goodness)
		return false;

	if (!atomic_inc_not_zero(&oz->oz_ref))
		return false;

@@ -581,14 +581,14 @@ static struct xfs_open_zone *
xfs_select_open_zone_lru(
	struct xfs_zone_info	*zi,
	enum rw_hint		file_hint,
	bool			lowspace)
	unsigned int		goodness)
{
	struct xfs_open_zone	*oz;

	lockdep_assert_held(&zi->zi_open_zones_lock);

	list_for_each_entry(oz, &zi->zi_open_zones, oz_entry)
		if (xfs_try_use_zone(zi, file_hint, oz, lowspace))
		if (xfs_try_use_zone(zi, file_hint, oz, goodness))
			return oz;

	cond_resched_lock(&zi->zi_open_zones_lock);
@@ -651,9 +651,11 @@ xfs_select_zone_nowait(
	 * data.
	 */
	spin_lock(&zi->zi_open_zones_lock);
	if (xfs_colocate_eagerly(write_hint))
		oz = xfs_select_open_zone_lru(zi, write_hint, false);
	else if (pack_tight)
	oz = xfs_select_open_zone_lru(zi, write_hint, XFS_ZONE_ALLOC_GOOD);
	if (oz)
		goto out_unlock;

	if (pack_tight)
		oz = xfs_select_open_zone_mru(zi, write_hint);
	if (oz)
		goto out_unlock;
@@ -667,16 +669,16 @@ xfs_select_zone_nowait(
		goto out_unlock;

	/*
	 * Try to colocate cold data with other cold data if we failed to open a
	 * new zone for it.
	 * Try to find an zone that is an ok match to colocate data with.
	 */
	oz = xfs_select_open_zone_lru(zi, write_hint, XFS_ZONE_ALLOC_OK);
	if (oz)
		goto out_unlock;

	/*
	 * Pick the least recently used zone, regardless of hint match
	 */
	if (write_hint != WRITE_LIFE_NOT_SET &&
	    !xfs_colocate_eagerly(write_hint))
		oz = xfs_select_open_zone_lru(zi, write_hint, false);
	if (!oz)
		oz = xfs_select_open_zone_lru(zi, WRITE_LIFE_NOT_SET, false);
	if (!oz)
		oz = xfs_select_open_zone_lru(zi, WRITE_LIFE_NOT_SET, true);
	oz = xfs_select_open_zone_lru(zi, write_hint, XFS_ZONE_ALLOC_ANY);
out_unlock:
	spin_unlock(&zi->zi_open_zones_lock);
	return oz;