diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_ialloc.c')
-rw-r--r-- | fs/xfs/libxfs/xfs_ialloc.c | 170 |
1 files changed, 88 insertions, 82 deletions
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index 974e71bc4a3a..69b228fce81a 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -607,13 +607,13 @@ error: /* * Allocate new inodes in the allocation group specified by agbp. - * Return 0 for success, else error code. + * Returns 0 if inodes were allocated in this AG; 1 if there was no space + * in this AG; or the usual negative error code. */ STATIC int xfs_ialloc_ag_alloc( struct xfs_trans *tp, - struct xfs_buf *agbp, - int *alloc) + struct xfs_buf *agbp) { struct xfs_agi *agi; struct xfs_alloc_arg args; @@ -795,10 +795,9 @@ sparse_alloc: allocmask = (1 << (newlen / XFS_INODES_PER_HOLEMASK_BIT)) - 1; } - if (args.fsbno == NULLFSBLOCK) { - *alloc = 0; - return 0; - } + if (args.fsbno == NULLFSBLOCK) + return 1; + ASSERT(args.len == args.minlen); /* @@ -903,7 +902,6 @@ sparse_alloc: */ xfs_trans_mod_sb(tp, XFS_TRANS_SB_ICOUNT, (long)newlen); xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, (long)newlen); - *alloc = 1; return 0; } @@ -1570,7 +1568,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, @@ -1682,65 +1680,78 @@ error_cur: return error; } +static int +xfs_dialloc_roll( + struct xfs_trans **tpp, + struct xfs_buf *agibp) +{ + struct xfs_trans *tp = *tpp; + struct xfs_dquot_acct *dqinfo; + int error; + + /* + * Hold to on to the agibp across the commit so no other allocation can + * come in and take the free inodes we just allocated for our caller. + */ + xfs_trans_bhold(tp, agibp); + + /* + * We want the quota changes to be associated with the next transaction, + * NOT this one. So, detach the dqinfo from this and attach it to the + * next transaction. + */ + dqinfo = tp->t_dqinfo; + tp->t_dqinfo = NULL; + + error = xfs_trans_roll(&tp); + + /* Re-attach the quota info that we detached from prev trx. */ + tp->t_dqinfo = dqinfo; + + *tpp = tp; + if (error) + return error; + xfs_trans_bjoin(tp, agibp); + return 0; +} + /* - * Allocate an inode on disk. - * - * Mode is used to tell whether the new inode will need space, and whether it - * is a directory. + * Select and prepare an AG for inode allocation. * - * 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. + * 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( - struct xfs_trans *tp, +xfs_dialloc_select_ag( + struct xfs_trans **tpp, xfs_ino_t parent, umode_t mode, - struct xfs_buf **IO_agbp, - xfs_ino_t *inop) + struct xfs_buf **IO_agbp) { - struct xfs_mount *mp = tp->t_mountp; + struct xfs_mount *mp = (*tpp)->t_mountp; struct xfs_buf *agbp; xfs_agnumber_t agno; int error; - int ialloced; - int noroom = 0; + bool noroom = false; xfs_agnumber_t start_agno; struct xfs_perag *pag; struct xfs_ino_geometry *igeo = M_IGEO(mp); - int okalloc = 1; + 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; - } + *IO_agbp = NULL; /* * We do not have an agbp, so select an initial allocation * group for inode allocation. */ - start_agno = xfs_ialloc_ag_select(tp, parent, mode); - if (start_agno == NULLAGNUMBER) { - *inop = NULLFSINO; + start_agno = xfs_ialloc_ag_select(*tpp, parent, mode); + if (start_agno == NULLAGNUMBER) return 0; - } /* * If we have already hit the ceiling of inode blocks then clear @@ -1753,8 +1764,8 @@ xfs_dialloc( if (igeo->maxicount && percpu_counter_read_positive(&mp->m_icount) + igeo->ialloc_inos > igeo->maxicount) { - noroom = 1; - okalloc = 0; + noroom = true; + okalloc = false; } /* @@ -1771,9 +1782,9 @@ 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; + break; } /* @@ -1786,64 +1797,59 @@ 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; + break; if (pag->pagi_freecount) { xfs_perag_put(pag); - goto out_alloc; + goto found_ag; } if (!okalloc) goto nextag_relse_buffer; + error = xfs_ialloc_ag_alloc(*tpp, agbp); + if (error < 0) { + xfs_trans_brelse(*tpp, agbp); - error = xfs_ialloc_ag_alloc(tp, agbp, &ialloced); - if (error) { - xfs_trans_brelse(tp, agbp); - - if (error != -ENOSPC) - goto out_error; - - xfs_perag_put(pag); - *inop = NULLFSINO; - return 0; + if (error == -ENOSPC) + error = 0; + break; } - if (ialloced) { + if (error == 0) { /* - * 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; - *inop = NULLFSINO; - return 0; + error = xfs_dialloc_roll(tpp, agbp); + if (error) { + xfs_buf_relse(agbp); + return error; + } + goto found_ag; } 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) agno = 0; - if (agno == start_agno) { - *inop = NULLFSINO; + if (agno == start_agno) return noroom ? -ENOSPC : 0; - } } -out_alloc: - *IO_agbp = NULL; - return xfs_dialloc_ag(tp, agbp, parent, inop); -out_error: xfs_perag_put(pag); return error; +found_ag: + *IO_agbp = agbp; + return 0; } /* @@ -2453,7 +2459,7 @@ out_map: void xfs_ialloc_log_agi( xfs_trans_t *tp, /* transaction pointer */ - xfs_buf_t *bp, /* allocation group header buffer */ + struct xfs_buf *bp, /* allocation group header buffer */ int fields) /* bitmask of fields to log */ { int first; /* first byte number */ @@ -2674,7 +2680,7 @@ xfs_ialloc_pagi_init( xfs_trans_t *tp, /* transaction pointer */ xfs_agnumber_t agno) /* allocation group number */ { - xfs_buf_t *bp = NULL; + struct xfs_buf *bp = NULL; int error; error = xfs_ialloc_read_agi(mp, tp, agno, &bp); |