From aececc9f8dec92a25c84a3378021636ce58d72dc Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 9 Dec 2020 10:02:17 -0800 Subject: xfs: introduce xfs_dialloc_roll() Introduce a helper to make the on-disk inode allocation rolling logic clearer in preparation of the following cleanup. Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Dave Chinner Signed-off-by: Gao Xiang Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_ialloc.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs/xfs/libxfs/xfs_ialloc.h') diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h index 72b3468b97b1..bd6e0db9e23c 100644 --- a/fs/xfs/libxfs/xfs_ialloc.h +++ b/fs/xfs/libxfs/xfs_ialloc.h @@ -32,6 +32,11 @@ xfs_make_iptr(struct xfs_mount *mp, struct xfs_buf *b, int o) return xfs_buf_offset(b, o << (mp)->m_sb.sb_inodelog); } +int +xfs_dialloc_roll( + struct xfs_trans **tpp, + struct xfs_buf *agibp); + /* * Allocate an inode on disk. * Mode is used to tell whether the new inode will need space, and whether -- cgit From f3bf6e0f1196c69a7b0412521596cd1cc7622a82 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 9 Dec 2020 10:05:16 -0800 Subject: xfs: move xfs_dialloc_roll() into xfs_dialloc() Get rid of the confusing ialloc_context and failure handling around xfs_dialloc() by moving xfs_dialloc_roll() into xfs_dialloc(). Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner Signed-off-by: Gao Xiang Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_ialloc.c | 59 ++++++++++++++++------------------------------ fs/xfs/libxfs/xfs_ialloc.h | 21 +---------------- fs/xfs/xfs_inode.c | 38 +++-------------------------- 3 files changed, 24 insertions(+), 94 deletions(-) (limited to 'fs/xfs/libxfs/xfs_ialloc.h') diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index 3ae83f6998dc..cf1cc9b40e1b 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -1682,7 +1682,7 @@ error_cur: return error; } -int +static int xfs_dialloc_roll( struct xfs_trans **tpp, struct xfs_buf *agibp) @@ -1723,30 +1723,18 @@ xfs_dialloc_roll( * Mode is used to tell whether the new inode will need space, and whether it * is a directory. * - * This function is designed to be called twice if it has to do an allocation - * to make more free inodes. On the first call, *IO_agbp should be set to NULL. - * If an inode is available without having to performn an allocation, an inode - * number is returned. In this case, *IO_agbp is set to NULL. If an allocation - * needs to be done, xfs_dialloc returns the current AGI buffer in *IO_agbp. - * The caller should then commit the current transaction, allocate a - * new transaction, and call xfs_dialloc() again, passing in the previous value - * of *IO_agbp. IO_agbp should be held across the transactions. Since the AGI - * buffer is locked across the two calls, the second call is guaranteed to have - * a free inode available. - * * Once we successfully pick an inode its number is returned and the on-disk * data structures are updated. The inode itself is not read in, since doing so * would break ordering constraints with xfs_reclaim. */ int xfs_dialloc( - struct xfs_trans *tp, + struct xfs_trans **tpp, xfs_ino_t parent, umode_t mode, - struct xfs_buf **IO_agbp, xfs_ino_t *inop) { - struct xfs_mount *mp = tp->t_mountp; + struct xfs_mount *mp = (*tpp)->t_mountp; struct xfs_buf *agbp; xfs_agnumber_t agno; int error; @@ -1757,21 +1745,11 @@ xfs_dialloc( struct xfs_ino_geometry *igeo = M_IGEO(mp); bool okalloc = true; - if (*IO_agbp) { - /* - * If the caller passes in a pointer to the AGI buffer, - * continue where we left off before. In this case, we - * know that the allocation group has free inodes. - */ - agbp = *IO_agbp; - goto out_alloc; - } - /* * We do not have an agbp, so select an initial allocation * group for inode allocation. */ - start_agno = xfs_ialloc_ag_select(tp, parent, mode); + start_agno = xfs_ialloc_ag_select(*tpp, parent, mode); if (start_agno == NULLAGNUMBER) { *inop = NULLFSINO; return 0; @@ -1806,7 +1784,7 @@ xfs_dialloc( } if (!pag->pagi_init) { - error = xfs_ialloc_pagi_init(mp, tp, agno); + error = xfs_ialloc_pagi_init(mp, *tpp, agno); if (error) goto out_error; } @@ -1821,7 +1799,7 @@ xfs_dialloc( * Then read in the AGI buffer and recheck with the AGI buffer * lock held. */ - error = xfs_ialloc_read_agi(mp, tp, agno, &agbp); + error = xfs_ialloc_read_agi(mp, *tpp, agno, &agbp); if (error) goto out_error; @@ -1834,9 +1812,9 @@ xfs_dialloc( goto nextag_relse_buffer; - error = xfs_ialloc_ag_alloc(tp, agbp, &ialloced); + error = xfs_ialloc_ag_alloc(*tpp, agbp, &ialloced); if (error) { - xfs_trans_brelse(tp, agbp); + xfs_trans_brelse(*tpp, agbp); if (error != -ENOSPC) goto out_error; @@ -1848,21 +1826,25 @@ xfs_dialloc( if (ialloced) { /* - * We successfully allocated some inodes, return - * the current context to the caller so that it - * can commit the current transaction and call - * us again where we left off. + * We successfully allocated space for an inode cluster + * in this AG. Roll the transaction so that we can + * allocate one of the new inodes. */ ASSERT(pag->pagi_freecount > 0); xfs_perag_put(pag); - *IO_agbp = agbp; + error = xfs_dialloc_roll(tpp, agbp); + if (error) { + xfs_buf_relse(agbp); + return error; + } + *inop = NULLFSINO; - return 0; + goto out_alloc; } nextag_relse_buffer: - xfs_trans_brelse(tp, agbp); + xfs_trans_brelse(*tpp, agbp); nextag: xfs_perag_put(pag); if (++agno == mp->m_sb.sb_agcount) @@ -1874,8 +1856,7 @@ nextag: } out_alloc: - *IO_agbp = NULL; - return xfs_dialloc_ag(tp, agbp, parent, inop); + return xfs_dialloc_ag(*tpp, agbp, parent, inop); out_error: xfs_perag_put(pag); return error; diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h index bd6e0db9e23c..13810ffe4af9 100644 --- a/fs/xfs/libxfs/xfs_ialloc.h +++ b/fs/xfs/libxfs/xfs_ialloc.h @@ -32,39 +32,20 @@ xfs_make_iptr(struct xfs_mount *mp, struct xfs_buf *b, int o) return xfs_buf_offset(b, o << (mp)->m_sb.sb_inodelog); } -int -xfs_dialloc_roll( - struct xfs_trans **tpp, - struct xfs_buf *agibp); - /* * Allocate an inode on disk. * Mode is used to tell whether the new inode will need space, and whether * it is a directory. * - * To work within the constraint of one allocation per transaction, - * xfs_dialloc() is designed to be called twice if it has to do an - * allocation to make more free inodes. If an inode is - * available without an allocation, agbp would be set to the current - * agbp and alloc_done set to false. - * If an allocation needed to be done, agbp would be set to the - * inode header of the allocation group and alloc_done set to true. - * The caller should then commit the current transaction and allocate a new - * transaction. xfs_dialloc() should then be called again with - * the agbp value returned from the previous call. - * * Once we successfully pick an inode its number is returned and the * on-disk data structures are updated. The inode itself is not read * in, since doing so would break ordering constraints with xfs_reclaim. - * - * *agbp should be set to NULL on the first call, *alloc_done set to FALSE. */ int /* error */ xfs_dialloc( - struct xfs_trans *tp, /* transaction pointer */ + struct xfs_trans **tpp, /* double pointer of transaction */ xfs_ino_t parent, /* parent inode (directory) */ umode_t mode, /* mode bits for new inode */ - struct xfs_buf **agbp, /* buf for a.g. inode header */ xfs_ino_t *inop); /* inode number allocated */ /* diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index bc1027ce0d1f..3c4e7edec1f6 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -909,7 +909,6 @@ xfs_dir_ialloc( prid_t prid, struct xfs_inode **ipp) { - struct xfs_buf *ialloc_context = NULL; xfs_ino_t parent_ino = dp ? dp->i_ino : 0; xfs_ino_t ino; int error; @@ -918,43 +917,12 @@ xfs_dir_ialloc( /* * Call the space management code to pick the on-disk inode to be - * allocated and replenish the freelist. Since we can only do one - * allocation per transaction without deadlocks, we will need to - * commit the current transaction and start a new one. - * If xfs_dialloc did an allocation to replenish the freelist, it - * returns the bp containing the head of the freelist as - * ialloc_context. We will hold a lock on it across the transaction - * commit so that no other process can steal the inode(s) that we've - * just allocated. - */ - error = xfs_dialloc(*tpp, parent_ino, mode, &ialloc_context, &ino); + * allocated. + */ + error = xfs_dialloc(tpp, parent_ino, mode, &ino); if (error) return error; - /* - * If the AGI buffer is non-NULL, then we were unable to get an - * inode in one operation. We need to commit the current - * transaction and call xfs_dialloc() again. It is guaranteed - * to succeed the second time. - */ - if (ialloc_context) { - error = xfs_dialloc_roll(tpp, ialloc_context); - if (error) { - xfs_buf_relse(ialloc_context); - return error; - } - /* - * Call dialloc again. Since we've locked out all other - * allocations in this allocation group, this call should - * always succeed. - */ - error = xfs_dialloc(*tpp, parent_ino, mode, &ialloc_context, - &ino); - if (error) - return error; - ASSERT(!ialloc_context); - } - if (ino == NULLFSINO) return -ENOSPC; -- cgit From 8d822dc38ad781b1bfa5c03227da80dbd87e9959 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 9 Dec 2020 10:05:16 -0800 Subject: xfs: spilt xfs_dialloc() into 2 functions This patch explicitly separates free inode chunk allocation and inode allocation into two individual high level operations. Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner Signed-off-by: Gao Xiang Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_ialloc.c | 54 +++++++++++++++++++++------------------------- fs/xfs/libxfs/xfs_ialloc.h | 20 ++++++++++++----- fs/xfs/xfs_inode.c | 11 ++++++++-- 3 files changed, 48 insertions(+), 37 deletions(-) (limited to 'fs/xfs/libxfs/xfs_ialloc.h') diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index cf1cc9b40e1b..4c45d0bb17ba 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -1570,7 +1570,7 @@ xfs_dialloc_ag_update_inobt( * The caller selected an AG for us, and made sure that free inodes are * available. */ -STATIC int +int xfs_dialloc_ag( struct xfs_trans *tp, struct xfs_buf *agbp, @@ -1718,21 +1718,22 @@ xfs_dialloc_roll( } /* - * Allocate an inode on disk. + * Select and prepare an AG for inode allocation. * - * Mode is used to tell whether the new inode will need space, and whether it - * is a directory. + * Mode is used to tell whether the new inode is a directory and hence where to + * locate it. * - * Once we successfully pick an inode its number is returned and the on-disk - * data structures are updated. The inode itself is not read in, since doing so - * would break ordering constraints with xfs_reclaim. + * This function will ensure that the selected AG has free inodes available to + * allocate from. The selected AGI will be returned locked to the caller, and it + * will allocate more free inodes if required. If no free inodes are found or + * can be allocated, no AGI will be returned. */ int -xfs_dialloc( +xfs_dialloc_select_ag( struct xfs_trans **tpp, xfs_ino_t parent, umode_t mode, - xfs_ino_t *inop) + struct xfs_buf **IO_agbp) { struct xfs_mount *mp = (*tpp)->t_mountp; struct xfs_buf *agbp; @@ -1745,15 +1746,15 @@ xfs_dialloc( struct xfs_ino_geometry *igeo = M_IGEO(mp); bool okalloc = true; + *IO_agbp = NULL; + /* * We do not have an agbp, so select an initial allocation * group for inode allocation. */ start_agno = xfs_ialloc_ag_select(*tpp, parent, mode); - if (start_agno == NULLAGNUMBER) { - *inop = NULLFSINO; + if (start_agno == NULLAGNUMBER) return 0; - } /* * If we have already hit the ceiling of inode blocks then clear @@ -1786,7 +1787,7 @@ xfs_dialloc( if (!pag->pagi_init) { error = xfs_ialloc_pagi_init(mp, *tpp, agno); if (error) - goto out_error; + break; } /* @@ -1801,11 +1802,11 @@ xfs_dialloc( */ error = xfs_ialloc_read_agi(mp, *tpp, agno, &agbp); if (error) - goto out_error; + break; if (pag->pagi_freecount) { xfs_perag_put(pag); - goto out_alloc; + goto found_ag; } if (!okalloc) @@ -1816,12 +1817,9 @@ xfs_dialloc( if (error) { xfs_trans_brelse(*tpp, agbp); - if (error != -ENOSPC) - goto out_error; - - xfs_perag_put(pag); - *inop = NULLFSINO; - return 0; + if (error == -ENOSPC) + error = 0; + break; } if (ialloced) { @@ -1838,9 +1836,7 @@ xfs_dialloc( xfs_buf_relse(agbp); return error; } - - *inop = NULLFSINO; - goto out_alloc; + goto found_ag; } nextag_relse_buffer: @@ -1849,17 +1845,15 @@ nextag: xfs_perag_put(pag); if (++agno == mp->m_sb.sb_agcount) agno = 0; - if (agno == start_agno) { - *inop = NULLFSINO; + if (agno == start_agno) return noroom ? -ENOSPC : 0; - } } -out_alloc: - return xfs_dialloc_ag(*tpp, agbp, parent, inop); -out_error: xfs_perag_put(pag); return error; +found_ag: + *IO_agbp = agbp; + return 0; } /* diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h index 13810ffe4af9..3511086a7ae1 100644 --- a/fs/xfs/libxfs/xfs_ialloc.h +++ b/fs/xfs/libxfs/xfs_ialloc.h @@ -37,16 +37,26 @@ xfs_make_iptr(struct xfs_mount *mp, struct xfs_buf *b, int o) * Mode is used to tell whether the new inode will need space, and whether * it is a directory. * - * Once we successfully pick an inode its number is returned and the - * on-disk data structures are updated. The inode itself is not read - * in, since doing so would break ordering constraints with xfs_reclaim. + * There are two phases to inode allocation: selecting an AG and ensuring + * that it contains free inodes, followed by allocating one of the free + * inodes. xfs_dialloc_select_ag() does the former and returns a locked AGI + * to the caller, ensuring that followup call to xfs_dialloc_ag() will + * have free inodes to allocate from. xfs_dialloc_ag() will return the inode + * number of the free inode we allocated. */ int /* error */ -xfs_dialloc( +xfs_dialloc_select_ag( struct xfs_trans **tpp, /* double pointer of transaction */ xfs_ino_t parent, /* parent inode (directory) */ umode_t mode, /* mode bits for new inode */ - xfs_ino_t *inop); /* inode number allocated */ + struct xfs_buf **IO_agbp); + +int +xfs_dialloc_ag( + struct xfs_trans *tp, + struct xfs_buf *agbp, + xfs_ino_t parent, + xfs_ino_t *inop); /* * Free disk inode. Carefully avoids touching the incore inode, all diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 3c4e7edec1f6..b7352bc4c815 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -909,6 +909,7 @@ xfs_dir_ialloc( prid_t prid, struct xfs_inode **ipp) { + struct xfs_buf *agibp; xfs_ino_t parent_ino = dp ? dp->i_ino : 0; xfs_ino_t ino; int error; @@ -919,13 +920,19 @@ xfs_dir_ialloc( * Call the space management code to pick the on-disk inode to be * allocated. */ - error = xfs_dialloc(tpp, parent_ino, mode, &ino); + error = xfs_dialloc_select_ag(tpp, parent_ino, mode, &agibp); if (error) return error; - if (ino == NULLFSINO) + if (!agibp) return -ENOSPC; + /* Allocate an inode from the selected AG */ + error = xfs_dialloc_ag(*tpp, agibp, parent_ino, &ino); + if (error) + return error; + ASSERT(ino != NULLFSINO); + return xfs_init_new_inode(*tpp, dp, ino, mode, nlink, rdev, prid, ipp); } -- cgit