Skip to content

Commit

Permalink
xfs: implement deferred bmbt map/unmap operations
Browse files Browse the repository at this point in the history
Implement deferred versions of the inode block map/unmap functions.
These will be used in subsequent patches to make reflink operations
atomic.

Signed-off-by: Darrick J. Wong <[email protected]>
Reviewed-by: Christoph Hellwig <[email protected]>
  • Loading branch information
djwong committed Oct 4, 2016
1 parent 4847acf commit 9f3afb5
Show file tree
Hide file tree
Showing 9 changed files with 393 additions and 3 deletions.
143 changes: 143 additions & 0 deletions fs/xfs/libxfs/xfs_bmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -6062,3 +6062,146 @@ xfs_bmap_split_extent(
xfs_trans_cancel(tp);
return error;
}

/* Deferred mapping is only for real extents in the data fork. */
static bool
xfs_bmap_is_update_needed(
struct xfs_bmbt_irec *bmap)
{
return bmap->br_startblock != HOLESTARTBLOCK &&
bmap->br_startblock != DELAYSTARTBLOCK;
}

/* Record a bmap intent. */
static int
__xfs_bmap_add(
struct xfs_mount *mp,
struct xfs_defer_ops *dfops,
enum xfs_bmap_intent_type type,
struct xfs_inode *ip,
int whichfork,
struct xfs_bmbt_irec *bmap)
{
int error;
struct xfs_bmap_intent *bi;

trace_xfs_bmap_defer(mp,
XFS_FSB_TO_AGNO(mp, bmap->br_startblock),
type,
XFS_FSB_TO_AGBNO(mp, bmap->br_startblock),
ip->i_ino, whichfork,
bmap->br_startoff,
bmap->br_blockcount,
bmap->br_state);

bi = kmem_alloc(sizeof(struct xfs_bmap_intent), KM_SLEEP | KM_NOFS);
INIT_LIST_HEAD(&bi->bi_list);
bi->bi_type = type;
bi->bi_owner = ip;
bi->bi_whichfork = whichfork;
bi->bi_bmap = *bmap;

error = xfs_defer_join(dfops, bi->bi_owner);
if (error) {
kmem_free(bi);
return error;
}

xfs_defer_add(dfops, XFS_DEFER_OPS_TYPE_BMAP, &bi->bi_list);
return 0;
}

/* Map an extent into a file. */
int
xfs_bmap_map_extent(
struct xfs_mount *mp,
struct xfs_defer_ops *dfops,
struct xfs_inode *ip,
struct xfs_bmbt_irec *PREV)
{
if (!xfs_bmap_is_update_needed(PREV))
return 0;

return __xfs_bmap_add(mp, dfops, XFS_BMAP_MAP, ip,
XFS_DATA_FORK, PREV);
}

/* Unmap an extent out of a file. */
int
xfs_bmap_unmap_extent(
struct xfs_mount *mp,
struct xfs_defer_ops *dfops,
struct xfs_inode *ip,
struct xfs_bmbt_irec *PREV)
{
if (!xfs_bmap_is_update_needed(PREV))
return 0;

return __xfs_bmap_add(mp, dfops, XFS_BMAP_UNMAP, ip,
XFS_DATA_FORK, PREV);
}

/*
* Process one of the deferred bmap operations. We pass back the
* btree cursor to maintain our lock on the bmapbt between calls.
*/
int
xfs_bmap_finish_one(
struct xfs_trans *tp,
struct xfs_defer_ops *dfops,
struct xfs_inode *ip,
enum xfs_bmap_intent_type type,
int whichfork,
xfs_fileoff_t startoff,
xfs_fsblock_t startblock,
xfs_filblks_t blockcount,
xfs_exntst_t state)
{
struct xfs_bmbt_irec bmap;
int nimaps = 1;
xfs_fsblock_t firstfsb;
int flags = XFS_BMAPI_REMAP;
int done;
int error = 0;

bmap.br_startblock = startblock;
bmap.br_startoff = startoff;
bmap.br_blockcount = blockcount;
bmap.br_state = state;

trace_xfs_bmap_deferred(tp->t_mountp,
XFS_FSB_TO_AGNO(tp->t_mountp, startblock), type,
XFS_FSB_TO_AGBNO(tp->t_mountp, startblock),
ip->i_ino, whichfork, startoff, blockcount, state);

if (whichfork != XFS_DATA_FORK && whichfork != XFS_ATTR_FORK)
return -EFSCORRUPTED;
if (whichfork == XFS_ATTR_FORK)
flags |= XFS_BMAPI_ATTRFORK;

if (XFS_TEST_ERROR(false, tp->t_mountp,
XFS_ERRTAG_BMAP_FINISH_ONE,
XFS_RANDOM_BMAP_FINISH_ONE))
return -EIO;

switch (type) {
case XFS_BMAP_MAP:
firstfsb = bmap.br_startblock;
error = xfs_bmapi_write(tp, ip, bmap.br_startoff,
bmap.br_blockcount, flags, &firstfsb,
bmap.br_blockcount, &bmap, &nimaps,
dfops);
break;
case XFS_BMAP_UNMAP:
error = xfs_bunmapi(tp, ip, bmap.br_startoff,
bmap.br_blockcount, flags, 1, &firstfsb,
dfops, &done);
ASSERT(done);
break;
default:
ASSERT(0);
error = -EFSCORRUPTED;
}

return error;
}
9 changes: 9 additions & 0 deletions fs/xfs/libxfs/xfs_bmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,13 @@ struct xfs_bmap_intent {
struct xfs_bmbt_irec bi_bmap;
};

int xfs_bmap_finish_one(struct xfs_trans *tp, struct xfs_defer_ops *dfops,
struct xfs_inode *ip, enum xfs_bmap_intent_type type,
int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
xfs_filblks_t blockcount, xfs_exntst_t state);
int xfs_bmap_map_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
struct xfs_inode *ip, struct xfs_bmbt_irec *imap);
int xfs_bmap_unmap_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
struct xfs_inode *ip, struct xfs_bmbt_irec *imap);

#endif /* __XFS_BMAP_H__ */
1 change: 1 addition & 0 deletions fs/xfs/libxfs/xfs_defer.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ struct xfs_defer_pending {
* find all the space it needs.
*/
enum xfs_defer_ops_type {
XFS_DEFER_OPS_TYPE_BMAP,
XFS_DEFER_OPS_TYPE_REFCOUNT,
XFS_DEFER_OPS_TYPE_RMAP,
XFS_DEFER_OPS_TYPE_FREE,
Expand Down
65 changes: 64 additions & 1 deletion fs/xfs/xfs_bmap_item.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,10 +389,19 @@ xfs_bui_recover(
struct xfs_bui_log_item *buip)
{
int error = 0;
unsigned int bui_type;
struct xfs_map_extent *bmap;
xfs_fsblock_t startblock_fsb;
xfs_fsblock_t inode_fsb;
bool op_ok;
struct xfs_bud_log_item *budp;
enum xfs_bmap_intent_type type;
int whichfork;
xfs_exntst_t state;
struct xfs_trans *tp;
struct xfs_inode *ip = NULL;
struct xfs_defer_ops dfops;
xfs_fsblock_t firstfsb;

ASSERT(!test_bit(XFS_BUI_RECOVERED, &buip->bui_flags));

Expand Down Expand Up @@ -437,7 +446,61 @@ xfs_bui_recover(
return -EIO;
}

error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
if (error)
return error;
budp = xfs_trans_get_bud(tp, buip);

/* Grab the inode. */
error = xfs_iget(mp, tp, bmap->me_owner, 0, XFS_ILOCK_EXCL, &ip);
if (error)
goto err_inode;

xfs_defer_init(&dfops, &firstfsb);

/* Process deferred bmap item. */
state = (bmap->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ?
XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
whichfork = (bmap->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ?
XFS_ATTR_FORK : XFS_DATA_FORK;
bui_type = bmap->me_flags & XFS_BMAP_EXTENT_TYPE_MASK;
switch (bui_type) {
case XFS_BMAP_MAP:
case XFS_BMAP_UNMAP:
type = bui_type;
break;
default:
error = -EFSCORRUPTED;
goto err_dfops;
}
xfs_trans_ijoin(tp, ip, 0);

error = xfs_trans_log_finish_bmap_update(tp, budp, &dfops, type,
ip, whichfork, bmap->me_startoff,
bmap->me_startblock, bmap->me_len,
state);
if (error)
goto err_dfops;

/* Finish transaction, free inodes. */
error = xfs_defer_finish(&tp, &dfops, NULL);
if (error)
goto err_dfops;

set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);
xfs_bui_release(buip);
error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
IRELE(ip);

return error;

err_dfops:
xfs_defer_cancel(&dfops);
err_inode:
xfs_trans_cancel(tp);
if (ip) {
xfs_iunlock(ip, XFS_ILOCK_EXCL);
IRELE(ip);
}
return error;
}
4 changes: 3 additions & 1 deletion fs/xfs/xfs_error.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ extern void xfs_verifier_error(struct xfs_buf *bp);
#define XFS_ERRTAG_RMAP_FINISH_ONE 23
#define XFS_ERRTAG_REFCOUNT_CONTINUE_UPDATE 24
#define XFS_ERRTAG_REFCOUNT_FINISH_ONE 25
#define XFS_ERRTAG_MAX 26
#define XFS_ERRTAG_BMAP_FINISH_ONE 26
#define XFS_ERRTAG_MAX 27

/*
* Random factors for above tags, 1 means always, 2 means 1/2 time, etc.
Expand Down Expand Up @@ -125,6 +126,7 @@ extern void xfs_verifier_error(struct xfs_buf *bp);
#define XFS_RANDOM_RMAP_FINISH_ONE 1
#define XFS_RANDOM_REFCOUNT_CONTINUE_UPDATE 1
#define XFS_RANDOM_REFCOUNT_FINISH_ONE 1
#define XFS_RANDOM_BMAP_FINISH_ONE 1

#ifdef DEBUG
extern int xfs_error_test_active;
Expand Down
1 change: 1 addition & 0 deletions fs/xfs/xfs_super.c
Original file line number Diff line number Diff line change
Expand Up @@ -1922,6 +1922,7 @@ init_xfs_fs(void)
xfs_extent_free_init_defer_op();
xfs_rmap_update_init_defer_op();
xfs_refcount_update_init_defer_op();
xfs_bmap_update_init_defer_op();

xfs_dir_startup();

Expand Down
5 changes: 5 additions & 0 deletions fs/xfs/xfs_trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -2587,6 +2587,11 @@ DEFINE_RMAPBT_EVENT(xfs_rmap_lookup_le_range_result);
DEFINE_RMAPBT_EVENT(xfs_rmap_find_right_neighbor_result);
DEFINE_RMAPBT_EVENT(xfs_rmap_find_left_neighbor_result);

/* deferred bmbt updates */
#define DEFINE_BMAP_DEFERRED_EVENT DEFINE_RMAP_DEFERRED_EVENT
DEFINE_BMAP_DEFERRED_EVENT(xfs_bmap_defer);
DEFINE_BMAP_DEFERRED_EVENT(xfs_bmap_deferred);

/* per-AG reservation */
DECLARE_EVENT_CLASS(xfs_ag_resv_class,
TP_PROTO(struct xfs_perag *pag, enum xfs_ag_resv_type resv,
Expand Down
1 change: 1 addition & 0 deletions fs/xfs/xfs_trans.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct xfs_cui_log_item;
struct xfs_cud_log_item;
struct xfs_defer_ops;
struct xfs_bui_log_item;
struct xfs_bud_log_item;

typedef struct xfs_log_item {
struct list_head li_ail; /* AIL pointers */
Expand Down
Loading

0 comments on commit 9f3afb5

Please sign in to comment.