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: Hans Holmberg <hans.holmberg@wdc.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
Hans Holmberg 2025-09-01 10:52:05 +00:00 committed by Carlos Maiolino
parent 94deac977f
commit 0301dae732
1 changed files with 63 additions and 61 deletions

View File

@ -493,64 +493,64 @@ xfs_try_open_zone(
return oz; return oz;
} }
/* enum xfs_zone_alloc_score {
* For data with short or medium lifetime, try to colocated it into an /* Any open zone will do it, we're desperate */
* already open zone with a matching temperature. XFS_ZONE_ALLOC_ANY = 0,
*/
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 /* It better fit somehow */
xfs_good_hint_match( XFS_ZONE_ALLOC_OK = 1,
struct xfs_open_zone *oz,
enum rw_hint file_hint) /* Only reuse a zone if it fits really well. */
{ XFS_ZONE_ALLOC_GOOD = 2,
switch (oz->oz_write_hint) { };
case WRITE_LIFE_LONG:
case WRITE_LIFE_EXTREME: /*
/* colocate long and extreme */ * Life time hint co-location matrix. Fields not set default to 0
if (file_hint == WRITE_LIFE_LONG || * aka XFS_ZONE_ALLOC_ANY.
file_hint == WRITE_LIFE_EXTREME) */
return true; static const unsigned int
break; xfs_zoned_hint_score[WRITE_LIFE_HINT_NR][WRITE_LIFE_HINT_NR] = {
case WRITE_LIFE_MEDIUM: [WRITE_LIFE_NOT_SET] = {
/* colocate medium with medium */ [WRITE_LIFE_NOT_SET] = XFS_ZONE_ALLOC_OK,
if (file_hint == WRITE_LIFE_MEDIUM) [WRITE_LIFE_NONE] = XFS_ZONE_ALLOC_OK,
return true; [WRITE_LIFE_SHORT] = XFS_ZONE_ALLOC_OK,
break; },
case WRITE_LIFE_SHORT: [WRITE_LIFE_NONE] = {
case WRITE_LIFE_NONE: [WRITE_LIFE_NOT_SET] = XFS_ZONE_ALLOC_OK,
case WRITE_LIFE_NOT_SET: [WRITE_LIFE_NONE] = XFS_ZONE_ALLOC_GOOD,
/* colocate short and none */ [WRITE_LIFE_SHORT] = XFS_ZONE_ALLOC_GOOD,
if (file_hint <= WRITE_LIFE_SHORT) },
return true; [WRITE_LIFE_SHORT] = {
break; [WRITE_LIFE_NOT_SET] = XFS_ZONE_ALLOC_GOOD,
} [WRITE_LIFE_NONE] = XFS_ZONE_ALLOC_GOOD,
return false; [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 static bool
xfs_try_use_zone( xfs_try_use_zone(
struct xfs_zone_info *zi, struct xfs_zone_info *zi,
enum rw_hint file_hint, enum rw_hint file_hint,
struct xfs_open_zone *oz, struct xfs_open_zone *oz,
bool lowspace) unsigned int goodness)
{ {
if (oz->oz_allocated == rtg_blocks(oz->oz_rtg)) if (oz->oz_allocated == rtg_blocks(oz->oz_rtg))
return false; 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; return false;
if (!atomic_inc_not_zero(&oz->oz_ref)) if (!atomic_inc_not_zero(&oz->oz_ref))
return false; return false;
@ -581,14 +581,14 @@ static struct xfs_open_zone *
xfs_select_open_zone_lru( xfs_select_open_zone_lru(
struct xfs_zone_info *zi, struct xfs_zone_info *zi,
enum rw_hint file_hint, enum rw_hint file_hint,
bool lowspace) unsigned int goodness)
{ {
struct xfs_open_zone *oz; struct xfs_open_zone *oz;
lockdep_assert_held(&zi->zi_open_zones_lock); lockdep_assert_held(&zi->zi_open_zones_lock);
list_for_each_entry(oz, &zi->zi_open_zones, oz_entry) 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; return oz;
cond_resched_lock(&zi->zi_open_zones_lock); cond_resched_lock(&zi->zi_open_zones_lock);
@ -651,9 +651,11 @@ xfs_select_zone_nowait(
* data. * data.
*/ */
spin_lock(&zi->zi_open_zones_lock); spin_lock(&zi->zi_open_zones_lock);
if (xfs_colocate_eagerly(write_hint)) oz = xfs_select_open_zone_lru(zi, write_hint, XFS_ZONE_ALLOC_GOOD);
oz = xfs_select_open_zone_lru(zi, write_hint, false); if (oz)
else if (pack_tight) goto out_unlock;
if (pack_tight)
oz = xfs_select_open_zone_mru(zi, write_hint); oz = xfs_select_open_zone_mru(zi, write_hint);
if (oz) if (oz)
goto out_unlock; goto out_unlock;
@ -667,16 +669,16 @@ xfs_select_zone_nowait(
goto out_unlock; goto out_unlock;
/* /*
* Try to colocate cold data with other cold data if we failed to open a * Try to find an zone that is an ok match to colocate data with.
* new zone for it.
*/ */
if (write_hint != WRITE_LIFE_NOT_SET && oz = xfs_select_open_zone_lru(zi, write_hint, XFS_ZONE_ALLOC_OK);
!xfs_colocate_eagerly(write_hint)) if (oz)
oz = xfs_select_open_zone_lru(zi, write_hint, false); goto out_unlock;
if (!oz)
oz = xfs_select_open_zone_lru(zi, WRITE_LIFE_NOT_SET, false); /*
if (!oz) * Pick the least recently used zone, regardless of hint match
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: out_unlock:
spin_unlock(&zi->zi_open_zones_lock); spin_unlock(&zi->zi_open_zones_lock);
return oz; return oz;