xfs: connect refcount adjust functions to upper layers

Plumb in the upper level interface to schedule and finish deferred
refcount operations via the deferred ops mechanism.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Darrick J. Wong
2016-10-03 09:11:22 -07:00
parent 3172725814
commit 33ba612920
9 changed files with 472 additions and 6 deletions

View File

@@ -51,6 +51,7 @@ struct xfs_defer_pending {
* find all the space it needs.
*/
enum xfs_defer_ops_type {
XFS_DEFER_OPS_TYPE_REFCOUNT,
XFS_DEFER_OPS_TYPE_RMAP,
XFS_DEFER_OPS_TYPE_FREE,
XFS_DEFER_OPS_TYPE_MAX,

View File

@@ -989,3 +989,177 @@ out_error:
error, _RET_IP_);
return error;
}
/* Clean up after calling xfs_refcount_finish_one. */
void
xfs_refcount_finish_one_cleanup(
struct xfs_trans *tp,
struct xfs_btree_cur *rcur,
int error)
{
struct xfs_buf *agbp;
if (rcur == NULL)
return;
agbp = rcur->bc_private.a.agbp;
xfs_btree_del_cursor(rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
if (error)
xfs_trans_brelse(tp, agbp);
}
/*
* Process one of the deferred refcount operations. We pass back the
* btree cursor to maintain our lock on the btree between calls.
* This saves time and eliminates a buffer deadlock between the
* superblock and the AGF because we'll always grab them in the same
* order.
*/
int
xfs_refcount_finish_one(
struct xfs_trans *tp,
struct xfs_defer_ops *dfops,
enum xfs_refcount_intent_type type,
xfs_fsblock_t startblock,
xfs_extlen_t blockcount,
xfs_fsblock_t *new_fsb,
xfs_extlen_t *new_len,
struct xfs_btree_cur **pcur)
{
struct xfs_mount *mp = tp->t_mountp;
struct xfs_btree_cur *rcur;
struct xfs_buf *agbp = NULL;
int error = 0;
xfs_agnumber_t agno;
xfs_agblock_t bno;
xfs_agblock_t new_agbno;
unsigned long nr_ops = 0;
int shape_changes = 0;
agno = XFS_FSB_TO_AGNO(mp, startblock);
ASSERT(agno != NULLAGNUMBER);
bno = XFS_FSB_TO_AGBNO(mp, startblock);
trace_xfs_refcount_deferred(mp, XFS_FSB_TO_AGNO(mp, startblock),
type, XFS_FSB_TO_AGBNO(mp, startblock),
blockcount);
if (XFS_TEST_ERROR(false, mp,
XFS_ERRTAG_REFCOUNT_FINISH_ONE,
XFS_RANDOM_REFCOUNT_FINISH_ONE))
return -EIO;
/*
* If we haven't gotten a cursor or the cursor AG doesn't match
* the startblock, get one now.
*/
rcur = *pcur;
if (rcur != NULL && rcur->bc_private.a.agno != agno) {
nr_ops = rcur->bc_private.a.priv.refc.nr_ops;
shape_changes = rcur->bc_private.a.priv.refc.shape_changes;
xfs_refcount_finish_one_cleanup(tp, rcur, 0);
rcur = NULL;
*pcur = NULL;
}
if (rcur == NULL) {
error = xfs_alloc_read_agf(tp->t_mountp, tp, agno,
XFS_ALLOC_FLAG_FREEING, &agbp);
if (error)
return error;
if (!agbp)
return -EFSCORRUPTED;
rcur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, dfops);
if (!rcur) {
error = -ENOMEM;
goto out_cur;
}
rcur->bc_private.a.priv.refc.nr_ops = nr_ops;
rcur->bc_private.a.priv.refc.shape_changes = shape_changes;
}
*pcur = rcur;
switch (type) {
case XFS_REFCOUNT_INCREASE:
error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno,
new_len, XFS_REFCOUNT_ADJUST_INCREASE, dfops, NULL);
*new_fsb = XFS_AGB_TO_FSB(mp, agno, new_agbno);
break;
case XFS_REFCOUNT_DECREASE:
error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno,
new_len, XFS_REFCOUNT_ADJUST_DECREASE, dfops, NULL);
*new_fsb = XFS_AGB_TO_FSB(mp, agno, new_agbno);
break;
default:
ASSERT(0);
error = -EFSCORRUPTED;
}
if (!error && *new_len > 0)
trace_xfs_refcount_finish_one_leftover(mp, agno, type,
bno, blockcount, new_agbno, *new_len);
return error;
out_cur:
xfs_trans_brelse(tp, agbp);
return error;
}
/*
* Record a refcount intent for later processing.
*/
static int
__xfs_refcount_add(
struct xfs_mount *mp,
struct xfs_defer_ops *dfops,
enum xfs_refcount_intent_type type,
xfs_fsblock_t startblock,
xfs_extlen_t blockcount)
{
struct xfs_refcount_intent *ri;
trace_xfs_refcount_defer(mp, XFS_FSB_TO_AGNO(mp, startblock),
type, XFS_FSB_TO_AGBNO(mp, startblock),
blockcount);
ri = kmem_alloc(sizeof(struct xfs_refcount_intent),
KM_SLEEP | KM_NOFS);
INIT_LIST_HEAD(&ri->ri_list);
ri->ri_type = type;
ri->ri_startblock = startblock;
ri->ri_blockcount = blockcount;
xfs_defer_add(dfops, XFS_DEFER_OPS_TYPE_REFCOUNT, &ri->ri_list);
return 0;
}
/*
* Increase the reference count of the blocks backing a file's extent.
*/
int
xfs_refcount_increase_extent(
struct xfs_mount *mp,
struct xfs_defer_ops *dfops,
struct xfs_bmbt_irec *PREV)
{
if (!xfs_sb_version_hasreflink(&mp->m_sb))
return 0;
return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_INCREASE,
PREV->br_startblock, PREV->br_blockcount);
}
/*
* Decrease the reference count of the blocks backing a file's extent.
*/
int
xfs_refcount_decrease_extent(
struct xfs_mount *mp,
struct xfs_defer_ops *dfops,
struct xfs_bmbt_irec *PREV)
{
if (!xfs_sb_version_hasreflink(&mp->m_sb))
return 0;
return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_DECREASE,
PREV->br_startblock, PREV->br_blockcount);
}

View File

@@ -41,4 +41,17 @@ struct xfs_refcount_intent {
xfs_extlen_t ri_blockcount;
};
extern int xfs_refcount_increase_extent(struct xfs_mount *mp,
struct xfs_defer_ops *dfops, struct xfs_bmbt_irec *irec);
extern int xfs_refcount_decrease_extent(struct xfs_mount *mp,
struct xfs_defer_ops *dfops, struct xfs_bmbt_irec *irec);
extern void xfs_refcount_finish_one_cleanup(struct xfs_trans *tp,
struct xfs_btree_cur *rcur, int error);
extern int xfs_refcount_finish_one(struct xfs_trans *tp,
struct xfs_defer_ops *dfops, enum xfs_refcount_intent_type type,
xfs_fsblock_t startblock, xfs_extlen_t blockcount,
xfs_fsblock_t *new_fsb, xfs_extlen_t *new_len,
struct xfs_btree_cur **pcur);
#endif /* __XFS_REFCOUNT_H__ */