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@xxxxxxxxxx> --- 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