Skip to content

Commit c658488

Browse files
Christoph Hellwigcmaiolino
authored andcommitted
xfs: handle too many open zones when mounting
When running on conventional zones or devices, the zoned allocator does not have a real write pointer, but instead fakes it up at mount time based on the last block recorded in the rmap. This can create spurious "open" zones when the last written blocks in a conventional zone are invalidated. Add a loop to the mount code to find the conventional zone with the highest used block in the rmap tree and "finish" it until we are below the open zones limit. While we're at it, also error out if there are too many open sequential zones, which can only happen when the user overrode the max open zones limit (or with really buggy hardware reducing the limit, but not much we can do about that). Fixes: 4e4d520 ("xfs: add the zoned space allocator") Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Hans Holmberg <hans.holmberg@wdc.com> Reviewed-by: Damien Le Moal <dlemoal@kernel.org> Signed-off-by: Carlos Maiolino <cem@kernel.org>
1 parent 0236799 commit c658488

2 files changed

Lines changed: 76 additions & 0 deletions

File tree

fs/xfs/xfs_trace.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ DEFINE_EVENT(xfs_zone_alloc_class, name, \
461461
DEFINE_ZONE_ALLOC_EVENT(xfs_zone_record_blocks);
462462
DEFINE_ZONE_ALLOC_EVENT(xfs_zone_skip_blocks);
463463
DEFINE_ZONE_ALLOC_EVENT(xfs_zone_alloc_blocks);
464+
DEFINE_ZONE_ALLOC_EVENT(xfs_zone_spurious_open);
464465

465466
TRACE_EVENT(xfs_zone_gc_select_victim,
466467
TP_PROTO(struct xfs_rtgroup *rtg, unsigned int bucket),

fs/xfs/xfs_zone_alloc.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,77 @@ xfs_report_zones(
12531253
return 0;
12541254
}
12551255

1256+
static inline bool
1257+
xfs_zone_is_conv(
1258+
struct xfs_rtgroup *rtg)
1259+
{
1260+
return !bdev_zone_is_seq(rtg_mount(rtg)->m_rtdev_targp->bt_bdev,
1261+
xfs_gbno_to_daddr(rtg_group(rtg), 0));
1262+
}
1263+
1264+
static struct xfs_open_zone *
1265+
xfs_find_fullest_conventional_open_zone(
1266+
struct xfs_mount *mp)
1267+
{
1268+
struct xfs_zone_info *zi = mp->m_zone_info;
1269+
struct xfs_open_zone *found = NULL, *oz;
1270+
1271+
spin_lock(&zi->zi_open_zones_lock);
1272+
list_for_each_entry(oz, &zi->zi_open_zones, oz_entry) {
1273+
if (!xfs_zone_is_conv(oz->oz_rtg))
1274+
continue;
1275+
if (!found || oz->oz_allocated > found->oz_allocated)
1276+
found = oz;
1277+
}
1278+
spin_unlock(&zi->zi_open_zones_lock);
1279+
1280+
return found;
1281+
}
1282+
1283+
/*
1284+
* Find the fullest conventional zones and remove them from the open zone pool
1285+
* until we are at the open zone limit.
1286+
*
1287+
* We can end up with spurious "open" zones when the last blocks in a fully
1288+
* written zone were invalidate as there is no write pointer for conventional
1289+
* zones.
1290+
*
1291+
* If we are still over the limit when there is no conventional open zone left,
1292+
* the user overrode the max open zones limit using the max_open_zones mount
1293+
* option we should fail.
1294+
*/
1295+
static int
1296+
xfs_finish_spurious_open_zones(
1297+
struct xfs_mount *mp,
1298+
struct xfs_init_zones *iz)
1299+
{
1300+
struct xfs_zone_info *zi = mp->m_zone_info;
1301+
1302+
while (zi->zi_nr_open_zones > mp->m_max_open_zones) {
1303+
struct xfs_open_zone *oz;
1304+
xfs_filblks_t adjust;
1305+
1306+
oz = xfs_find_fullest_conventional_open_zone(mp);
1307+
if (!oz) {
1308+
xfs_err(mp,
1309+
"too many open zones for max_open_zones limit (%u/%u)",
1310+
zi->zi_nr_open_zones, mp->m_max_open_zones);
1311+
return -EINVAL;
1312+
}
1313+
1314+
xfs_rtgroup_lock(oz->oz_rtg, XFS_RTGLOCK_RMAP);
1315+
adjust = rtg_blocks(oz->oz_rtg) - oz->oz_written;
1316+
trace_xfs_zone_spurious_open(oz, oz->oz_written, adjust);
1317+
oz->oz_written = rtg_blocks(oz->oz_rtg);
1318+
xfs_open_zone_mark_full(oz);
1319+
xfs_rtgroup_unlock(oz->oz_rtg, XFS_RTGLOCK_RMAP);
1320+
iz->available -= adjust;
1321+
iz->reclaimable += adjust;
1322+
}
1323+
1324+
return 0;
1325+
}
1326+
12561327
int
12571328
xfs_mount_zones(
12581329
struct xfs_mount *mp)
@@ -1294,6 +1365,10 @@ xfs_mount_zones(
12941365
if (error)
12951366
goto out_free_zone_info;
12961367

1368+
error = xfs_finish_spurious_open_zones(mp, &iz);
1369+
if (error)
1370+
goto out_free_zone_info;
1371+
12971372
xfs_set_freecounter(mp, XC_FREE_RTAVAILABLE, iz.available);
12981373
xfs_set_freecounter(mp, XC_FREE_RTEXTENTS,
12991374
iz.available + iz.reclaimable);

0 commit comments

Comments
 (0)