[RFC PATCH 1/4] xfs: support deactivating AGs

Gao Xiang
2021-04-14T19:53:27Z

To get rid of paralleled requests related to AGs which are pending
for shrinking, mark these perags as inactive rather than playing
with per-ag structures theirselves.

Since in that way, a per-ag lock can be used to stablize the inactive
status together with agi/agf buffer lock (which is much easier than
adding more complicated perag_{get, put} pairs..) Also, Such per-ags
can be released / reused when unmountfs / growfs.

On the read side, pag_inactive_rwsem can be unlocked immediately after
the agf or agi buffer lock is acquired. However, pag_inactive_rwsem
can only be unlocked after the agf/agi buffer locks are all acquired
with the inactive status on the write side.

XXX: maybe there are some missing cases.

Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
---
fs/xfs/libxfs/xfs_ag.c | 16 +++++++++++++---
fs/xfs/libxfs/xfs_alloc.c | 12 +++++++++++-
fs/xfs/libxfs/xfs_ialloc.c | 26 +++++++++++++++++++++++++-
fs/xfs/xfs_mount.c | 2 ++
fs/xfs/xfs_mount.h | 6 ++++++
5 files changed, 57 insertions(+), 5 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c
index c68a36688474..ba5702e5c9ad 100644
--- a/fs/xfs/libxfs/xfs_ag.c
+++ b/fs/xfs/libxfs/xfs_ag.c
@@ -676,16 +676,24 @@ xfs_ag_get_geometry(
if (agno >= mp->m_sb.sb_agcount)
return -EINVAL;

+ pag = xfs_perag_get(mp, agno);
+ down_read(&pag->pag_inactive_rwsem);
+
+ if (pag->pag_inactive) {
+ error = -EBUSY;
+ up_read(&pag->pag_inactive_rwsem);
+ goto out;
+ }
+
/* Lock the AG headers. */
error = xfs_ialloc_read_agi(mp, NULL, agno, &agi_bp);
+ up_read(&pag->pag_inactive_rwsem);
if (error)
- return error;
+ goto out;
error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agf_bp);
if (error)
goto out_agi;

- pag = agi_bp->b_pag;
-
/* Fill out form. */
memset(ageo, 0, sizeof(*ageo));
ageo->ag_number = agno;
@@ -707,5 +715,7 @@ xfs_ag_get_geometry(
xfs_buf_relse(agf_bp);
out_agi:
xfs_buf_relse(agi_bp);
+out:
+ xfs_perag_put(pag);
return error;
}
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index aaa19101bb2a..01d4e4d4c1d6 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -2537,12 +2537,17 @@ xfs_alloc_fix_freelist(
/* deferred ops (AGFL block frees) require permanent transactions */
ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);

+ down_read(&pag->pag_inactive_rwsem);
+ if (pag->pag_inactive)
+ goto out_no_agbp;
+
if (!pag->pagf_init) {
error = xfs_alloc_read_agf(mp, tp, args->agno, flags, &agbp);
if (error) {
/* Couldn't lock the AGF so skip this AG. */
if (error == -EAGAIN)
error = 0;
+ up_read(&pag->pag_inactive_rwsem);
goto out_no_agbp;
}
}
@@ -2555,13 +2560,16 @@ xfs_alloc_fix_freelist(
if (pag->pagf_metadata && (args->datatype & XFS_ALLOC_USERDATA) &&
(flags & XFS_ALLOC_FLAG_TRYLOCK)) {
ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING));
+ up_read(&pag->pag_inactive_rwsem);
goto out_agbp_relse;
}

need = xfs_alloc_min_freelist(mp, pag);
if (!xfs_alloc_space_available(args, need, flags |
- XFS_ALLOC_FLAG_CHECK))
+ XFS_ALLOC_FLAG_CHECK)) {
+ up_read(&pag->pag_inactive_rwsem);
goto out_agbp_relse;
+ }

/*
* Get the a.g. freespace buffer.
@@ -2573,9 +2581,11 @@ xfs_alloc_fix_freelist(
/* Couldn't lock the AGF so skip this AG. */
if (error == -EAGAIN)
error = 0;
+ up_read(&pag->pag_inactive_rwsem);
goto out_no_agbp;
}
}
+ up_read(&pag->pag_inactive_rwsem);

/* reset a padding mismatched agfl before final free space check */
if (pag->pagf_agflreset)
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index eefdb518fe64..4df218eeb088 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -969,11 +969,15 @@ xfs_ialloc_ag_select(
flags = XFS_ALLOC_FLAG_TRYLOCK;
for (;;) {
pag = xfs_perag_get(mp, agno);
+ down_read(&pag->pag_inactive_rwsem);
if (!pag->pagi_inodeok) {
xfs_ialloc_next_ag(mp);
goto nextag;
}

+ if (pag->pag_inactive)
+ goto nextag;
+
if (!pag->pagi_init) {
error = xfs_ialloc_pagi_init(mp, tp, agno);
if (error)
@@ -981,6 +985,7 @@ xfs_ialloc_ag_select(
}

if (pag->pagi_freecount) {
+ up_read(&pag->pag_inactive_rwsem);
xfs_perag_put(pag);
return agno;
}
@@ -1016,10 +1021,12 @@ xfs_ialloc_ag_select(

if (pag->pagf_freeblks >= needspace + ineed &&
longest >= ineed) {
+ up_read(&pag->pag_inactive_rwsem);
xfs_perag_put(pag);
return agno;
}
nextag:
+ up_read(&pag->pag_inactive_rwsem);
xfs_perag_put(pag);
/*
* No point in iterating over the rest, if we're shutting
@@ -1776,10 +1783,13 @@ xfs_dialloc_select_ag(
agno = start_agno;
for (;;) {
pag = xfs_perag_get(mp, agno);
+ down_read(&pag->pag_inactive_rwsem);
if (!pag->pagi_inodeok) {
xfs_ialloc_next_ag(mp);
goto nextag;
}
+ if (pag->pag_inactive)
+ goto nextag;

if (!pag->pagi_init) {
error = xfs_ialloc_pagi_init(mp, *tpp, agno);
@@ -1802,6 +1812,7 @@ xfs_dialloc_select_ag(
break;

if (pag->pagi_freecount) {
+ up_read(&pag->pag_inactive_rwsem);
xfs_perag_put(pag);
goto found_ag;
}
@@ -1825,6 +1836,7 @@ xfs_dialloc_select_ag(
* allocate one of the new inodes.
*/
ASSERT(pag->pagi_freecount > 0);
+ up_read(&pag->pag_inactive_rwsem);
xfs_perag_put(pag);

error = xfs_dialloc_roll(tpp, agbp);
@@ -1838,13 +1850,14 @@ xfs_dialloc_select_ag(
nextag_relse_buffer:
xfs_trans_brelse(*tpp, agbp);
nextag:
+ up_read(&pag->pag_inactive_rwsem);
xfs_perag_put(pag);
if (++agno == mp->m_sb.sb_agcount)
agno = 0;
if (agno == start_agno)
return noroom ? -ENOSPC : 0;
}
-
+ up_read(&pag->pag_inactive_rwsem);
xfs_perag_put(pag);
return error;
found_ag:
@@ -2263,11 +2276,22 @@ xfs_imap_lookup(
{
struct xfs_inobt_rec_incore rec;
struct xfs_btree_cur *cur;
+ struct xfs_perag *pag;
struct xfs_buf *agbp;
int error;
int i;

+ pag = xfs_perag_get(mp, agno);
+ down_read(&pag->pag_inactive_rwsem);
+ if (pag->pag_inactive) {
+ up_read(&pag->pag_inactive_rwsem);
+ xfs_perag_put(pag);
+ return -EINVAL;
+ }
+
error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
+ up_read(&pag->pag_inactive_rwsem);
+ xfs_perag_put(pag);
if (error) {
xfs_alert(mp,
"%s: xfs_ialloc_read_agi() returned error %d, agno %d",
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 1c97b155a8ee..f86360514828 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -235,6 +235,8 @@ xfs_initialize_perag(
if (error)
goto out_hash_destroy;
spin_lock_init(&pag->pag_state_lock);
+
+ init_rwsem(&pag->pag_inactive_rwsem);
}

index = xfs_set_inode_alloc(mp, agcount);
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 81829d19596e..667dae0acaf9 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -309,6 +309,12 @@ typedef struct xfs_perag {
struct xfs_mount *pag_mount; /* owner filesystem */
xfs_agnumber_t pag_agno; /* AG this structure belongs to */
atomic_t pag_ref; /* perag reference count */
+
+ struct rw_semaphore pag_inactive_rwsem;
+ bool pag_inactive;
+
+ /* zero the following fields when growfs pag_inactive == true pags */
+
char pagf_init; /* this agf's entry is initialized */
char pagi_init; /* this agi's entry is initialized */
char pagf_metadata; /* the agf is preferred to be metadata */
--
2.27.0