mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:19:30 +02:00
Merge tag 'xfs-fixes-7.3-rc3' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
Pull xfs fixes from Carlos Maiolino: "More than the usual amount of fixes. The highlights here are a block under reservation fix which caused an assert to be triggered in non-default configurations. The assert, initially added on 7.3-rc2 just makes the problem explicit but is not the cause. Another highlight is a missed lock/unlock mutex in the xfs healthmonitor which was causing lockdeps warnings. Besides those two, this also contains a myriad of fixes for random bugs found by LLM tools in the healthmon, scrub and online repair. A few bug fixes for zoned xfs are also included. This also includes an accounting fix for our buffer slab cache where the memory payload associated to each object was not being properly accounted for. The remaining of the patches are a few lock context annotations added and/or fixed. They are mostly disabled by now, but still worth fixing before we get them enabled. And last but not least, a few clean ups" * tag 'xfs-fixes-7.3-rc3' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: (75 commits) xfs: advance the findparent inode scan cursor while holding ILOCK xfs: reset parent pointer args before each dir tree unlink repair xfs: fix replaying dirent removals into the temporary directory xfs: fix termination logic in xchk_bmap xfs: fix rtrmap cross-referencing elision logic xfs: actually check internal-rtdev fields in the superblock xfs: fix under-reservation of blocks when repairing sf directories xfs: take hm->lock in xfs_ioc_health_monitor() before insert xfs: set IOMAP_F_INTEGRITY for zoned writes on integrity devices xfs: avoid extra cache flushes for multi-device file systems in xfs_fsync xfs: don't continue on error in xfs_fsync xfs: also flush the RT device cache in xlog_write_iclog xfs: bail out on bitmap errors in xrep_agfl_fill xfs: snapshot old AGFL before rewriting it xfs: remove redundant function declaration xfs: report runtime failures in scrub xfs: report healthy filesystem events in scrub stats xfs: snapshot scrub stats when rendering them xfs: remove several unused and never-implemented declarations xfs: count escaped corruption errors in scrub stats ...
This commit is contained in:
@@ -117,6 +117,7 @@ xfbtree_init(
|
||||
struct xfs_buftarg *btp,
|
||||
const struct xfs_btree_ops *ops)
|
||||
{
|
||||
unsigned long long owner = xfbt->owner;
|
||||
unsigned int blocklen = xfbtree_rec_bytes(mp, ops);
|
||||
unsigned int keyptr_len;
|
||||
int error;
|
||||
@@ -133,6 +134,7 @@ xfbtree_init(
|
||||
|
||||
memset(xfbt, 0, sizeof(*xfbt));
|
||||
xfbt->target = btp;
|
||||
xfbt->owner = owner;
|
||||
|
||||
/* Set up min/maxrecs for this btree. */
|
||||
keyptr_len = ops->key_len + sizeof(__be64);
|
||||
|
||||
@@ -336,8 +336,10 @@ xfs_btree_bload_prep_block(
|
||||
xfs_btree_set_sibling(cur, *blockp, &new_ptr, XFS_BB_RIGHTSIB);
|
||||
|
||||
ret = xfs_btree_bload_drop_buf(bbl, buffers_list, bpp);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
xfs_buf_relse(new_bp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize the new btree block. */
|
||||
xfs_btree_init_block_cur(cur, new_bp, level, nr_this_block);
|
||||
|
||||
@@ -130,7 +130,7 @@ xfs_da_state_reset(
|
||||
state->mp = state->args->dp->i_mount;
|
||||
}
|
||||
|
||||
static inline int xfs_dabuf_nfsb(struct xfs_mount *mp, int whichfork)
|
||||
inline int xfs_dabuf_nfsb(struct xfs_mount *mp, int whichfork)
|
||||
{
|
||||
if (whichfork == XFS_DATA_FORK)
|
||||
return mp->m_dir_geo->fsbcount;
|
||||
@@ -2384,6 +2384,7 @@ xfs_da_grow_inode_int(
|
||||
}
|
||||
|
||||
/* account for newly allocated blocks in reserved blocks total */
|
||||
ASSERT(args->total >= dp->i_nblocks - nblks);
|
||||
args->total -= dp->i_nblocks - nblks;
|
||||
|
||||
out_free_map:
|
||||
|
||||
@@ -244,4 +244,6 @@ xfs_failaddr_t xfs_da3_node_header_check(struct xfs_buf *bp, xfs_ino_t owner);
|
||||
|
||||
extern struct kmem_cache *xfs_da_state_cache;
|
||||
|
||||
int xfs_dabuf_nfsb(struct xfs_mount *mp, int whichfork);
|
||||
|
||||
#endif /* __XFS_DA_BTREE_H__ */
|
||||
|
||||
@@ -229,6 +229,7 @@ xfs_defer_barrier_cancel_item(
|
||||
}
|
||||
|
||||
static const struct xfs_defer_op_type xfs_barrier_defer_type = {
|
||||
.name = "barrier",
|
||||
.max_items = 1,
|
||||
.create_intent = xfs_defer_barrier_create_intent,
|
||||
.abort_intent = xfs_defer_barrier_abort_intent,
|
||||
@@ -583,7 +584,7 @@ xfs_defer_finish_one(
|
||||
const struct xfs_defer_op_type *ops = dfp->dfp_ops;
|
||||
struct xfs_btree_cur *state = NULL;
|
||||
struct list_head *li, *n;
|
||||
int error;
|
||||
int error = 0;
|
||||
|
||||
trace_xfs_defer_pending_finish(tp->t_mountp, dfp);
|
||||
|
||||
@@ -655,6 +656,7 @@ xfs_defer_finish_noroll(
|
||||
struct xfs_trans **tp)
|
||||
{
|
||||
struct xfs_defer_pending *dfp = NULL;
|
||||
const char *what = "chain";
|
||||
int error = 0;
|
||||
LIST_HEAD(dop_pending);
|
||||
LIST_HEAD(dop_paused);
|
||||
@@ -704,9 +706,17 @@ xfs_defer_finish_noroll(
|
||||
struct xfs_defer_pending, dfp_list);
|
||||
if (!dfp)
|
||||
break;
|
||||
what = dfp->dfp_ops->name;
|
||||
error = xfs_defer_finish_one(*tp, dfp);
|
||||
if (error && error != -EAGAIN)
|
||||
goto out_shutdown;
|
||||
/*
|
||||
* A finished item is no longer a candidate for a later
|
||||
* failure. An -EAGAIN one is not finished, so it keeps the
|
||||
* attribution across the roll that completes it.
|
||||
*/
|
||||
if (!error)
|
||||
what = "chain";
|
||||
}
|
||||
|
||||
/* Requeue the paused items in the outgoing transaction. */
|
||||
@@ -718,8 +728,12 @@ xfs_defer_finish_noroll(
|
||||
out_shutdown:
|
||||
list_splice_tail_init(&dop_paused, &dop_pending);
|
||||
xfs_defer_trans_abort(*tp, &dop_pending);
|
||||
xfs_force_shutdown((*tp)->t_mountp, SHUTDOWN_CORRUPT_INCORE);
|
||||
trace_xfs_defer_finish_error(*tp, error);
|
||||
if (!xfs_is_shutdown((*tp)->t_mountp))
|
||||
xfs_alert((*tp)->t_mountp,
|
||||
"deferred %s work failed, error %d, %u blocks reserved",
|
||||
what, error, (*tp)->t_blk_res);
|
||||
xfs_force_shutdown((*tp)->t_mountp, SHUTDOWN_CORRUPT_INCORE);
|
||||
xfs_defer_cancel_list((*tp)->t_mountp, &dop_pending);
|
||||
xfs_defer_cancel(*tp);
|
||||
return error;
|
||||
|
||||
@@ -959,6 +959,16 @@ xmi_can_exchange_reflink_flags(
|
||||
{
|
||||
struct xfs_mount *mp = req->ip1->i_mount;
|
||||
|
||||
/*
|
||||
* The INO1_WRITTEN optimization can skip exchanging hole and
|
||||
* unwritten mappings, which means we cannot guarantee that all
|
||||
* shared extents actually moved to the other file. Clearing the
|
||||
* reflink flag of an inode that still holds shared extents breaks
|
||||
* the CoW write path, so refuse to exchange the flags in that case.
|
||||
*/
|
||||
if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* The INO1_WRITTEN optimization can skip exchanging hole and
|
||||
* unwritten mappings, which means we cannot guarantee that all
|
||||
|
||||
@@ -193,7 +193,7 @@ xfs_parent_addname(
|
||||
const struct xfs_name *parent_name,
|
||||
struct xfs_inode *child)
|
||||
{
|
||||
int error;
|
||||
int error, local;
|
||||
|
||||
error = xfs_parent_iread_extents(tp, child);
|
||||
if (error)
|
||||
@@ -203,6 +203,10 @@ xfs_parent_addname(
|
||||
xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
|
||||
I_INO(child), parent_name);
|
||||
|
||||
/* Growing the attr fork needs a real reservation in args->total. */
|
||||
ppargs->args.total = xfs_attr_calc_size(&ppargs->args, &local);
|
||||
ASSERT(local);
|
||||
|
||||
return xfs_attr_setname(&ppargs->args, 0);
|
||||
}
|
||||
|
||||
@@ -239,7 +243,7 @@ xfs_parent_replacename(
|
||||
const struct xfs_name *new_name,
|
||||
struct xfs_inode *child)
|
||||
{
|
||||
int error;
|
||||
int error, local;
|
||||
|
||||
error = xfs_parent_iread_extents(tp, child);
|
||||
if (error)
|
||||
@@ -249,6 +253,10 @@ xfs_parent_replacename(
|
||||
xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
|
||||
I_INO(child), old_name);
|
||||
|
||||
/* Growing the attr fork needs a real reservation in args->total. */
|
||||
ppargs->args.total = xfs_attr_calc_size(&ppargs->args, &local);
|
||||
ASSERT(local);
|
||||
|
||||
xfs_inode_to_parent_rec(&ppargs->new_rec, new_dp);
|
||||
|
||||
ppargs->args.new_name = new_name->name;
|
||||
|
||||
@@ -489,8 +489,11 @@ xfs_rtrefcountbt_maxlevels_ondisk(void)
|
||||
minrecs[0] = xfs_rtrefcountbt_block_maxrecs(blocklen, true) / 2;
|
||||
minrecs[1] = xfs_rtrefcountbt_block_maxrecs(blocklen, false) / 2;
|
||||
|
||||
/* We need at most one record for every block in an rt group. */
|
||||
return xfs_btree_compute_maxlevels(minrecs, XFS_MAX_RGBLOCKS);
|
||||
/*
|
||||
* We need at most one record for every block in an rt group, and
|
||||
* one extra level for the inode root.
|
||||
*/
|
||||
return xfs_btree_compute_maxlevels(minrecs, XFS_MAX_RGBLOCKS) + 1;
|
||||
}
|
||||
|
||||
int __init
|
||||
|
||||
@@ -618,7 +618,7 @@ xfs_rtrmapbt_mem_cursor(
|
||||
struct xfs_btree_cur *cur;
|
||||
|
||||
cur = xfs_btree_alloc_cursor(mp, tp, &xfs_rtrmapbt_mem_ops,
|
||||
mp->m_rtrmap_maxlevels, xfs_rtrmapbt_cur_cache);
|
||||
xfs_rtrmapbt_maxlevels_ondisk(), xfs_rtrmapbt_cur_cache);
|
||||
cur->bc_mem.xfbtree = xfbt;
|
||||
cur->bc_nlevels = xfbt->nlevels;
|
||||
cur->bc_group = xfs_group_hold(rtg_group(rtg));
|
||||
@@ -716,10 +716,12 @@ xfs_rtrmapbt_maxlevels_ondisk(void)
|
||||
* happens, which means that we must compute the max height based on
|
||||
* what the btree will look like if it consumes almost all the blocks
|
||||
* in the data device due to maximal sharing factor.
|
||||
*
|
||||
* Add one extra level for the inode root.
|
||||
*/
|
||||
max_dblocks = -1U; /* max ag count */
|
||||
max_dblocks *= XFS_MAX_CRC_AG_BLOCKS;
|
||||
return xfs_btree_space_to_height(minrecs, max_dblocks);
|
||||
return xfs_btree_space_to_height(minrecs, max_dblocks) + 1;
|
||||
}
|
||||
|
||||
int __init
|
||||
|
||||
@@ -22,8 +22,23 @@ xfs_parent_calc_space_res(
|
||||
unsigned int namelen)
|
||||
{
|
||||
/*
|
||||
* Parent pointers are always the first attr in an attr tree, and never
|
||||
* larger than a block
|
||||
* A parent pointer is recorded per dirent, so an inode with N links
|
||||
* carries N of them and the attr fork can already be in leaf or node
|
||||
* format when one is added. That does not affect the reservation:
|
||||
* XFS_DAENTER_SPACE_RES covers a split at every level of a
|
||||
* maximum-depth attr dabtree, whatever format the fork is in now.
|
||||
*
|
||||
* The name is a dirent name and the value is a struct xfs_parent_rec,
|
||||
* so the leaf entry is always local and never exceeds 272 bytes.
|
||||
* Parent pointers require V5, hence a 1k minimum block size, so the
|
||||
* entry always stays under half a block and this needs none of the
|
||||
* double split allowance that xfs_attr_calc_size() makes.
|
||||
*
|
||||
* The second term hands a byte count to a macro whose parameter counts
|
||||
* mappings, so it asks for more extent-add allowance than the single
|
||||
* mapping a parent pointer adds - how much more depends on the block
|
||||
* size. It over-reserves either way, which is why it is left alone:
|
||||
* correcting the unit would shrink a reservation that is only generous.
|
||||
*/
|
||||
return XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) +
|
||||
XFS_NEXTENTADD_SPACE_RES(mp, namelen, XFS_ATTR_FORK);
|
||||
|
||||
@@ -418,6 +418,13 @@ xchk_superblock(
|
||||
xchk_block_set_corrupt(sc, bp);
|
||||
}
|
||||
|
||||
if (xfs_has_zoned(mp)) {
|
||||
if (sb->sb_rtstart != cpu_to_be64(mp->m_sb.sb_rtstart))
|
||||
xchk_block_set_corrupt(sc, bp);
|
||||
if (sb->sb_rtreserved != cpu_to_be64(mp->m_sb.sb_rtreserved))
|
||||
xchk_block_set_corrupt(sc, bp);
|
||||
}
|
||||
|
||||
/* Everything else must be zero. */
|
||||
sblen = xchk_superblock_ondisk_size(mp);
|
||||
if (memchr_inv((char *)sb + sblen, 0, BBTOB(bp->b_length) - sblen))
|
||||
|
||||
@@ -668,14 +668,16 @@ xrep_agfl_init_header(
|
||||
struct xfs_scrub *sc,
|
||||
struct xfs_buf *agfl_bp,
|
||||
struct xagb_bitmap *agfl_extents,
|
||||
xfs_agblock_t flcount)
|
||||
xfs_agblock_t flcount,
|
||||
struct xfs_agfl *old_agfl)
|
||||
{
|
||||
struct xrep_agfl_fill af = {
|
||||
.sc = sc,
|
||||
.flcount = flcount,
|
||||
};
|
||||
struct xfs_mount *mp = sc->mp;
|
||||
struct xfs_agfl *agfl;
|
||||
struct xfs_agfl *agfl = XFS_BUF_TO_AGFL(agfl_bp);
|
||||
const size_t agfl_sz = BBTOB(agfl_bp->b_length);
|
||||
int error;
|
||||
|
||||
ASSERT(flcount <= xfs_agfl_size(mp));
|
||||
@@ -684,8 +686,8 @@ xrep_agfl_init_header(
|
||||
* Start rewriting the header by setting the bno[] array to
|
||||
* NULLAGBLOCK, then setting AGFL header fields.
|
||||
*/
|
||||
agfl = XFS_BUF_TO_AGFL(agfl_bp);
|
||||
memset(agfl, 0xFF, BBTOB(agfl_bp->b_length));
|
||||
memcpy(old_agfl, agfl, agfl_sz);
|
||||
memset(agfl, 0xFF, agfl_sz);
|
||||
agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
|
||||
agfl->agfl_seqno = cpu_to_be32(pag_agno(sc->sa.pag));
|
||||
uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
|
||||
@@ -697,16 +699,23 @@ xrep_agfl_init_header(
|
||||
*/
|
||||
xagb_bitmap_init(&af.used_extents);
|
||||
af.agfl_bno = xfs_buf_to_agfl_bno(agfl_bp);
|
||||
xagb_bitmap_walk(agfl_extents, xrep_agfl_fill, &af);
|
||||
error = xagb_bitmap_walk(agfl_extents, xrep_agfl_fill, &af);
|
||||
if (error && error != -ECANCELED)
|
||||
goto err_undo;
|
||||
error = xagb_bitmap_disunion(agfl_extents, &af.used_extents);
|
||||
if (error)
|
||||
return error;
|
||||
goto err_undo;
|
||||
|
||||
/* Write new AGFL to disk. */
|
||||
xfs_trans_buf_set_type(sc->tp, agfl_bp, XFS_BLFT_AGFL_BUF);
|
||||
xfs_trans_log_buf(sc->tp, agfl_bp, 0, BBTOB(agfl_bp->b_length) - 1);
|
||||
xfs_trans_log_buf(sc->tp, agfl_bp, 0, agfl_sz - 1);
|
||||
xagb_bitmap_destroy(&af.used_extents);
|
||||
return 0;
|
||||
|
||||
err_undo:
|
||||
xagb_bitmap_destroy(&af.used_extents);
|
||||
memcpy(agfl, old_agfl, agfl_sz);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Repair the AGFL. */
|
||||
@@ -718,6 +727,7 @@ xrep_agfl(
|
||||
struct xfs_mount *mp = sc->mp;
|
||||
struct xfs_buf *agf_bp;
|
||||
struct xfs_buf *agfl_bp;
|
||||
struct xfs_agfl *old_agfl;
|
||||
xfs_agblock_t flcount;
|
||||
int error;
|
||||
|
||||
@@ -725,6 +735,10 @@ xrep_agfl(
|
||||
if (!xfs_has_rmapbt(mp))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
old_agfl = kzalloc(BBTOB(XFS_FSS_TO_BB(mp, 1)), XCHK_GFP_FLAGS);
|
||||
if (!old_agfl)
|
||||
return -ENOMEM;
|
||||
|
||||
xagb_bitmap_init(&agfl_extents);
|
||||
|
||||
/*
|
||||
@@ -734,7 +748,7 @@ xrep_agfl(
|
||||
*/
|
||||
error = xfs_alloc_read_agf(sc->sa.pag, sc->tp, 0, &agf_bp);
|
||||
if (error)
|
||||
return error;
|
||||
goto err_old_agfl;
|
||||
|
||||
/*
|
||||
* Make sure we have the AGFL buffer, as scrub might have decided it
|
||||
@@ -745,7 +759,7 @@ xrep_agfl(
|
||||
XFS_AGFL_DADDR(mp)),
|
||||
XFS_FSS_TO_BB(mp, 1), 0, &agfl_bp, NULL);
|
||||
if (error)
|
||||
return error;
|
||||
goto err_old_agfl;
|
||||
agfl_bp->b_ops = &xfs_agfl_buf_ops;
|
||||
|
||||
/* Gather all the extents we're going to put on the new AGFL. */
|
||||
@@ -762,10 +776,11 @@ xrep_agfl(
|
||||
* we adjust the AGF flcount (which can fail) so avoid updating any
|
||||
* buffers until we know that part works.
|
||||
*/
|
||||
xrep_agfl_update_agf(sc, agf_bp, flcount);
|
||||
error = xrep_agfl_init_header(sc, agfl_bp, &agfl_extents, flcount);
|
||||
error = xrep_agfl_init_header(sc, agfl_bp, &agfl_extents, flcount,
|
||||
old_agfl);
|
||||
if (error)
|
||||
goto err;
|
||||
xrep_agfl_update_agf(sc, agf_bp, flcount);
|
||||
|
||||
/*
|
||||
* Ok, the AGFL should be ready to go now. Roll the transaction to
|
||||
@@ -785,6 +800,8 @@ xrep_agfl(
|
||||
|
||||
err:
|
||||
xagb_bitmap_destroy(&agfl_extents);
|
||||
err_old_agfl:
|
||||
kfree(old_agfl);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@@ -571,7 +571,7 @@ xrep_abt_dispose_one(
|
||||
* allocation, and blocks that didn't get used can be freed via the usual
|
||||
* (deferred) means.
|
||||
*/
|
||||
STATIC void
|
||||
STATIC int
|
||||
xrep_abt_dispose_reservations(
|
||||
struct xrep_abt *ra,
|
||||
int error)
|
||||
@@ -582,9 +582,13 @@ xrep_abt_dispose_reservations(
|
||||
goto junkit;
|
||||
|
||||
list_for_each_entry_safe(resv, n, &ra->new_bnobt.resv_list, list) {
|
||||
error = xrep_abt_dispose_one(ra, resv);
|
||||
if (error)
|
||||
int error2 = xrep_abt_dispose_one(ra, resv);
|
||||
|
||||
if (error2) {
|
||||
if (!error)
|
||||
error = error2;
|
||||
goto junkit;
|
||||
}
|
||||
}
|
||||
|
||||
junkit:
|
||||
@@ -596,6 +600,7 @@ junkit:
|
||||
|
||||
xrep_newbt_cancel(&ra->new_bnobt);
|
||||
xrep_newbt_cancel(&ra->new_cntbt);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Retrieve free space data for bulk load. */
|
||||
@@ -801,7 +806,9 @@ xrep_abt_build_new_trees(
|
||||
goto err_newbt;
|
||||
|
||||
/* Dispose of any unused blocks and the accounting information. */
|
||||
xrep_abt_dispose_reservations(ra, error);
|
||||
error = xrep_abt_dispose_reservations(ra, error);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return xrep_roll_ag_trans(sc);
|
||||
|
||||
@@ -812,8 +819,7 @@ err_cur:
|
||||
xfs_btree_del_cursor(cnt_cur, error);
|
||||
xfs_btree_del_cursor(bno_cur, error);
|
||||
err_newbt:
|
||||
xrep_abt_dispose_reservations(ra, error);
|
||||
return error;
|
||||
return xrep_abt_dispose_reservations(ra, error);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1294,7 +1294,7 @@ xrep_xattr_swap_prep(
|
||||
.geo = sc->mp->m_attr_geo,
|
||||
.whichfork = XFS_ATTR_FORK,
|
||||
.trans = sc->tp,
|
||||
.total = 1,
|
||||
.total = xfs_dabuf_nfsb(sc->mp, XFS_ATTR_FORK),
|
||||
.owner = I_INO(sc->ip),
|
||||
};
|
||||
|
||||
|
||||
+4
-3
@@ -274,7 +274,7 @@ xchk_bmap_xref_rmap_cow(
|
||||
unsigned long long rmap_end;
|
||||
uint64_t owner = XFS_RMAP_OWN_COW;
|
||||
|
||||
if (!info->sc->sa.rmap_cur || xchk_skip_xref(info->sc->sm))
|
||||
if (xchk_skip_xref(info->sc->sm))
|
||||
return;
|
||||
|
||||
/* Find the rmap record for this irec. */
|
||||
@@ -1103,8 +1103,9 @@ xchk_bmap(
|
||||
* the rmap must match the combined mapping exactly.
|
||||
*/
|
||||
while (xchk_bmap_iext_iter(&info, &irec)) {
|
||||
if (xchk_should_terminate(sc, &error) ||
|
||||
(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
|
||||
if (xchk_should_terminate(sc, &error))
|
||||
return error;
|
||||
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
||||
return 0;
|
||||
|
||||
if (irec.br_startoff >= endoff) {
|
||||
|
||||
@@ -74,7 +74,6 @@ int xchk_setup_ag_rmapbt(struct xfs_scrub *sc);
|
||||
int xchk_setup_ag_refcountbt(struct xfs_scrub *sc);
|
||||
int xchk_setup_inode(struct xfs_scrub *sc);
|
||||
int xchk_setup_inode_bmap(struct xfs_scrub *sc);
|
||||
int xchk_setup_inode_bmap_data(struct xfs_scrub *sc);
|
||||
int xchk_setup_directory(struct xfs_scrub *sc);
|
||||
int xchk_setup_xattr(struct xfs_scrub *sc);
|
||||
int xchk_setup_symlink(struct xfs_scrub *sc);
|
||||
|
||||
@@ -37,8 +37,6 @@ bool xchk_da_process_error(struct xchk_da_btree *ds, int level, int *error);
|
||||
void xchk_da_set_corrupt(struct xchk_da_btree *ds, int level);
|
||||
void xchk_da_set_preen(struct xchk_da_btree *ds, int level);
|
||||
|
||||
void xchk_da_set_preen(struct xchk_da_btree *ds, int level);
|
||||
|
||||
int xchk_da_btree_hash(struct xchk_da_btree *ds, int level, __be32 *hashp);
|
||||
int xchk_da_btree(struct xfs_scrub *sc, int whichfork,
|
||||
xchk_da_btree_rec_fn scrub_fn, void *private);
|
||||
|
||||
@@ -484,18 +484,24 @@ xrep_dir_recover_data(
|
||||
while (offset < end) {
|
||||
struct xfs_dir2_data_unused *dup = bp->b_addr + offset;
|
||||
struct xfs_dir2_data_entry *dep = bp->b_addr + offset;
|
||||
unsigned int advance;
|
||||
|
||||
if (xchk_should_terminate(rd->sc, &error))
|
||||
return error;
|
||||
|
||||
/* Skip unused entries. */
|
||||
if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
|
||||
if (!dup->length)
|
||||
break;
|
||||
offset += be16_to_cpu(dup->length);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Don't walk off the end of the block. */
|
||||
offset += xfs_dir2_data_entsize(rd->sc->mp, dep->namelen);
|
||||
advance = xfs_dir2_data_entsize(rd->sc->mp, dep->namelen);
|
||||
if (!advance)
|
||||
break;
|
||||
offset += advance;
|
||||
if (offset > end)
|
||||
break;
|
||||
|
||||
@@ -721,7 +727,7 @@ xrep_dir_replay_removename(
|
||||
const struct xfs_name *name,
|
||||
xfs_extlen_t total)
|
||||
{
|
||||
struct xfs_inode *dp = rd->args.dp;
|
||||
struct xfs_inode *dp = rd->sc->tempip;
|
||||
|
||||
ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
|
||||
|
||||
@@ -1375,9 +1381,24 @@ xrep_dir_live_update(
|
||||
if (p->delta > 0)
|
||||
error = xrep_dir_stash_createname(rd, p->name,
|
||||
I_INO(p->ip));
|
||||
else
|
||||
error = xrep_dir_stash_removename(rd, p->name,
|
||||
else {
|
||||
/*
|
||||
* xfs_dentry_to_name in unlink or rename-exchange can
|
||||
* pass us names with ftype FT_UNKNOWN, but we really
|
||||
* must know the ftype of the child that is being
|
||||
* removed so that we can do nlink updates correctly
|
||||
* without holding inode references.
|
||||
*/
|
||||
struct xfs_name name = {
|
||||
.name = p->name->name,
|
||||
.len = p->name->len,
|
||||
.type = xfs_mode_to_ftype(
|
||||
VFS_IC(p->ip)->i_mode),
|
||||
};
|
||||
|
||||
error = xrep_dir_stash_removename(rd, &name,
|
||||
I_INO(p->ip));
|
||||
}
|
||||
mutex_unlock(&rd->pscan.lock);
|
||||
if (error)
|
||||
goto out_abort;
|
||||
@@ -1467,7 +1488,7 @@ xrep_dir_swap_prep(
|
||||
.geo = sc->mp->m_dir_geo,
|
||||
.whichfork = XFS_DATA_FORK,
|
||||
.trans = sc->tp,
|
||||
.total = 1,
|
||||
.total = xfs_dabuf_nfsb(sc->mp, XFS_DATA_FORK),
|
||||
.owner = I_INO(sc->ip),
|
||||
};
|
||||
|
||||
|
||||
+29
-2
@@ -259,6 +259,7 @@ xchk_dirtree_create_path(
|
||||
dl->nr_paths++;
|
||||
return 0;
|
||||
out_path:
|
||||
xino_bitmap_destroy(&path->seen_inodes);
|
||||
kfree(path);
|
||||
return error;
|
||||
}
|
||||
@@ -368,12 +369,38 @@ xchk_dirpath_step_up(
|
||||
struct xfs_inode *dp;
|
||||
xfs_ino_t parent_ino = be64_to_cpu(dl->pptr_rec.p_ino);
|
||||
unsigned int lock_mode;
|
||||
int error;
|
||||
int error = 0;
|
||||
|
||||
if (xchk_should_terminate(sc, &error))
|
||||
return error;
|
||||
|
||||
/* Grab and lock the parent directory. */
|
||||
error = xchk_iget(sc, parent_ino, &dp);
|
||||
if (error)
|
||||
switch (error) {
|
||||
case -EINVAL:
|
||||
case -ENOENT:
|
||||
mutex_lock(&dl->lock);
|
||||
|
||||
if (dl->stale) {
|
||||
/* live update detected a change in this path */
|
||||
error = -ESTALE;
|
||||
} else {
|
||||
/* inode doesn't exist, path invalid */
|
||||
error = -EFSCORRUPTED;
|
||||
|
||||
trace_xchk_dirpath_badino(dl->sc, path->path_nr,
|
||||
path->nr_steps, &dl->xname,
|
||||
&dl->pptr_rec);
|
||||
}
|
||||
|
||||
mutex_unlock(&dl->lock);
|
||||
return error;
|
||||
case 0:
|
||||
/* keep going */
|
||||
break;
|
||||
default:
|
||||
return error;
|
||||
}
|
||||
|
||||
lock_mode = xfs_ilock_attr_map_shared(dp);
|
||||
mutex_lock(&dl->lock);
|
||||
|
||||
@@ -479,6 +479,7 @@ again:
|
||||
}
|
||||
|
||||
if (xfs_has_parent(sc->mp)) {
|
||||
memset(&dl->ppargs, 0, sizeof(dl->ppargs));
|
||||
error = xfs_parent_removename(sc->tp, &dl->ppargs, dp,
|
||||
&dl->xname, sc->ip);
|
||||
if (error)
|
||||
@@ -618,6 +619,7 @@ xrep_dirtree_create_adoption_path(
|
||||
return 0;
|
||||
|
||||
out_path:
|
||||
xino_bitmap_destroy(&path->seen_inodes);
|
||||
kfree(path);
|
||||
return error;
|
||||
}
|
||||
|
||||
+37
-19
@@ -139,12 +139,40 @@ xrep_findparent_dirent(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xrep_findparent_want_scan_file(
|
||||
const struct xrep_findparent_info *fpi)
|
||||
{
|
||||
const struct xfs_scrub *sc = fpi->sc;
|
||||
const struct xfs_inode *dp = fpi->dp;
|
||||
|
||||
/* Only directories can be parents */
|
||||
if (!S_ISDIR(VFS_IC(dp)->i_mode))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* The inode being scanned cannot be its own parent, nor can any
|
||||
* temporary directory we created to stage this repair.
|
||||
*/
|
||||
if (dp == sc->ip || dp == sc->tempip)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Similarly, temporary files created to stage a repair cannot be the
|
||||
* parent of this inode.
|
||||
*/
|
||||
if (xrep_is_tempfile(dp))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a directory, walk the dirents looking for any that point to the
|
||||
* scrub target inode.
|
||||
*/
|
||||
STATIC int
|
||||
xrep_findparent_walk_directory(
|
||||
xrep_findparent_walk_file(
|
||||
struct xrep_findparent_info *fpi)
|
||||
{
|
||||
struct xfs_scrub *sc = fpi->sc;
|
||||
@@ -152,19 +180,11 @@ xrep_findparent_walk_directory(
|
||||
unsigned int lock_mode;
|
||||
int error = 0;
|
||||
|
||||
/*
|
||||
* The inode being scanned cannot be its own parent, nor can any
|
||||
* temporary directory we created to stage this repair.
|
||||
*/
|
||||
if (dp == sc->ip || dp == sc->tempip)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Similarly, temporary files created to stage a repair cannot be the
|
||||
* parent of this inode.
|
||||
*/
|
||||
if (xrep_is_tempfile(dp))
|
||||
if (!xrep_findparent_want_scan_file(fpi)) {
|
||||
if (fpi->parent_scan)
|
||||
xchk_iscan_mark_visited(&fpi->parent_scan->iscan, dp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the directory to see if there it contains an entry pointing to
|
||||
@@ -201,6 +221,8 @@ xrep_findparent_walk_directory(
|
||||
goto out_unlock;
|
||||
|
||||
out_unlock:
|
||||
if (fpi->parent_scan)
|
||||
xchk_iscan_mark_visited(&fpi->parent_scan->iscan, dp);
|
||||
xfs_iunlock(dp, lock_mode);
|
||||
return error;
|
||||
}
|
||||
@@ -308,11 +330,7 @@ xrep_findparent_scan(
|
||||
ASSERT(S_ISDIR(VFS_IC(sc->ip)->i_mode));
|
||||
|
||||
while ((ret = xchk_iscan_iter(&pscan->iscan, &fpi.dp)) == 1) {
|
||||
if (S_ISDIR(VFS_I(fpi.dp)->i_mode))
|
||||
ret = xrep_findparent_walk_directory(&fpi);
|
||||
else
|
||||
ret = 0;
|
||||
xchk_iscan_mark_visited(&pscan->iscan, fpi.dp);
|
||||
ret = xrep_findparent_walk_file(&fpi);
|
||||
xchk_irele(sc, fpi.dp);
|
||||
if (ret)
|
||||
break;
|
||||
@@ -401,7 +419,7 @@ xrep_findparent_confirm(
|
||||
goto out_rele;
|
||||
}
|
||||
|
||||
error = xrep_findparent_walk_directory(&fpi);
|
||||
error = xrep_findparent_walk_file(&fpi);
|
||||
if (error)
|
||||
goto out_rele;
|
||||
|
||||
|
||||
@@ -85,6 +85,8 @@ xchk_inobt_xref_finobt(
|
||||
goto no_record;
|
||||
|
||||
error = xfs_inobt_get_rec(cur, &frec, &has_record);
|
||||
if (error)
|
||||
return error;
|
||||
if (!has_record)
|
||||
return -EFSCORRUPTED;
|
||||
|
||||
@@ -188,6 +190,8 @@ xchk_finobt_xref_inobt(
|
||||
goto no_record;
|
||||
|
||||
error = xfs_inobt_get_rec(cur, &irec, &has_record);
|
||||
if (error)
|
||||
return error;
|
||||
if (!has_record)
|
||||
return -EFSCORRUPTED;
|
||||
|
||||
|
||||
+85
-2
@@ -23,6 +23,7 @@
|
||||
#include "xfs_rtgroup.h"
|
||||
#include "xfs_rtrmap_btree.h"
|
||||
#include "xfs_rtrefcount_btree.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/common.h"
|
||||
#include "scrub/trace.h"
|
||||
@@ -348,12 +349,78 @@ out_cancel:
|
||||
}
|
||||
|
||||
#ifdef CONFIG_XFS_ONLINE_REPAIR
|
||||
/*
|
||||
* Given a directory @dp, an existing inode @ip, and a @name, link @ip into @dp
|
||||
* under the given @name.
|
||||
*/
|
||||
static int
|
||||
xrep_metadir_add_child(
|
||||
struct xchk_metapath *mpath,
|
||||
xfs_ino_t old_dotdot)
|
||||
{
|
||||
struct xfs_trans *tp = mpath->sc->tp;
|
||||
struct xfs_dir_update *du = &mpath->du;
|
||||
struct xfs_inode *dp = du->dp;
|
||||
const struct xfs_name *name = du->name;
|
||||
struct xfs_inode *ip = du->ip;
|
||||
struct xfs_mount *mp = tp->t_mountp;
|
||||
const unsigned int resblks = mpath->link_resblks;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* The metadata file shouldn't be on the unlinked list, but we'll fix
|
||||
* it if that is the case.
|
||||
*/
|
||||
if (VFS_I(ip)->i_nlink == 0) {
|
||||
struct xfs_perag *pag;
|
||||
|
||||
pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, I_INO(ip)));
|
||||
error = xfs_iunlink_remove(tp, pag, ip);
|
||||
xfs_perag_put(pag);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = xfs_dir_createname(tp, dp, name, I_INO(ip), resblks);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
|
||||
|
||||
xfs_bumplink(tp, ip);
|
||||
|
||||
/* update dotdot entry in child */
|
||||
if (S_ISDIR(VFS_I(ip)->i_mode)) {
|
||||
xfs_bumplink(tp, dp);
|
||||
|
||||
/* Replace the dotdot entry in the child */
|
||||
if (old_dotdot != I_INO(dp)) {
|
||||
error = xfs_dir_replace(tp, ip, &xfs_name_dotdot,
|
||||
I_INO(dp), resblks);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the child's parent pointer */
|
||||
if (du->ppargs) {
|
||||
error = xfs_parent_addname(tp, du->ppargs, dp, name, ip);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
xfs_dir_update_hook(dp, ip, 1, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create the dirent represented by the final component of the path. */
|
||||
STATIC int
|
||||
xrep_metapath_link(
|
||||
struct xchk_metapath *mpath)
|
||||
{
|
||||
struct xfs_scrub *sc = mpath->sc;
|
||||
xfs_ino_t old_dotdot = NULLFSINO;
|
||||
int error;
|
||||
|
||||
mpath->du.dp = mpath->dp;
|
||||
mpath->du.name = &mpath->xname;
|
||||
@@ -366,7 +433,21 @@ xrep_metapath_link(
|
||||
|
||||
trace_xrep_metapath_link(sc, mpath->path, mpath->dp, I_INO(sc->ip));
|
||||
|
||||
return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du);
|
||||
if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {
|
||||
error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot,
|
||||
&old_dotdot);
|
||||
if (error && error != -ENOENT)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* subdir didn't give us a dotdot entry, so we just give up
|
||||
* and let the repair get marked as failed.
|
||||
*/
|
||||
if (old_dotdot == NULLFSINO)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return xrep_metadir_add_child(mpath, old_dotdot);
|
||||
}
|
||||
|
||||
/* Remove the dirent at the final component of the path. */
|
||||
@@ -397,7 +478,7 @@ xrep_metapath_unlink(
|
||||
|
||||
/* Figure out if we're removing a parent pointer too. */
|
||||
if (xfs_has_parent(mp)) {
|
||||
xfs_inode_to_parent_rec(&rec, ip);
|
||||
xfs_inode_to_parent_rec(&rec, mpath->dp);
|
||||
error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec,
|
||||
&mpath->pptr_args);
|
||||
switch (error) {
|
||||
@@ -556,6 +637,8 @@ xrep_metapath_try_unlink(
|
||||
error = xchk_metapath_ilock_parent_and_child(mpath, ip);
|
||||
if (error) {
|
||||
xchk_trans_cancel(sc);
|
||||
if (ip)
|
||||
xchk_irele(sc, ip);
|
||||
return error;
|
||||
}
|
||||
xfs_trans_ijoin(sc->tp, mpath->dp, 0);
|
||||
|
||||
@@ -325,7 +325,7 @@ xrep_quota_block(
|
||||
* If there's nothing that would impede a dqiterate, we're
|
||||
* done.
|
||||
*/
|
||||
if ((ddq->d_type & XFS_DQTYPE_REC_MASK) != dqtype ||
|
||||
if ((ddq->d_type & XFS_DQTYPE_REC_MASK) == dqtype &&
|
||||
id == be32_to_cpu(ddq->d_id)) {
|
||||
xfs_trans_brelse(sc->tp, bp);
|
||||
return 0;
|
||||
@@ -363,11 +363,18 @@ xrep_quota_block(
|
||||
ddq->d_rtbcount, &ddq->d_rtbtimer,
|
||||
defq->rtb.time);
|
||||
|
||||
/*
|
||||
* This transaction operates on raw disk buffers, so we don't
|
||||
* have a dquot log item to assign the LSN for us. Instead,
|
||||
* set it to zero so that log recovery will always replay any
|
||||
* logged dquot item atop this buffer.
|
||||
*/
|
||||
dqblk->dd_lsn = 0;
|
||||
|
||||
/* We only support v5 filesystems so always set these. */
|
||||
uuid_copy(&dqblk->dd_uuid, &sc->mp->m_sb.sb_meta_uuid);
|
||||
xfs_update_cksum((char *)dqblk, sizeof(struct xfs_dqblk),
|
||||
XFS_DQUOT_CRC_OFF);
|
||||
dqblk->dd_lsn = 0;
|
||||
}
|
||||
switch (dqtype) {
|
||||
case XFS_DQTYPE_USER:
|
||||
@@ -455,8 +462,7 @@ xrep_quota_data_fork(
|
||||
|
||||
if (truncate) {
|
||||
/* Erase everything after the block containing the max dquot */
|
||||
error = xfs_bunmapi_range(&sc->tp, sc->ip, 0,
|
||||
max_dqid_off * sc->mp->m_sb.sb_blocksize,
|
||||
error = xfs_bunmapi_range(&sc->tp, sc->ip, 0, max_dqid_off + 1,
|
||||
XFS_MAX_FILEOFF);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
@@ -263,8 +263,10 @@ xqcheck_mod_live_ino_dqtrx(
|
||||
dqa->tx_id = p->tx_id;
|
||||
error = rhashtable_insert_fast(&xqc->shadow_dquot_acct,
|
||||
&dqa->hash, xqcheck_dqacct_hash_params);
|
||||
if (error)
|
||||
if (error) {
|
||||
kfree(dqa);
|
||||
goto out_abort;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the shadow dqtrx (or an empty slot) here. */
|
||||
|
||||
+3
-3
@@ -601,7 +601,7 @@ xreap_configure_agextent_limits(
|
||||
|
||||
/* Maximum overhead of invalidating one buffer. */
|
||||
const unsigned int per_binval =
|
||||
xfs_buf_inval_log_space(1, XFS_B_TO_FSBT(mp, max_binval));
|
||||
xfs_buf_inval_log_space(1, XFS_FSB_TO_B(mp, max_binval));
|
||||
|
||||
/*
|
||||
* For each transaction in a reap chain, we can delete some number of
|
||||
@@ -680,7 +680,7 @@ xreap_configure_agcow_limits(
|
||||
|
||||
/* Overhead of invalidating one buffer */
|
||||
const unsigned int per_binval =
|
||||
xfs_buf_inval_log_space(1, XFS_B_TO_FSBT(mp, max_binval));
|
||||
xfs_buf_inval_log_space(1, XFS_FSB_TO_B(mp, max_binval));
|
||||
|
||||
/*
|
||||
* For each transaction in a reap chain, we can delete some number of
|
||||
@@ -1399,7 +1399,7 @@ xreap_bmapi_binval(
|
||||
* far we've gotten.
|
||||
*/
|
||||
if (!xreap_inc_binval(rs)) {
|
||||
imap->br_blockcount = agbno_next - bno;
|
||||
imap->br_blockcount = bno - agbno;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ xchk_refcount_mergeable(
|
||||
const struct xfs_refcount_irec *r1 = &rrc->prev_rec;
|
||||
|
||||
/* Ignore if prev_rec is not yet initialized. */
|
||||
if (r1->rc_blockcount > 0)
|
||||
if (r1->rc_blockcount == 0)
|
||||
return false;
|
||||
|
||||
if (r1->rc_domain != r2->rc_domain)
|
||||
@@ -581,8 +581,12 @@ xchk_xref_is_cow_staging(
|
||||
if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
|
||||
|
||||
/* Can't start after bno */
|
||||
if (rc.rc_startblock > agbno)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
|
||||
|
||||
/* Must be at least as long as what was passed in */
|
||||
if (rc.rc_blockcount < len)
|
||||
if (rc.rc_startblock + rc.rc_blockcount < agbno + len)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,10 @@ xchk_rgsuperblock_xref(
|
||||
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
||||
return;
|
||||
|
||||
xchk_xref_is_used_rt_space(sc, xfs_rgbno_to_rtb(sc->sr.rtg, 0), 1);
|
||||
xchk_xref_is_only_rt_owned_by(sc, 0, 1, &XFS_RMAP_OINFO_FS);
|
||||
xchk_xref_is_used_rt_space(sc, xfs_rgbno_to_rtb(sc->sr.rtg, 0),
|
||||
sc->mp->m_sb.sb_rextsize);
|
||||
xchk_xref_is_only_rt_owned_by(sc, 0, sc->mp->m_sb.sb_rextsize,
|
||||
&XFS_RMAP_OINFO_FS);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
+66
-16
@@ -20,6 +20,7 @@
|
||||
#include "xfs_metafile.h"
|
||||
#include "xfs_rtrefcount_btree.h"
|
||||
#include "xfs_rtalloc.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/common.h"
|
||||
#include "scrub/btree.h"
|
||||
@@ -375,7 +376,7 @@ xchk_rtrefcount_mergeable(
|
||||
const struct xfs_refcount_irec *r1 = &rrc->prev_rec;
|
||||
|
||||
/* Ignore if prev_rec is not yet initialized. */
|
||||
if (r1->rc_blockcount > 0)
|
||||
if (r1->rc_blockcount == 0)
|
||||
return false;
|
||||
|
||||
if (r1->rc_startblock + r1->rc_blockcount != r2->rc_startblock)
|
||||
@@ -428,7 +429,7 @@ static inline void
|
||||
xchk_rtrefcountbt_xref_gaps(
|
||||
struct xfs_scrub *sc,
|
||||
struct xchk_rtrefcbt_records *rrc,
|
||||
xfs_rtblock_t bno)
|
||||
xfs_rgblock_t bno)
|
||||
{
|
||||
struct xfs_rmap_irec low;
|
||||
struct xfs_rmap_irec high;
|
||||
@@ -504,30 +505,75 @@ xchk_rtrefcountbt_rec(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Count the number of blocks used by the rtrefcount btree file in this AG. */
|
||||
static int
|
||||
xchk_rtrefcount_count_agblocks(
|
||||
struct xfs_scrub *sc,
|
||||
xfs_agnumber_t agno,
|
||||
const struct xfs_owner_info *btree_oinfo,
|
||||
xfs_filblks_t *blocks)
|
||||
{
|
||||
xfs_filblks_t agblocks = 0;
|
||||
int error;
|
||||
|
||||
error = xchk_ag_init_existing(sc, agno, &sc->sa);
|
||||
if (error)
|
||||
goto out_free;
|
||||
|
||||
/*
|
||||
* If we don't have an rmap cursor, we can't complete the cross
|
||||
* referencing, so return EFSCORRUPTED to end the loop and trigger the
|
||||
* XFAIL flag.
|
||||
*/
|
||||
if (!sc->sa.rmap_cur) {
|
||||
error = -EFSCORRUPTED;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
error = xchk_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, btree_oinfo,
|
||||
&agblocks);
|
||||
if (error)
|
||||
goto out_free;
|
||||
|
||||
*blocks += agblocks;
|
||||
out_free:
|
||||
xchk_ag_free(sc, &sc->sa);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Make sure we have as many refc blocks as the rmap says. */
|
||||
STATIC void
|
||||
xchk_refcount_xref_rmap(
|
||||
xchk_rtrefcount_xref_rmap(
|
||||
struct xfs_scrub *sc,
|
||||
const struct xfs_owner_info *btree_oinfo,
|
||||
xfs_extlen_t cow_blocks)
|
||||
{
|
||||
xfs_filblks_t refcbt_blocks = 0;
|
||||
xfs_filblks_t blocks;
|
||||
int error;
|
||||
xfs_filblks_t blocks = 1; /* one for the iroot */
|
||||
xfs_agnumber_t agno;
|
||||
int error = 0;
|
||||
|
||||
if (!sc->sr.rmap_cur || !sc->sa.rmap_cur || xchk_skip_xref(sc->sm))
|
||||
if (!xfs_has_rmapbt(sc->mp) || xchk_skip_xref(sc->sm))
|
||||
return;
|
||||
|
||||
/* Check that we saw as many refcbt blocks as the rmap knows about. */
|
||||
error = xfs_btree_count_blocks(sc->sr.refc_cur, &refcbt_blocks);
|
||||
if (!xchk_btree_process_error(sc, sc->sr.refc_cur, 0, &error))
|
||||
return;
|
||||
error = xchk_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, btree_oinfo,
|
||||
&blocks);
|
||||
if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur))
|
||||
|
||||
for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
|
||||
error = xchk_rtrefcount_count_agblocks(sc, agno, btree_oinfo,
|
||||
&blocks);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
|
||||
return;
|
||||
if (blocks != refcbt_blocks)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
|
||||
xchk_fblock_xref_set_corrupt(sc, XFS_DATA_FORK, 0);
|
||||
|
||||
if (!sc->sr.rmap_cur || xchk_skip_xref(sc->sm))
|
||||
return;
|
||||
|
||||
/* Check that we saw as many cow blocks as the rmap knows about. */
|
||||
error = xchk_count_rmap_ownedby_ag(sc, sc->sr.rmap_cur,
|
||||
@@ -538,7 +584,7 @@ xchk_refcount_xref_rmap(
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0);
|
||||
}
|
||||
|
||||
/* Scrub the refcount btree for some AG. */
|
||||
/* Scrub the refcount btree for some rtgroup. */
|
||||
int
|
||||
xchk_rtrefcountbt(
|
||||
struct xfs_scrub *sc)
|
||||
@@ -564,11 +610,11 @@ xchk_rtrefcountbt(
|
||||
|
||||
/*
|
||||
* Check that all blocks between the last refcount > 1 record and the
|
||||
* end of the rt volume have at most one reverse mapping.
|
||||
* end of the rtgroup have at most one reverse mapping.
|
||||
*/
|
||||
xchk_rtrefcountbt_xref_gaps(sc, &rrc, sc->mp->m_sb.sb_rblocks);
|
||||
|
||||
xchk_refcount_xref_rmap(sc, &btree_oinfo, rrc.cow_blocks);
|
||||
xchk_rtrefcountbt_xref_gaps(sc, &rrc,
|
||||
xfs_rtx_to_rgbno(sc->sr.rtg, sc->mp->m_sb.sb_rgextents));
|
||||
xchk_rtrefcount_xref_rmap(sc, &btree_oinfo, rrc.cow_blocks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -609,8 +655,12 @@ xchk_xref_is_rt_cow_staging(
|
||||
if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
|
||||
|
||||
/* Can't start after bno */
|
||||
if (rc.rc_startblock > bno)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
|
||||
|
||||
/* Must be at least as long as what was passed in */
|
||||
if (rc.rc_blockcount < len)
|
||||
if (rc.rc_startblock + rc.rc_blockcount < bno + len)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -164,9 +164,10 @@ xrep_rtsummary(
|
||||
/*
|
||||
* Now exchange the contents. Nothing in repair uses the temporary
|
||||
* buffer, so we can reuse it for the tempfile exchrange information.
|
||||
* Use XFS_MAX_FILEOFF here so that we correct the rtsummary file size.
|
||||
*/
|
||||
error = xrep_tempexch_trans_reserve(sc, XFS_DATA_FORK, 0,
|
||||
rts->rsumblocks, &rts->tempexch);
|
||||
XFS_MAX_FILEOFF, &rts->tempexch);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
||||
@@ -765,8 +765,7 @@ out_nofix:
|
||||
out_teardown:
|
||||
error = xchk_teardown(sc, error);
|
||||
out_sc:
|
||||
if (error != -ENOENT)
|
||||
xchk_stats_merge(mp, sm, &run);
|
||||
xchk_stats_merge(mp, sm, error, &run);
|
||||
kfree(sc);
|
||||
out:
|
||||
trace_xchk_done(XFS_I(file_inode(file)), sm, error);
|
||||
|
||||
@@ -261,7 +261,6 @@ static inline int xchk_nothing(struct xfs_scrub *sc)
|
||||
}
|
||||
|
||||
/* Metadata scrubbers */
|
||||
int xchk_tester(struct xfs_scrub *sc);
|
||||
int xchk_superblock(struct xfs_scrub *sc);
|
||||
int xchk_agf(struct xfs_scrub *sc);
|
||||
int xchk_agfl(struct xfs_scrub *sc);
|
||||
|
||||
+46
-23
@@ -29,6 +29,7 @@ struct xchk_scrub_stats {
|
||||
uint32_t incomplete;
|
||||
uint32_t warning;
|
||||
uint32_t retries;
|
||||
uint32_t runtime_errors;
|
||||
|
||||
/* repair stats */
|
||||
uint32_t repair_invocations;
|
||||
@@ -84,6 +85,7 @@ static const char *name_map[XFS_SCRUB_TYPE_NR] = {
|
||||
[XFS_SCRUB_TYPE_RGSUPER] = "rgsuper",
|
||||
[XFS_SCRUB_TYPE_RTRMAPBT] = "rtrmapbt",
|
||||
[XFS_SCRUB_TYPE_RTREFCBT] = "rtrefcountbt",
|
||||
[XFS_SCRUB_TYPE_HEALTHY] = "healthy",
|
||||
};
|
||||
|
||||
/* Format the scrub stats into a text buffer, similar to pcp style. */
|
||||
@@ -99,25 +101,32 @@ xchk_stats_format(
|
||||
int ret = 0;
|
||||
|
||||
for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {
|
||||
struct xchk_scrub_stats fss;
|
||||
|
||||
if (!name_map[i])
|
||||
continue;
|
||||
|
||||
spin_lock(&css->css_lock);
|
||||
memcpy(&fss, css, offsetof(struct xchk_scrub_stats, css_lock));
|
||||
spin_unlock(&css->css_lock);
|
||||
|
||||
ret = scnprintf(buf, remaining,
|
||||
"%s %u %u %u %u %u %u %u %u %u %llu %u %u %llu\n",
|
||||
"%s %u %u %u %u %u %u %u %u %u %llu %u %u %llu %u\n",
|
||||
name_map[i],
|
||||
(unsigned int)css->invocations,
|
||||
(unsigned int)css->clean,
|
||||
(unsigned int)css->corrupt,
|
||||
(unsigned int)css->preen,
|
||||
(unsigned int)css->xfail,
|
||||
(unsigned int)css->xcorrupt,
|
||||
(unsigned int)css->incomplete,
|
||||
(unsigned int)css->warning,
|
||||
(unsigned int)css->retries,
|
||||
(unsigned long long)css->checktime_us,
|
||||
(unsigned int)css->repair_invocations,
|
||||
(unsigned int)css->repair_success,
|
||||
(unsigned long long)css->repairtime_us);
|
||||
(unsigned int)fss.invocations,
|
||||
(unsigned int)fss.clean,
|
||||
(unsigned int)fss.corrupt,
|
||||
(unsigned int)fss.preen,
|
||||
(unsigned int)fss.xfail,
|
||||
(unsigned int)fss.xcorrupt,
|
||||
(unsigned int)fss.incomplete,
|
||||
(unsigned int)fss.warning,
|
||||
(unsigned int)fss.retries,
|
||||
(unsigned long long)fss.checktime_us,
|
||||
(unsigned int)fss.repair_invocations,
|
||||
(unsigned int)fss.repair_success,
|
||||
(unsigned long long)fss.repairtime_us,
|
||||
(unsigned int)fss.runtime_errors);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
|
||||
@@ -188,31 +197,41 @@ STATIC void
|
||||
xchk_stats_merge_one(
|
||||
struct xchk_stats *cs,
|
||||
const struct xfs_scrub_metadata *sm,
|
||||
int error,
|
||||
const struct xchk_stats_run *run)
|
||||
{
|
||||
struct xchk_scrub_stats *css;
|
||||
unsigned int sm_flags = sm->sm_flags;
|
||||
|
||||
if (sm->sm_type >= XFS_SCRUB_TYPE_NR) {
|
||||
ASSERT(sm->sm_type < XFS_SCRUB_TYPE_NR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* caller applies this same transformation after we return */
|
||||
if (error == -EFSCORRUPTED || error == -EFSBADCRC) {
|
||||
sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
|
||||
error = 0;
|
||||
}
|
||||
|
||||
css = &cs->cs_stats[sm->sm_type];
|
||||
spin_lock(&css->css_lock);
|
||||
css->invocations++;
|
||||
if (!(sm->sm_flags & XFS_SCRUB_OFLAG_UNCLEAN))
|
||||
if (error)
|
||||
css->runtime_errors++;
|
||||
else if (!(sm_flags & XFS_SCRUB_OFLAG_UNCLEAN))
|
||||
css->clean++;
|
||||
if (sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
||||
if (sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
||||
css->corrupt++;
|
||||
if (sm->sm_flags & XFS_SCRUB_OFLAG_PREEN)
|
||||
if (sm_flags & XFS_SCRUB_OFLAG_PREEN)
|
||||
css->preen++;
|
||||
if (sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL)
|
||||
if (sm_flags & XFS_SCRUB_OFLAG_XFAIL)
|
||||
css->xfail++;
|
||||
if (sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)
|
||||
if (sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)
|
||||
css->xcorrupt++;
|
||||
if (sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)
|
||||
if (sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)
|
||||
css->incomplete++;
|
||||
if (sm->sm_flags & XFS_SCRUB_OFLAG_WARNING)
|
||||
if (sm_flags & XFS_SCRUB_OFLAG_WARNING)
|
||||
css->warning++;
|
||||
css->retries += run->retries;
|
||||
css->checktime_us += howmany_64(run->scrub_ns, NSEC_PER_USEC);
|
||||
@@ -230,10 +249,14 @@ void
|
||||
xchk_stats_merge(
|
||||
struct xfs_mount *mp,
|
||||
const struct xfs_scrub_metadata *sm,
|
||||
int error,
|
||||
const struct xchk_stats_run *run)
|
||||
{
|
||||
xchk_stats_merge_one(&global_stats, sm, run);
|
||||
xchk_stats_merge_one(mp->m_scrub_stats, sm, run);
|
||||
if (error == -ENOENT)
|
||||
return;
|
||||
|
||||
xchk_stats_merge_one(&global_stats, sm, error, run);
|
||||
xchk_stats_merge_one(mp->m_scrub_stats, sm, error, run);
|
||||
}
|
||||
|
||||
/* debugfs boilerplate */
|
||||
|
||||
@@ -27,7 +27,7 @@ void xchk_stats_register(struct xchk_stats *cs, struct dentry *parent);
|
||||
void xchk_stats_unregister(struct xchk_stats *cs);
|
||||
|
||||
void xchk_stats_merge(struct xfs_mount *mp, const struct xfs_scrub_metadata *sm,
|
||||
const struct xchk_stats_run *run);
|
||||
int error, const struct xchk_stats_run *run);
|
||||
|
||||
static inline u64 xchk_stats_now(void) { return ktime_get_ns(); }
|
||||
static inline u64 xchk_stats_elapsed_ns(u64 since)
|
||||
@@ -53,7 +53,7 @@ static inline u64 xchk_stats_elapsed_ns(u64 since)
|
||||
# define xchk_stats_unregister(cs) ((void)0)
|
||||
# define xchk_stats_now() (0)
|
||||
# define xchk_stats_elapsed_ns(x) (0 * (x))
|
||||
# define xchk_stats_merge(mp, sm, run) ((void)0)
|
||||
# define xchk_stats_merge(mp, sm, error, run) ((void)0)
|
||||
#endif /* CONFIG_XFS_ONLINE_SCRUB_STATS */
|
||||
|
||||
#endif /* __XFS_SCRUB_STATS_H__ */
|
||||
|
||||
@@ -291,7 +291,7 @@ xrep_symlink_swap_prep(
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_trans_log_inode(sc->tp, sc->ip, 0);
|
||||
xfs_trans_log_inode(sc->tp, sc->tempip, logflags);
|
||||
|
||||
error = xfs_defer_finish(&sc->tp);
|
||||
if (error)
|
||||
|
||||
+22
-7
@@ -649,6 +649,19 @@ xrep_tempexch_prep_request(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
xrep_tempexch_estimate_sf_resblks(
|
||||
struct xfs_scrub *sc,
|
||||
int whichfork)
|
||||
{
|
||||
/* repairing a symlink target */
|
||||
if (S_ISLNK(VFS_I(sc->ip)->i_mode) && whichfork == XFS_DATA_FORK)
|
||||
return 1;
|
||||
|
||||
/* everything else is a directory or an xattr structure */
|
||||
return xfs_dabuf_nfsb(sc->mp, whichfork);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill out the mapping exchange resource estimation structures in preparation
|
||||
* for exchanging the contents of a metadata file that we've rebuilt in the
|
||||
@@ -663,6 +676,8 @@ xrep_tempexch_estimate(
|
||||
struct xfs_ifork *ifp;
|
||||
struct xfs_ifork *tifp;
|
||||
int whichfork = xfs_exchmaps_reqfork(req);
|
||||
unsigned int sf_resblks =
|
||||
xrep_tempexch_estimate_sf_resblks(sc, whichfork);
|
||||
int state = 0;
|
||||
|
||||
/*
|
||||
@@ -693,9 +708,9 @@ xrep_tempexch_estimate(
|
||||
* plus the block we converted.
|
||||
*/
|
||||
req->ip1_bcount = sc->tempip->i_nblocks;
|
||||
req->ip2_bcount = 1;
|
||||
req->ip2_bcount = sf_resblks;
|
||||
req->nr_exchanges = 1 + tifp->if_nextents;
|
||||
req->resblks = 1;
|
||||
req->resblks = sf_resblks;
|
||||
break;
|
||||
case 2:
|
||||
/*
|
||||
@@ -707,10 +722,10 @@ xrep_tempexch_estimate(
|
||||
* is (worst case) the extent count of the file being repaired
|
||||
* plus the block we converted.
|
||||
*/
|
||||
req->ip1_bcount = 1;
|
||||
req->ip1_bcount = sf_resblks;
|
||||
req->ip2_bcount = sc->ip->i_nblocks;
|
||||
req->nr_exchanges = 1 + ifp->if_nextents;
|
||||
req->resblks = 1;
|
||||
req->resblks = sf_resblks;
|
||||
break;
|
||||
case 3:
|
||||
/*
|
||||
@@ -722,10 +737,10 @@ xrep_tempexch_estimate(
|
||||
* fileoff 0. Presumably, the caller could not exchange the
|
||||
* two inode fork areas directly.
|
||||
*/
|
||||
req->ip1_bcount = 1;
|
||||
req->ip2_bcount = 1;
|
||||
req->ip1_bcount = sf_resblks;
|
||||
req->ip2_bcount = sf_resblks;
|
||||
req->nr_exchanges = 1;
|
||||
req->resblks = 2;
|
||||
req->resblks = 2 * sf_resblks;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,10 +39,6 @@ int xrep_tempfile_roll_trans(struct xfs_scrub *sc);
|
||||
void xrep_tempfile_copyout_local(struct xfs_scrub *sc, int whichfork);
|
||||
bool xrep_is_tempfile(const struct xfs_inode *ip);
|
||||
#else
|
||||
static inline void xrep_tempfile_iolock_both(struct xfs_scrub *sc)
|
||||
{
|
||||
xchk_ilock(sc, XFS_IOLOCK_EXCL);
|
||||
}
|
||||
# define xrep_is_tempfile(ip) (false)
|
||||
# define xrep_tempfile_adjust_directory_tree(sc) (0)
|
||||
# define xrep_tempfile_rele(sc)
|
||||
|
||||
+34
-1
@@ -1640,7 +1640,7 @@ DECLARE_EVENT_CLASS(xchk_pptr_class,
|
||||
__entry->dev = ip->i_mount->m_super->s_dev;
|
||||
__entry->ino = I_INO(ip);
|
||||
__entry->namelen = name->len;
|
||||
memcpy(__get_str(name), name, name->len);
|
||||
memcpy(__get_str(name), name->name, name->len);
|
||||
__entry->far_ino = far_ino;
|
||||
),
|
||||
TP_printk("dev %d:%d ino 0x%llx name '%.*s' far_ino 0x%llx",
|
||||
@@ -1706,6 +1706,39 @@ DEFINE_EVENT(xchk_dirtree_class, name, \
|
||||
DEFINE_XCHK_DIRTREE_EVENT(xchk_dirtree_create_path);
|
||||
DEFINE_XCHK_DIRTREE_EVENT(xchk_dirpath_walk_upwards);
|
||||
|
||||
TRACE_EVENT(xchk_dirpath_badino,
|
||||
TP_PROTO(struct xfs_scrub *sc, unsigned int path_nr,
|
||||
unsigned int step_nr, const struct xfs_name *name,
|
||||
const struct xfs_parent_rec *pptr),
|
||||
TP_ARGS(sc, path_nr, step_nr, name, pptr),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(unsigned int, path_nr)
|
||||
__field(unsigned int, step_nr)
|
||||
__field(xfs_ino_t, parent_ino)
|
||||
__field(unsigned int, parent_gen)
|
||||
__field(unsigned int, namelen)
|
||||
__dynamic_array(char, name, name->len)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dev = sc->mp->m_super->s_dev;
|
||||
__entry->path_nr = path_nr;
|
||||
__entry->step_nr = step_nr;
|
||||
__entry->parent_ino = be64_to_cpu(pptr->p_ino);
|
||||
__entry->parent_gen = be32_to_cpu(pptr->p_gen);
|
||||
__entry->namelen = name->len;
|
||||
memcpy(__get_str(name), name->name, name->len);
|
||||
),
|
||||
TP_printk("dev %d:%d path %u step %u parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->path_nr,
|
||||
__entry->step_nr,
|
||||
__entry->parent_ino,
|
||||
__entry->parent_gen,
|
||||
__entry->namelen,
|
||||
__get_str(name))
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(xchk_dirpath_class,
|
||||
TP_PROTO(struct xfs_scrub *sc, struct xfs_inode *ip,
|
||||
unsigned int path_nr, unsigned int step_nr,
|
||||
|
||||
+2
-2
@@ -139,7 +139,7 @@ xfs_buf_free(
|
||||
ASSERT(list_empty(&bp->b_lru));
|
||||
|
||||
if (!xfs_buftarg_is_mem(bp->b_target) && size >= PAGE_SIZE)
|
||||
mm_account_reclaimed_pages(howmany(size, PAGE_SHIFT));
|
||||
mm_account_reclaimed_pages(howmany(size, PAGE_SIZE));
|
||||
|
||||
if (is_vmalloc_addr(bp->b_addr))
|
||||
vfree(bp->b_addr);
|
||||
@@ -176,7 +176,7 @@ xfs_buf_alloc_kmem(
|
||||
ASSERT(is_power_of_2(size));
|
||||
ASSERT(size < PAGE_SIZE);
|
||||
|
||||
bp->b_addr = kmalloc(size, gfp_mask);
|
||||
bp->b_addr = kmalloc(size, gfp_mask | __GFP_RECLAIMABLE);
|
||||
if (!bp->b_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ static inline void xfs_buf_dquot_iodone(struct xfs_buf *bp)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_XFS_QUOTA */
|
||||
void xfs_buf_iodone(struct xfs_buf *);
|
||||
bool xfs_buf_log_check_iovec(struct kvec *iovec);
|
||||
|
||||
unsigned int xfs_buf_inval_log_space(unsigned int map_count,
|
||||
|
||||
@@ -344,7 +344,17 @@ xfs_xmi_validate(
|
||||
if (!xfs_verify_fileext(mp, xlf->xmi_startoff1, xlf->xmi_blockcount))
|
||||
return false;
|
||||
|
||||
return xfs_verify_fileext(mp, xlf->xmi_startoff2, xlf->xmi_blockcount);
|
||||
if (!xfs_verify_fileext(mp, xlf->xmi_startoff2, xlf->xmi_blockcount))
|
||||
return false;
|
||||
|
||||
if (xlf->xmi_flags & XFS_EXCHMAPS_SET_SIZES) {
|
||||
if ((int64_t)xlf->xmi_isize1 < 0)
|
||||
return false;
|
||||
if ((int64_t)xlf->xmi_isize2 < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -403,6 +413,13 @@ xfs_xmi_item_recover_intent(
|
||||
*ipp1 = ip1;
|
||||
*ipp2 = ip2;
|
||||
xmi = xfs_exchmaps_init_intent(req);
|
||||
|
||||
/* Restore intended file sizes from recovered logged item */
|
||||
if (req->flags & XFS_EXCHMAPS_SET_SIZES) {
|
||||
xmi->xmi_isize1 = xlf->xmi_isize1;
|
||||
xmi->xmi_isize2 = xlf->xmi_isize2;
|
||||
}
|
||||
|
||||
xfs_defer_add_item(dfp, &xmi->xmi_list);
|
||||
return xmi;
|
||||
|
||||
|
||||
@@ -504,6 +504,9 @@ xfs_exchange_range_finish(
|
||||
{
|
||||
int error;
|
||||
|
||||
if (fxr->flags & XFS_EXCHANGE_RANGE_DRY_RUN)
|
||||
return 0;
|
||||
|
||||
error = file_remove_privs(fxr->file1);
|
||||
if (error)
|
||||
return error;
|
||||
@@ -783,9 +786,12 @@ xfs_exchange_range(
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fsnotify_modify(fxr->file1);
|
||||
if (fxr->file2 != fxr->file1)
|
||||
fsnotify_modify(fxr->file2);
|
||||
if (!(fxr->flags & XFS_EXCHANGE_RANGE_DRY_RUN)) {
|
||||
fsnotify_modify(fxr->file1);
|
||||
if (fxr->file2 != fxr->file1)
|
||||
fsnotify_modify(fxr->file2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -161,8 +161,8 @@ xfs_extent_busy_update_extent(
|
||||
xfs_agblock_t fbno,
|
||||
xfs_extlen_t flen,
|
||||
bool userdata)
|
||||
__releases(&eb->eb_lock)
|
||||
__acquires(&eb->eb_lock)
|
||||
__releases(&xg->xg_busy_extents->eb_lock)
|
||||
__acquires(&xg->xg_busy_extents->eb_lock)
|
||||
{
|
||||
struct xfs_extent_busy_tree *eb = xg->xg_busy_extents;
|
||||
xfs_agblock_t fend = fbno + flen;
|
||||
|
||||
+14
-33
@@ -129,9 +129,8 @@ xfs_file_fsync(
|
||||
int datasync)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(file->f_mapping->host);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
int error, err2;
|
||||
int log_flushed = 0;
|
||||
int error;
|
||||
|
||||
trace_xfs_file_fsync(ip);
|
||||
|
||||
@@ -139,30 +138,22 @@ xfs_file_fsync(
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (xfs_is_shutdown(mp))
|
||||
if (xfs_is_shutdown(ip->i_mount))
|
||||
return -EIO;
|
||||
|
||||
xfs_iflags_clear(ip, XFS_ITRUNCATED);
|
||||
|
||||
/*
|
||||
* If we have an RT and/or log subvolume we need to make sure to flush
|
||||
* the write cache the device used for file data first. This is to
|
||||
* ensure newly written file data make it to disk before logging the new
|
||||
* inode size in case of an extending write.
|
||||
*/
|
||||
if (XFS_IS_REALTIME_INODE(ip) && mp->m_rtdev_targp != mp->m_ddev_targp)
|
||||
error = blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev);
|
||||
else if (mp->m_logdev_targp != mp->m_ddev_targp)
|
||||
error = blkdev_issue_flush(mp->m_ddev_targp->bt_bdev);
|
||||
|
||||
/*
|
||||
* If the inode has a inode log item attached, it may need the journal
|
||||
* flushed to persist any changes the log item might be tracking.
|
||||
* If the inode has a log item attached, we must force the log up to the
|
||||
* last LSN in which the inode was modified to ensure all metadata is
|
||||
* persisted. The log force will flush the caches for all devices
|
||||
* before writing the log records unless it is a no-op because there are
|
||||
* no modifications to this inode that need to be pushed out.
|
||||
*/
|
||||
if (ip->i_itemp) {
|
||||
err2 = xfs_fsync_flush_log(ip, datasync, &log_flushed);
|
||||
if (err2 && !error)
|
||||
error = err2;
|
||||
error = xfs_fsync_flush_log(ip, datasync, &log_flushed);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -171,21 +162,11 @@ xfs_file_fsync(
|
||||
* when no metadata needed to be committed.
|
||||
*
|
||||
* Use the inode's actual file data target rather than assuming the
|
||||
* main data device. Realtime inodes with a separate realtime device
|
||||
* are flushed before the log force, so this fallback only applies
|
||||
* when the file data target is the same as the log target.
|
||||
* main data device.
|
||||
*/
|
||||
if (!log_flushed) {
|
||||
struct xfs_buftarg *file_targp = xfs_inode_buftarg(ip);
|
||||
|
||||
if (mp->m_logdev_targp == file_targp) {
|
||||
err2 = blkdev_issue_flush(file_targp->bt_bdev);
|
||||
if (err2 && !error)
|
||||
error = err2;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
if (!log_flushed)
|
||||
return blkdev_issue_flush(xfs_inode_buftarg(ip)->bt_bdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
+72
-60
@@ -87,12 +87,10 @@ xfs_healthmon_put(
|
||||
struct xfs_healthmon *hm)
|
||||
{
|
||||
if (refcount_dec_and_test(&hm->ref)) {
|
||||
struct xfs_healthmon_event *event;
|
||||
struct xfs_healthmon_event *next = hm->first_event;
|
||||
struct xfs_healthmon_event *event, *s;
|
||||
|
||||
while ((event = next) != NULL) {
|
||||
list_for_each_entry_safe(event, s, &hm->event_list, entry) {
|
||||
trace_xfs_healthmon_drop(hm, event);
|
||||
next = event->next;
|
||||
kfree(event);
|
||||
}
|
||||
|
||||
@@ -173,9 +171,13 @@ static inline void xfs_healthmon_bump_lost(struct xfs_healthmon *hm)
|
||||
*/
|
||||
static bool
|
||||
xfs_healthmon_merge_events(
|
||||
struct xfs_healthmon_event *existing,
|
||||
struct xfs_healthmon *hm,
|
||||
const struct xfs_healthmon_event *new)
|
||||
{
|
||||
struct xfs_healthmon_event *existing =
|
||||
list_last_entry_or_null(&hm->event_list, struct
|
||||
xfs_healthmon_event, entry);
|
||||
|
||||
if (!existing)
|
||||
return false;
|
||||
|
||||
@@ -192,7 +194,7 @@ xfs_healthmon_merge_events(
|
||||
|
||||
case XFS_HEALTHMON_LOST:
|
||||
existing->lostcount += new->lostcount;
|
||||
return true;
|
||||
goto out_merge;
|
||||
|
||||
case XFS_HEALTHMON_SICK:
|
||||
case XFS_HEALTHMON_CORRUPT:
|
||||
@@ -200,19 +202,19 @@ xfs_healthmon_merge_events(
|
||||
switch (existing->domain) {
|
||||
case XFS_HEALTHMON_FS:
|
||||
existing->fsmask |= new->fsmask;
|
||||
return true;
|
||||
goto out_merge;
|
||||
case XFS_HEALTHMON_AG:
|
||||
case XFS_HEALTHMON_RTGROUP:
|
||||
if (existing->group == new->group){
|
||||
existing->grpmask |= new->grpmask;
|
||||
return true;
|
||||
goto out_merge;
|
||||
}
|
||||
return false;
|
||||
case XFS_HEALTHMON_INODE:
|
||||
if (existing->ino == new->ino &&
|
||||
existing->gen == new->gen) {
|
||||
existing->imask |= new->imask;
|
||||
return true;
|
||||
goto out_merge;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
@@ -224,18 +226,18 @@ xfs_healthmon_merge_events(
|
||||
case XFS_HEALTHMON_SHUTDOWN:
|
||||
/* yes, we can race to shutdown */
|
||||
existing->flags |= new->flags;
|
||||
return true;
|
||||
goto out_merge;
|
||||
|
||||
case XFS_HEALTHMON_MEDIA_ERROR:
|
||||
/* physically adjacent errors can merge */
|
||||
if (existing->daddr + existing->bbcount == new->daddr) {
|
||||
existing->bbcount += new->bbcount;
|
||||
return true;
|
||||
goto out_merge;
|
||||
}
|
||||
if (new->daddr + new->bbcount == existing->daddr) {
|
||||
existing->daddr = new->daddr;
|
||||
existing->bbcount += new->bbcount;
|
||||
return true;
|
||||
goto out_merge;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -250,63 +252,58 @@ xfs_healthmon_merge_events(
|
||||
|
||||
if (existing->fpos + existing->flen == new->fpos) {
|
||||
existing->flen += new->flen;
|
||||
return true;
|
||||
goto out_merge;
|
||||
}
|
||||
|
||||
if (new->fpos + new->flen == existing->fpos) {
|
||||
existing->fpos = new->fpos;
|
||||
existing->flen += new->flen;
|
||||
return true;
|
||||
goto out_merge;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
out_merge:
|
||||
trace_xfs_healthmon_merge(hm, existing);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Insert an event onto the start of the queue. */
|
||||
enum insert_where {
|
||||
INSERT_HEAD,
|
||||
INSERT_TAIL,
|
||||
};
|
||||
|
||||
/* Add an event onto the start or the end of the queue. */
|
||||
static inline void
|
||||
__xfs_healthmon_insert(
|
||||
struct xfs_healthmon *hm,
|
||||
enum insert_where where,
|
||||
struct xfs_healthmon_event *event)
|
||||
{
|
||||
struct timespec64 now;
|
||||
|
||||
ktime_get_coarse_real_ts64(&now);
|
||||
event->time_ns = (now.tv_sec * NSEC_PER_SEC) + now.tv_nsec;
|
||||
|
||||
event->next = hm->first_event;
|
||||
if (!hm->first_event)
|
||||
hm->first_event = event;
|
||||
if (!hm->last_event)
|
||||
hm->last_event = event;
|
||||
xfs_healthmon_bump_events(hm);
|
||||
wake_up(&hm->wait);
|
||||
|
||||
trace_xfs_healthmon_insert(hm, event);
|
||||
}
|
||||
|
||||
/* Push an event onto the end of the queue. */
|
||||
static inline void
|
||||
__xfs_healthmon_push(
|
||||
struct xfs_healthmon *hm,
|
||||
struct xfs_healthmon_event *event)
|
||||
{
|
||||
struct timespec64 now;
|
||||
lockdep_assert_held(&hm->lock);
|
||||
|
||||
ktime_get_coarse_real_ts64(&now);
|
||||
event->time_ns = (now.tv_sec * NSEC_PER_SEC) + now.tv_nsec;
|
||||
|
||||
if (!hm->first_event)
|
||||
hm->first_event = event;
|
||||
if (hm->last_event)
|
||||
hm->last_event->next = event;
|
||||
hm->last_event = event;
|
||||
event->next = NULL;
|
||||
switch (where) {
|
||||
case INSERT_HEAD:
|
||||
trace_xfs_healthmon_insert_head(hm, event);
|
||||
|
||||
list_add(&event->entry, &hm->event_list);
|
||||
break;
|
||||
case INSERT_TAIL:
|
||||
trace_xfs_healthmon_insert_tail(hm, event);
|
||||
|
||||
list_add_tail(&event->entry, &hm->event_list);
|
||||
break;
|
||||
}
|
||||
|
||||
xfs_healthmon_bump_events(hm);
|
||||
wake_up(&hm->wait);
|
||||
|
||||
trace_xfs_healthmon_push(hm, event);
|
||||
}
|
||||
|
||||
/* Deal with any previously lost events */
|
||||
@@ -321,8 +318,7 @@ xfs_healthmon_clear_lost_prev(
|
||||
};
|
||||
struct xfs_healthmon_event *event = NULL;
|
||||
|
||||
if (xfs_healthmon_merge_events(hm->last_event, &lost_event)) {
|
||||
trace_xfs_healthmon_merge(hm, hm->last_event);
|
||||
if (xfs_healthmon_merge_events(hm, &lost_event)) {
|
||||
wake_up(&hm->wait);
|
||||
goto cleared;
|
||||
}
|
||||
@@ -330,10 +326,12 @@ xfs_healthmon_clear_lost_prev(
|
||||
if (hm->events < XFS_HEALTHMON_MAX_EVENTS)
|
||||
event = kmemdup(&lost_event, sizeof(struct xfs_healthmon_event),
|
||||
GFP_NOFS);
|
||||
if (!event)
|
||||
if (!event) {
|
||||
xfs_healthmon_bump_lost(hm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
__xfs_healthmon_push(hm, event);
|
||||
__xfs_healthmon_insert(hm, INSERT_TAIL, event);
|
||||
cleared:
|
||||
hm->lost_prev_event = 0;
|
||||
return 0;
|
||||
@@ -369,8 +367,7 @@ xfs_healthmon_push(
|
||||
}
|
||||
|
||||
/* Try to merge with the newest event */
|
||||
if (xfs_healthmon_merge_events(hm->last_event, template)) {
|
||||
trace_xfs_healthmon_merge(hm, hm->last_event);
|
||||
if (xfs_healthmon_merge_events(hm, template)) {
|
||||
wake_up(&hm->wait);
|
||||
goto out_unlock;
|
||||
}
|
||||
@@ -387,7 +384,7 @@ xfs_healthmon_push(
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
__xfs_healthmon_push(hm, event);
|
||||
__xfs_healthmon_insert(hm, INSERT_TAIL, event);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&hm->lock);
|
||||
@@ -415,8 +412,10 @@ xfs_healthmon_unmount(
|
||||
* There's nothing actionable for userspace after an unmount. Once
|
||||
* we've inserted the unmount event, hm no longer owns that event.
|
||||
*/
|
||||
__xfs_healthmon_insert(hm, hm->unmount_event);
|
||||
mutex_lock(&hm->lock);
|
||||
__xfs_healthmon_insert(hm, INSERT_HEAD, hm->unmount_event);
|
||||
hm->unmount_event = NULL;
|
||||
mutex_unlock(&hm->lock);
|
||||
|
||||
xfs_healthmon_detach(hm);
|
||||
xfs_healthmon_put(hm);
|
||||
@@ -738,6 +737,13 @@ static const unsigned int type_map[] = {
|
||||
[XFS_HEALTHMON_DATALOST] = XFS_HEALTH_MONITOR_TYPE_DATALOST,
|
||||
};
|
||||
|
||||
static inline bool
|
||||
xfs_healthmon_check_outbuffer_space(const struct xfs_healthmon *hm)
|
||||
{
|
||||
return hm->bufhead + sizeof(struct xfs_health_monitor_event) <=
|
||||
hm->bufsize;
|
||||
}
|
||||
|
||||
/* Render event as a V0 structure */
|
||||
STATIC int
|
||||
xfs_healthmon_format_v0(
|
||||
@@ -804,10 +810,10 @@ xfs_healthmon_format_v0(
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT(hm->bufhead + sizeof(hme) <= hm->bufsize);
|
||||
ASSERT(xfs_healthmon_check_outbuffer_space(hm));
|
||||
|
||||
/* copy formatted object to the outbuf */
|
||||
if (hm->bufhead + sizeof(hme) <= hm->bufsize) {
|
||||
if (xfs_healthmon_check_outbuffer_space(hm)) {
|
||||
memcpy(hm->buffer + hm->bufhead, &hme, sizeof(hme));
|
||||
hm->bufhead += sizeof(hme);
|
||||
}
|
||||
@@ -890,15 +896,18 @@ xfs_healthmon_format_pop(
|
||||
{
|
||||
struct xfs_healthmon_event *event;
|
||||
|
||||
if (hm->bufhead + sizeof(*event) > hm->bufsize)
|
||||
/*
|
||||
* Don't bother if there's not enough space to format even one event in
|
||||
* the outbuffer.
|
||||
*/
|
||||
if (!xfs_healthmon_check_outbuffer_space(hm))
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&hm->lock);
|
||||
event = hm->first_event;
|
||||
event = list_first_entry_or_null(&hm->event_list,
|
||||
struct xfs_healthmon_event, entry);
|
||||
if (event) {
|
||||
if (hm->last_event == event)
|
||||
hm->last_event = NULL;
|
||||
hm->first_event = event->next;
|
||||
list_del_init(&event->entry);
|
||||
hm->events--;
|
||||
|
||||
trace_xfs_healthmon_pop(hm, event);
|
||||
@@ -1198,6 +1207,7 @@ xfs_ioc_health_monitor(
|
||||
return -ENOMEM;
|
||||
hm->dev = mp->m_super->s_dev;
|
||||
refcount_set(&hm->ref, 1);
|
||||
INIT_LIST_HEAD(&hm->event_list);
|
||||
|
||||
mutex_init(&hm->lock);
|
||||
init_waitqueue_head(&hm->wait);
|
||||
@@ -1213,7 +1223,9 @@ xfs_ioc_health_monitor(
|
||||
}
|
||||
running_event->type = XFS_HEALTHMON_RUNNING;
|
||||
running_event->domain = XFS_HEALTHMON_MOUNT;
|
||||
__xfs_healthmon_insert(hm, running_event);
|
||||
mutex_lock(&hm->lock);
|
||||
__xfs_healthmon_insert(hm, INSERT_HEAD, running_event);
|
||||
mutex_unlock(&hm->lock);
|
||||
|
||||
/*
|
||||
* Preallocate the unmount event so that we can't fail to notify the
|
||||
|
||||
@@ -31,8 +31,7 @@ struct xfs_healthmon {
|
||||
struct mutex lock;
|
||||
|
||||
/* list of event objects */
|
||||
struct xfs_healthmon_event *first_event;
|
||||
struct xfs_healthmon_event *last_event;
|
||||
struct list_head event_list;
|
||||
|
||||
/* preallocated event for unmount */
|
||||
struct xfs_healthmon_event *unmount_event;
|
||||
@@ -110,7 +109,7 @@ enum xfs_healthmon_domain {
|
||||
};
|
||||
|
||||
struct xfs_healthmon_event {
|
||||
struct xfs_healthmon_event *next;
|
||||
struct list_head entry;
|
||||
|
||||
enum xfs_healthmon_type type;
|
||||
enum xfs_healthmon_domain domain;
|
||||
|
||||
+2
-1
@@ -497,7 +497,8 @@ xfs_iget_cache_hit(
|
||||
struct xfs_inode *ip,
|
||||
xfs_ino_t ino,
|
||||
int flags,
|
||||
int lock_flags) __releases(RCU)
|
||||
int lock_flags)
|
||||
__releases_shared(RCU)
|
||||
{
|
||||
struct inode *inode = VFS_I(ip);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
|
||||
@@ -585,7 +585,6 @@ uint xfs_ilock_attr_map_shared(struct xfs_inode *);
|
||||
int xfs_ifree(struct xfs_trans *, struct xfs_inode *);
|
||||
int xfs_itruncate_extents_flags(struct xfs_trans **,
|
||||
struct xfs_inode *, int, xfs_fsize_t, int);
|
||||
void xfs_iext_realloc(xfs_inode_t *, int, int);
|
||||
|
||||
int xfs_log_force_inode(struct xfs_inode *ip);
|
||||
void xfs_iunpin_wait(xfs_inode_t *);
|
||||
|
||||
@@ -41,6 +41,8 @@ xfs_iomap_set_anon_write(
|
||||
iomap->offset = offset;
|
||||
iomap->length = length;
|
||||
iomap->flags = IOMAP_F_ANON_WRITE | IOMAP_F_DIRTY;
|
||||
if (bdev_has_integrity_csum(iomap->bdev))
|
||||
iomap->flags |= IOMAP_F_INTEGRITY;
|
||||
}
|
||||
|
||||
static inline xfs_filblks_t
|
||||
|
||||
+52
-26
@@ -422,6 +422,8 @@ out_error:
|
||||
static void
|
||||
xlog_state_shutdown_callbacks(
|
||||
struct xlog *log)
|
||||
__releases(&log->l_icloglock)
|
||||
__acquires(&log->l_icloglock)
|
||||
{
|
||||
struct xlog_in_core *iclog;
|
||||
LIST_HEAD(cb_list);
|
||||
@@ -470,6 +472,8 @@ xlog_state_release_iclog(
|
||||
struct xlog *log,
|
||||
struct xlog_in_core *iclog,
|
||||
struct xlog_ticket *ticket)
|
||||
__releases(&log->l_icloglock)
|
||||
__acquires(&log->l_icloglock)
|
||||
{
|
||||
bool last_ref;
|
||||
|
||||
@@ -744,13 +748,16 @@ xfs_log_mount_cancel(
|
||||
*/
|
||||
static inline int
|
||||
xlog_force_iclog(
|
||||
struct xlog *log,
|
||||
struct xlog_in_core *iclog)
|
||||
__releases(&log->l_icloglock)
|
||||
__acquires(&log->l_icloglock)
|
||||
{
|
||||
atomic_inc(&iclog->ic_refcnt);
|
||||
iclog->ic_flags |= XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA;
|
||||
if (iclog->ic_state == XLOG_STATE_ACTIVE)
|
||||
xlog_state_switch_iclogs(iclog->ic_log, iclog, 0);
|
||||
return xlog_state_release_iclog(iclog->ic_log, iclog, NULL);
|
||||
xlog_state_switch_iclogs(log, iclog, 0);
|
||||
return xlog_state_release_iclog(log, iclog, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -778,11 +785,10 @@ xlog_wait_iclog_completion(struct xlog *log)
|
||||
*/
|
||||
int
|
||||
xlog_wait_on_iclog(
|
||||
struct xlog *log,
|
||||
struct xlog_in_core *iclog)
|
||||
__releases(iclog->ic_log->l_icloglock)
|
||||
__releases(log->l_icloglock)
|
||||
{
|
||||
struct xlog *log = iclog->ic_log;
|
||||
|
||||
trace_xlog_iclog_wait_on(iclog, _RET_IP_);
|
||||
if (!xlog_is_shutdown(log) &&
|
||||
iclog->ic_state != XLOG_STATE_ACTIVE &&
|
||||
@@ -879,8 +885,8 @@ out_err:
|
||||
|
||||
spin_lock(&log->l_icloglock);
|
||||
iclog = log->l_iclog;
|
||||
error = xlog_force_iclog(iclog);
|
||||
xlog_wait_on_iclog(iclog);
|
||||
error = xlog_force_iclog(log, iclog);
|
||||
xlog_wait_on_iclog(log, iclog);
|
||||
|
||||
if (tic) {
|
||||
trace_xfs_log_umount_write(log, tic);
|
||||
@@ -1538,6 +1544,35 @@ xlog_bio_end_io(
|
||||
&iclog->ic_end_io_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* When using multiple devices, we also need to flush the data and RT device
|
||||
* caches first to ensure that all metadata writeback covered by the LSN in
|
||||
* this iclog is on stable storage. This is slow, but it *must* complete
|
||||
* before we issue the external log IO.
|
||||
*
|
||||
* If the flush fails, we cannot conclude that past metadata writeback from
|
||||
* the log succeeded. Repeating the flush is not possible, hence we must
|
||||
* shut down with log IO error to avoid shutdown re-entering this path and
|
||||
* erroring out again.
|
||||
*/
|
||||
static int
|
||||
xlog_flush_data_caches(
|
||||
struct xlog *log)
|
||||
{
|
||||
struct xfs_mount *mp = log->l_mp;
|
||||
|
||||
if (log->l_targ != mp->m_ddev_targp) {
|
||||
if (blkdev_issue_flush(mp->m_ddev_targp->bt_bdev))
|
||||
return -EIO;
|
||||
}
|
||||
if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp) {
|
||||
if (blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xlog_write_iclog(
|
||||
struct xlog *log,
|
||||
@@ -1582,21 +1617,9 @@ xlog_write_iclog(
|
||||
iclog->ic_bio.bi_private = iclog;
|
||||
|
||||
if (iclog->ic_flags & XLOG_ICL_NEED_FLUSH) {
|
||||
iclog->ic_bio.bi_opf |= REQ_PREFLUSH;
|
||||
/*
|
||||
* For external log devices, we also need to flush the data
|
||||
* device cache first to ensure all metadata writeback covered
|
||||
* by the LSN in this iclog is on stable storage. This is slow,
|
||||
* but it *must* complete before we issue the external log IO.
|
||||
*
|
||||
* If the flush fails, we cannot conclude that past metadata
|
||||
* writeback from the log succeeded. Repeating the flush is
|
||||
* not possible, hence we must shut down with log IO error to
|
||||
* avoid shutdown re-entering this path and erroring out again.
|
||||
*/
|
||||
if (log->l_targ != log->l_mp->m_ddev_targp &&
|
||||
blkdev_issue_flush(log->l_mp->m_ddev_targp->bt_bdev))
|
||||
if (xlog_flush_data_caches(log))
|
||||
goto shutdown;
|
||||
iclog->ic_bio.bi_opf |= REQ_PREFLUSH;
|
||||
}
|
||||
if (iclog->ic_flags & XLOG_ICL_NEED_FUA)
|
||||
iclog->ic_bio.bi_opf |= REQ_FUA;
|
||||
@@ -2741,14 +2764,17 @@ xlog_state_switch_iclogs(
|
||||
*/
|
||||
static int
|
||||
xlog_force_and_check_iclog(
|
||||
struct xlog *log,
|
||||
struct xlog_in_core *iclog,
|
||||
bool *completed)
|
||||
__releases(&log->l_icloglock)
|
||||
__acquires(&log->l_icloglock)
|
||||
{
|
||||
xfs_lsn_t lsn = be64_to_cpu(iclog->ic_header->h_lsn);
|
||||
int error;
|
||||
|
||||
*completed = false;
|
||||
error = xlog_force_iclog(iclog);
|
||||
error = xlog_force_iclog(log, iclog);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -2825,7 +2851,7 @@ xfs_log_force(
|
||||
/* We have exclusive access to this iclog. */
|
||||
bool completed;
|
||||
|
||||
if (xlog_force_and_check_iclog(iclog, &completed))
|
||||
if (xlog_force_and_check_iclog(log, iclog, &completed))
|
||||
goto out_error;
|
||||
|
||||
if (completed)
|
||||
@@ -2850,7 +2876,7 @@ xfs_log_force(
|
||||
iclog->ic_flags |= XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA;
|
||||
|
||||
if (flags & XFS_LOG_SYNC)
|
||||
return xlog_wait_on_iclog(iclog);
|
||||
return xlog_wait_on_iclog(log, iclog);
|
||||
out_unlock:
|
||||
spin_unlock(&log->l_icloglock);
|
||||
return 0;
|
||||
@@ -2920,7 +2946,7 @@ xlog_force_lsn(
|
||||
&log->l_icloglock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (xlog_force_and_check_iclog(iclog, &completed))
|
||||
if (xlog_force_and_check_iclog(log, iclog, &completed))
|
||||
goto out_error;
|
||||
if (log_flushed)
|
||||
*log_flushed = 1;
|
||||
@@ -2948,7 +2974,7 @@ xlog_force_lsn(
|
||||
}
|
||||
|
||||
if (flags & XFS_LOG_SYNC)
|
||||
return xlog_wait_on_iclog(iclog);
|
||||
return xlog_wait_on_iclog(log, iclog);
|
||||
out_unlock:
|
||||
spin_unlock(&log->l_icloglock);
|
||||
return 0;
|
||||
|
||||
@@ -105,8 +105,6 @@ int xfs_log_mount(struct xfs_mount *mp,
|
||||
int num_bblocks);
|
||||
int xfs_log_mount_finish(struct xfs_mount *mp);
|
||||
void xfs_log_mount_cancel(struct xfs_mount *);
|
||||
xfs_lsn_t xlog_assign_tail_lsn(struct xfs_mount *mp);
|
||||
xfs_lsn_t xlog_assign_tail_lsn_locked(struct xfs_mount *mp);
|
||||
void xfs_log_space_wake(struct xfs_mount *mp);
|
||||
int xfs_log_reserve(struct xfs_mount *mp, int length, int count,
|
||||
struct xlog_ticket **ticket, bool permanent);
|
||||
|
||||
@@ -1055,9 +1055,10 @@ xlog_cil_set_ctx_write_state(
|
||||
spin_unlock(&cil->xc_push_lock);
|
||||
|
||||
/*
|
||||
* Make sure the metadata we are about to overwrite in the log
|
||||
* has been flushed to stable storage before this iclog is
|
||||
* issued.
|
||||
* Flush the write cache before writing the start record so that
|
||||
* the metadata we are about to overwrite in the log and the
|
||||
* data that new allocations in this context refer to are
|
||||
* persisted to stable storage before this iclog is written.
|
||||
*/
|
||||
spin_lock(&cil->xc_log->l_icloglock);
|
||||
iclog->ic_flags |= XLOG_ICL_NEED_FLUSH;
|
||||
@@ -1556,7 +1557,7 @@ xlog_cil_push_work(
|
||||
* iclogs older than ic_prev. Hence we only need to wait
|
||||
* on the most recent older iclog here.
|
||||
*/
|
||||
xlog_wait_on_iclog(ctx->commit_iclog->ic_prev);
|
||||
xlog_wait_on_iclog(log, ctx->commit_iclog->ic_prev);
|
||||
spin_lock(&log->l_icloglock);
|
||||
}
|
||||
|
||||
@@ -1627,6 +1628,7 @@ out_abort_free_ticket:
|
||||
static void
|
||||
xlog_cil_push_background(
|
||||
struct xlog *log)
|
||||
__releases_shared(&log->l_cilp->xc_ctx_lock)
|
||||
{
|
||||
struct xfs_cil *cil = log->l_cilp;
|
||||
int space_used = atomic_read(&cil->xc_ctx->space_used);
|
||||
|
||||
@@ -605,8 +605,8 @@ xlog_wait(
|
||||
remove_wait_queue(wq, &wait);
|
||||
}
|
||||
|
||||
int xlog_wait_on_iclog(struct xlog_in_core *iclog)
|
||||
__releases(iclog->ic_log->l_icloglock);
|
||||
int xlog_wait_on_iclog(struct xlog *log, struct xlog_in_core *iclog)
|
||||
__releases(log->l_icloglock);
|
||||
|
||||
/* Calculate the distance between two LSNs in bytes */
|
||||
static inline uint64_t
|
||||
|
||||
@@ -520,7 +520,7 @@ xfs_mru_cache_lookup(
|
||||
if (elem) {
|
||||
list_del(&elem->list_node);
|
||||
_xfs_mru_cache_list_insert(mru, elem);
|
||||
__release(mru_lock); /* help sparse not be stupid */
|
||||
__release(&mru->lock);
|
||||
} else
|
||||
spin_unlock(&mru->lock);
|
||||
|
||||
|
||||
+2
-2
@@ -6139,8 +6139,8 @@ DEFINE_EVENT(xfs_healthmon_event_class, name, \
|
||||
TP_PROTO(const struct xfs_healthmon *hm, \
|
||||
const struct xfs_healthmon_event *event), \
|
||||
TP_ARGS(hm, event))
|
||||
DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_insert);
|
||||
DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_push);
|
||||
DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_insert_head);
|
||||
DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_insert_tail);
|
||||
DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_pop);
|
||||
DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_format);
|
||||
DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_format_overflow);
|
||||
|
||||
@@ -33,7 +33,7 @@ STATIC void
|
||||
xfs_ail_check(
|
||||
struct xfs_ail *ailp,
|
||||
struct xfs_log_item *lip)
|
||||
__must_hold(&ailp->ail_lock)
|
||||
__must_hold(&ailp->ail_lock)
|
||||
{
|
||||
struct xfs_log_item *prev_lip;
|
||||
struct xfs_log_item *next_lip;
|
||||
@@ -321,6 +321,7 @@ static void
|
||||
xfs_ail_delete(
|
||||
struct xfs_ail *ailp,
|
||||
struct xfs_log_item *lip)
|
||||
__must_hold(&ailp->ail_lock)
|
||||
{
|
||||
xfs_ail_check(ailp, lip);
|
||||
list_del(&lip->li_ail);
|
||||
@@ -899,6 +900,7 @@ xfs_lsn_t
|
||||
xfs_ail_delete_one(
|
||||
struct xfs_ail *ailp,
|
||||
struct xfs_log_item *lip)
|
||||
__must_hold(&ailp->ail_lock)
|
||||
{
|
||||
struct xfs_log_item *mlip = xfs_ail_min(ailp);
|
||||
xfs_lsn_t lsn = lip->li_lsn;
|
||||
|
||||
@@ -268,6 +268,8 @@ xfs_verify_media(
|
||||
struct xfs_buftarg *btp = NULL;
|
||||
struct bio *bio;
|
||||
struct folio *folio;
|
||||
xfs_daddr_t dev_start = 0;
|
||||
xfs_daddr_t dev_end = 0;
|
||||
xfs_daddr_t daddr;
|
||||
uint64_t bbcount;
|
||||
int error = 0;
|
||||
@@ -277,24 +279,33 @@ xfs_verify_media(
|
||||
switch (me->me_dev) {
|
||||
case XFS_DEV_DATA:
|
||||
btp = mp->m_ddev_targp;
|
||||
dev_end = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
|
||||
break;
|
||||
case XFS_DEV_LOG:
|
||||
if (mp->m_logdev_targp != mp->m_ddev_targp)
|
||||
if (mp->m_logdev_targp != mp->m_ddev_targp) {
|
||||
btp = mp->m_logdev_targp;
|
||||
dev_end = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks);
|
||||
}
|
||||
break;
|
||||
case XFS_DEV_RT:
|
||||
btp = mp->m_rtdev_targp;
|
||||
dev_start = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rtstart);
|
||||
dev_end = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rtstart +
|
||||
mp->m_sb.sb_rblocks);
|
||||
break;
|
||||
}
|
||||
if (!btp)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* If the caller told us to verify beyond the end of the disk, tell the
|
||||
* user exactly where that was.
|
||||
* If the caller told us to verify before the start or beyond the end
|
||||
* of the disk volume, tell the user exactly where the volume starts
|
||||
* and ends.
|
||||
*/
|
||||
if (me->me_end_daddr > btp->bt_nr_sectors)
|
||||
me->me_end_daddr = btp->bt_nr_sectors;
|
||||
if (me->me_end_daddr > dev_end)
|
||||
me->me_end_daddr = dev_end;
|
||||
if (me->me_start_daddr < dev_start)
|
||||
me->me_start_daddr = dev_start;
|
||||
|
||||
/* start and end have to be aligned to the lba size */
|
||||
if (!IS_ALIGNED(BBTOB(me->me_start_daddr | me->me_end_daddr),
|
||||
@@ -323,8 +334,7 @@ xfs_verify_media(
|
||||
* verifying.
|
||||
*/
|
||||
daddr = me->me_start_daddr;
|
||||
bbcount = min_t(sector_t, me->me_end_daddr, btp->bt_nr_sectors) -
|
||||
me->me_start_daddr;
|
||||
bbcount = me->me_end_daddr - me->me_start_daddr;
|
||||
|
||||
folio = xfs_verify_alloc_folio(xfs_verify_iosize(me, btp, bbcount));
|
||||
if (!folio)
|
||||
|
||||
@@ -475,6 +475,8 @@ static struct xfs_open_zone *
|
||||
xfs_try_open_zone(
|
||||
struct xfs_mount *mp,
|
||||
enum rw_hint write_hint)
|
||||
__releases(&mp->m_zone_info->zi_open_zones_lock)
|
||||
__acquires(&mp->m_zone_info->zi_open_zones_lock)
|
||||
{
|
||||
struct xfs_zone_info *zi = mp->m_zone_info;
|
||||
struct xfs_open_zone *oz;
|
||||
|
||||
@@ -85,13 +85,13 @@ xfs_zoned_add_available(
|
||||
struct xfs_zone_info *zi = mp->m_zone_info;
|
||||
struct xfs_zone_reservation *reservation;
|
||||
|
||||
if (list_empty_careful(&zi->zi_reclaim_reservations)) {
|
||||
xfs_add_freecounter(mp, XC_FREE_RTAVAILABLE, count_fsb);
|
||||
spin_lock(&zi->zi_reservation_lock);
|
||||
xfs_add_freecounter(mp, XC_FREE_RTAVAILABLE, count_fsb);
|
||||
if (list_empty(&zi->zi_reclaim_reservations)) {
|
||||
spin_unlock(&zi->zi_reservation_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&zi->zi_reservation_lock);
|
||||
xfs_add_freecounter(mp, XC_FREE_RTAVAILABLE, count_fsb);
|
||||
count_fsb = xfs_sum_freecounter(mp, XC_FREE_RTAVAILABLE);
|
||||
list_for_each_entry(reservation, &zi->zi_reclaim_reservations, entry) {
|
||||
if (reservation->count_fsb > count_fsb)
|
||||
|
||||
Reference in New Issue
Block a user