Skip to content

Commit

Permalink
Merge tag 'xfs-4.15-fixes-8' of git://git.kernel.org/pub/scm/fs/xfs/x…
Browse files Browse the repository at this point in the history
…fs-linux

Pull xfs fixes from Darrick Wong:
 "Here are some XFS fixes for 4.15-rc5. Apologies for the unusually
  large number of patches this late, but I wanted to make sure the
  corruption fixes were really ready to go.

  Changes since last update:

   - Fix a locking problem during xattr block conversion that could lead
     to the log checkpointing thread to try to write an incomplete
     buffer to disk, which leads to a corruption shutdown

   - Fix a null pointer dereference when removing delayed allocation
     extents

   - Remove post-eof speculative allocations when reflinking a block
     past current inode size so that we don't just leave them there and
     assert on inode reclaim

   - Relax an assert which didn't accurately reflect the way locking
     works and would trigger under heavy io load

   - Avoid infinite loop when cancelling copy on write extents after a
     writeback failure

   - Try to avoid copy on write transaction reservation overflows when
     remapping after a successful write

   - Fix various problems with the copy-on-write reservation automatic
     garbage collection not being cleaned up properly during a ro
     remount

   - Fix problems with rmap log items being processed in the wrong
     order, leading to corruption shutdowns

   - Fix problems with EFI recovery wherein the "remove any rmapping if
     present" mechanism wasn't actually doing anything, which would lead
     to corruption problems later when the extent is reallocated,
     leading to multiple rmaps for the same extent"

* tag 'xfs-4.15-fixes-8' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: only skip rmap owner checks for unknown-owner rmap removal
  xfs: always honor OWN_UNKNOWN rmap removal requests
  xfs: queue deferred rmap ops for cow staging extent alloc/free in the right order
  xfs: set cowblocks tag for direct cow writes too
  xfs: remove leftover CoW reservations when remounting ro
  xfs: don't be so eager to clear the cowblocks tag on truncate
  xfs: track cowblocks separately in i_flags
  xfs: allow CoW remap transactions to use reserve blocks
  xfs: avoid infinite loop when cancelling CoW blocks after writeback failure
  xfs: relax is_reflink_inode assert in xfs_reflink_find_cow_mapping
  xfs: remove dest file's post-eof preallocations before reflinking
  xfs: move xfs_iext_insert tracepoint to report useful information
  xfs: account for null transactions in bunmapi
  xfs: hold xfs_buf locked between shortform->leaf conversion and the addition of an attribute
  xfs: add the ability to join a held buffer to a defer_ops
  • Loading branch information
Linus Torvalds committed Dec 22, 2017
2 parents 0fc0f18 + 68c58e9 commit fca0e39
Show file tree
Hide file tree
Showing 19 changed files with 258 additions and 97 deletions.
4 changes: 2 additions & 2 deletions fs/xfs/libxfs/xfs_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ xfs_alloc_ag_vextent(
ASSERT(args->agbno % args->alignment == 0);

/* if not file data, insert new block into the reverse map btree */
if (args->oinfo.oi_owner != XFS_RMAP_OWN_UNKNOWN) {
if (!xfs_rmap_should_skip_owner_update(&args->oinfo)) {
error = xfs_rmap_alloc(args->tp, args->agbp, args->agno,
args->agbno, args->len, &args->oinfo);
if (error)
Expand Down Expand Up @@ -1682,7 +1682,7 @@ xfs_free_ag_extent(
bno_cur = cnt_cur = NULL;
mp = tp->t_mountp;

if (oinfo->oi_owner != XFS_RMAP_OWN_UNKNOWN) {
if (!xfs_rmap_should_skip_owner_update(oinfo)) {
error = xfs_rmap_free(tp, agbp, agno, bno, len, oinfo);
if (error)
goto error0;
Expand Down
20 changes: 15 additions & 5 deletions fs/xfs/libxfs/xfs_attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ xfs_attr_set(
int flags)
{
struct xfs_mount *mp = dp->i_mount;
struct xfs_buf *leaf_bp = NULL;
struct xfs_da_args args;
struct xfs_defer_ops dfops;
struct xfs_trans_res tres;
Expand Down Expand Up @@ -327,23 +328,31 @@ xfs_attr_set(
* GROT: another possible req'mt for a double-split btree op.
*/
xfs_defer_init(args.dfops, args.firstblock);
error = xfs_attr_shortform_to_leaf(&args);
error = xfs_attr_shortform_to_leaf(&args, &leaf_bp);
if (error)
goto out_defer_cancel;
/*
* Prevent the leaf buffer from being unlocked so that a
* concurrent AIL push cannot grab the half-baked leaf
* buffer and run into problems with the write verifier.
*/
xfs_trans_bhold(args.trans, leaf_bp);
xfs_defer_bjoin(args.dfops, leaf_bp);
xfs_defer_ijoin(args.dfops, dp);
error = xfs_defer_finish(&args.trans, args.dfops);
if (error)
goto out_defer_cancel;

/*
* Commit the leaf transformation. We'll need another (linked)
* transaction to add the new attribute to the leaf.
* transaction to add the new attribute to the leaf, which
* means that we have to hold & join the leaf buffer here too.
*/

error = xfs_trans_roll_inode(&args.trans, dp);
if (error)
goto out;

xfs_trans_bjoin(args.trans, leaf_bp);
leaf_bp = NULL;
}

if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
Expand Down Expand Up @@ -374,8 +383,9 @@ xfs_attr_set(

out_defer_cancel:
xfs_defer_cancel(&dfops);
args.trans = NULL;
out:
if (leaf_bp)
xfs_trans_brelse(args.trans, leaf_bp);
if (args.trans)
xfs_trans_cancel(args.trans);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
Expand Down
9 changes: 6 additions & 3 deletions fs/xfs/libxfs/xfs_attr_leaf.c
Original file line number Diff line number Diff line change
Expand Up @@ -735,10 +735,13 @@ xfs_attr_shortform_getvalue(xfs_da_args_t *args)
}

/*
* Convert from using the shortform to the leaf.
* Convert from using the shortform to the leaf. On success, return the
* buffer so that we can keep it locked until we're totally done with it.
*/
int
xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
xfs_attr_shortform_to_leaf(
struct xfs_da_args *args,
struct xfs_buf **leaf_bp)
{
xfs_inode_t *dp;
xfs_attr_shortform_t *sf;
Expand Down Expand Up @@ -818,7 +821,7 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
}
error = 0;

*leaf_bp = bp;
out:
kmem_free(tmpbuffer);
return error;
Expand Down
3 changes: 2 additions & 1 deletion fs/xfs/libxfs/xfs_attr_leaf.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ void xfs_attr_shortform_create(struct xfs_da_args *args);
void xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff);
int xfs_attr_shortform_lookup(struct xfs_da_args *args);
int xfs_attr_shortform_getvalue(struct xfs_da_args *args);
int xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
int xfs_attr_shortform_to_leaf(struct xfs_da_args *args,
struct xfs_buf **leaf_bp);
int xfs_attr_shortform_remove(struct xfs_da_args *args);
int xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
int xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes);
Expand Down
2 changes: 1 addition & 1 deletion fs/xfs/libxfs/xfs_bmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -5136,7 +5136,7 @@ __xfs_bunmapi(
* blowing out the transaction with a mix of EFIs and reflink
* adjustments.
*/
if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK)
if (tp && xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK)
max_len = min(len, xfs_refcount_max_unmap(tp->t_log_res));
else
max_len = len;
Expand Down
39 changes: 36 additions & 3 deletions fs/xfs/libxfs/xfs_defer.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ xfs_defer_trans_roll(
for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)
xfs_trans_log_inode(*tp, dop->dop_inodes[i], XFS_ILOG_CORE);

/* Hold the (previously bjoin'd) buffer locked across the roll. */
for (i = 0; i < XFS_DEFER_OPS_NR_BUFS && dop->dop_bufs[i]; i++)
xfs_trans_dirty_buf(*tp, dop->dop_bufs[i]);

trace_xfs_defer_trans_roll((*tp)->t_mountp, dop);

/* Roll the transaction. */
Expand All @@ -264,6 +268,12 @@ xfs_defer_trans_roll(
for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)
xfs_trans_ijoin(*tp, dop->dop_inodes[i], 0);

/* Rejoin the buffers and dirty them so the log moves forward. */
for (i = 0; i < XFS_DEFER_OPS_NR_BUFS && dop->dop_bufs[i]; i++) {
xfs_trans_bjoin(*tp, dop->dop_bufs[i]);
xfs_trans_bhold(*tp, dop->dop_bufs[i]);
}

return error;
}

Expand Down Expand Up @@ -295,6 +305,31 @@ xfs_defer_ijoin(
}
}

ASSERT(0);
return -EFSCORRUPTED;
}

/*
* Add this buffer to the deferred op. Each joined buffer is relogged
* each time we roll the transaction.
*/
int
xfs_defer_bjoin(
struct xfs_defer_ops *dop,
struct xfs_buf *bp)
{
int i;

for (i = 0; i < XFS_DEFER_OPS_NR_BUFS; i++) {
if (dop->dop_bufs[i] == bp)
return 0;
else if (dop->dop_bufs[i] == NULL) {
dop->dop_bufs[i] = bp;
return 0;
}
}

ASSERT(0);
return -EFSCORRUPTED;
}

Expand Down Expand Up @@ -493,9 +528,7 @@ xfs_defer_init(
struct xfs_defer_ops *dop,
xfs_fsblock_t *fbp)
{
dop->dop_committed = false;
dop->dop_low = false;
memset(&dop->dop_inodes, 0, sizeof(dop->dop_inodes));
memset(dop, 0, sizeof(struct xfs_defer_ops));
*fbp = NULLFSBLOCK;
INIT_LIST_HEAD(&dop->dop_intake);
INIT_LIST_HEAD(&dop->dop_pending);
Expand Down
5 changes: 4 additions & 1 deletion fs/xfs/libxfs/xfs_defer.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,17 @@ enum xfs_defer_ops_type {
};

#define XFS_DEFER_OPS_NR_INODES 2 /* join up to two inodes */
#define XFS_DEFER_OPS_NR_BUFS 2 /* join up to two buffers */

struct xfs_defer_ops {
bool dop_committed; /* did any trans commit? */
bool dop_low; /* alloc in low mode */
struct list_head dop_intake; /* unlogged pending work */
struct list_head dop_pending; /* logged pending work */

/* relog these inodes with each roll */
/* relog these with each roll */
struct xfs_inode *dop_inodes[XFS_DEFER_OPS_NR_INODES];
struct xfs_buf *dop_bufs[XFS_DEFER_OPS_NR_BUFS];
};

void xfs_defer_add(struct xfs_defer_ops *dop, enum xfs_defer_ops_type type,
Expand All @@ -77,6 +79,7 @@ void xfs_defer_cancel(struct xfs_defer_ops *dop);
void xfs_defer_init(struct xfs_defer_ops *dop, xfs_fsblock_t *fbp);
bool xfs_defer_has_unfinished_work(struct xfs_defer_ops *dop);
int xfs_defer_ijoin(struct xfs_defer_ops *dop, struct xfs_inode *ip);
int xfs_defer_bjoin(struct xfs_defer_ops *dop, struct xfs_buf *bp);

/* Description of a deferred type. */
struct xfs_defer_op_type {
Expand Down
4 changes: 2 additions & 2 deletions fs/xfs/libxfs/xfs_iext_tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -632,8 +632,6 @@ xfs_iext_insert(
struct xfs_iext_leaf *new = NULL;
int nr_entries, i;

trace_xfs_iext_insert(ip, cur, state, _RET_IP_);

if (ifp->if_height == 0)
xfs_iext_alloc_root(ifp, cur);
else if (ifp->if_height == 1)
Expand Down Expand Up @@ -661,6 +659,8 @@ xfs_iext_insert(
xfs_iext_set(cur_rec(cur), irec);
ifp->if_bytes += sizeof(struct xfs_iext_rec);

trace_xfs_iext_insert(ip, cur, state, _RET_IP_);

if (new)
xfs_iext_insert_node(ifp, xfs_iext_leaf_key(new, 0), new, 2);
}
Expand Down
52 changes: 19 additions & 33 deletions fs/xfs/libxfs/xfs_refcount.c
Original file line number Diff line number Diff line change
Expand Up @@ -1488,27 +1488,12 @@ __xfs_refcount_cow_alloc(
xfs_extlen_t aglen,
struct xfs_defer_ops *dfops)
{
int error;

trace_xfs_refcount_cow_increase(rcur->bc_mp, rcur->bc_private.a.agno,
agbno, aglen);

/* Add refcount btree reservation */
error = xfs_refcount_adjust_cow(rcur, agbno, aglen,
return xfs_refcount_adjust_cow(rcur, agbno, aglen,
XFS_REFCOUNT_ADJUST_COW_ALLOC, dfops);
if (error)
return error;

/* Add rmap entry */
if (xfs_sb_version_hasrmapbt(&rcur->bc_mp->m_sb)) {
error = xfs_rmap_alloc_extent(rcur->bc_mp, dfops,
rcur->bc_private.a.agno,
agbno, aglen, XFS_RMAP_OWN_COW);
if (error)
return error;
}

return error;
}

/*
Expand All @@ -1521,27 +1506,12 @@ __xfs_refcount_cow_free(
xfs_extlen_t aglen,
struct xfs_defer_ops *dfops)
{
int error;

trace_xfs_refcount_cow_decrease(rcur->bc_mp, rcur->bc_private.a.agno,
agbno, aglen);

/* Remove refcount btree reservation */
error = xfs_refcount_adjust_cow(rcur, agbno, aglen,
return xfs_refcount_adjust_cow(rcur, agbno, aglen,
XFS_REFCOUNT_ADJUST_COW_FREE, dfops);
if (error)
return error;

/* Remove rmap entry */
if (xfs_sb_version_hasrmapbt(&rcur->bc_mp->m_sb)) {
error = xfs_rmap_free_extent(rcur->bc_mp, dfops,
rcur->bc_private.a.agno,
agbno, aglen, XFS_RMAP_OWN_COW);
if (error)
return error;
}

return error;
}

/* Record a CoW staging extent in the refcount btree. */
Expand All @@ -1552,11 +1522,19 @@ xfs_refcount_alloc_cow_extent(
xfs_fsblock_t fsb,
xfs_extlen_t len)
{
int error;

if (!xfs_sb_version_hasreflink(&mp->m_sb))
return 0;

return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_ALLOC_COW,
error = __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_ALLOC_COW,
fsb, len);
if (error)
return error;

/* Add rmap entry */
return xfs_rmap_alloc_extent(mp, dfops, XFS_FSB_TO_AGNO(mp, fsb),
XFS_FSB_TO_AGBNO(mp, fsb), len, XFS_RMAP_OWN_COW);
}

/* Forget a CoW staging event in the refcount btree. */
Expand All @@ -1567,9 +1545,17 @@ xfs_refcount_free_cow_extent(
xfs_fsblock_t fsb,
xfs_extlen_t len)
{
int error;

if (!xfs_sb_version_hasreflink(&mp->m_sb))
return 0;

/* Remove rmap entry */
error = xfs_rmap_free_extent(mp, dfops, XFS_FSB_TO_AGNO(mp, fsb),
XFS_FSB_TO_AGBNO(mp, fsb), len, XFS_RMAP_OWN_COW);
if (error)
return error;

return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_FREE_COW,
fsb, len);
}
Expand Down
Loading

0 comments on commit fca0e39

Please sign in to comment.