mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 01:32:21 +02:00
xfs: fixes for v7.2-rc4
Signed-off-by: Carlos Maiolino <cem@kernel.org> -----BEGIN PGP SIGNATURE----- iJUEABMJAB0WIQSmtYVZ/MfVMGUq1GNcsMJ8RxYuYwUCalj/DwAKCRBcsMJ8RxYu Y3UMAYCrPzc41SGZdOkoxvO5g3BwGISXYCiCCIFFvnnDXztuiJytgZ+VPH+C7X4J tlGjRXUBfi8PcoePIbDfPXxay+T/g5Pz29ADyl2UAkxPYRRWeYYxKTyCTBvBdUXS lMCnv0pSPg== =Hcay -----END PGP SIGNATURE----- Merge tag 'xfs-fixes-7.2-rc4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux Pull xfs fixes from Carlos Maiolino: "This contains mostly a series of bug fixes found by different LLM models" * tag 'xfs-fixes-7.2-rc4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: (21 commits) xfs: don't zap bmbt forks if they are MAXLEVELS tall xfs: clamp timestamp nanoseconds correctly xfs: fully check the parent handle when it points to the rootdir xfs: handle non-inode owners for rtrmap record checking xfs: fix off-by-one error when calling xchk_xref_has_rt_owner xfs: set xfarray killable sort correctly xfs: grab rtrmap btree when checking rgsuper xfs: write the rg superblock when fixing it xfs: use the rt version of the cow staging checker xfs: use rtrefcount btree cursor in xchk_xref_is_rt_cow_staging xfs: don't wrap around quota ids in dqiterate xfs: move cow_replace_mapping to xfs_bmap_util.c xfs: make cow repair somewhat flaky when debugging knob enabled xfs: don't replace the wrong part of the cow fork xfs: resample the data fork mapping after cycling ILOCK xfs: fix null pointer dereference in tracepoint xfs: use xfs_csn_t for xlog_cil_push_now() push_seq parameter xfs: tie zoned sysfs lifetime to zone info xfs: fail recovery on a committed log item with no regions xfs: splice unsorted log items back to the transaction after the loop ...
This commit is contained in:
commit
e22254e9dd
|
|
@ -878,7 +878,7 @@ xfs_defer_add_barrier(
|
|||
if (dfp)
|
||||
return;
|
||||
|
||||
xfs_defer_alloc(&tp->t_dfops, &xfs_barrier_defer_type);
|
||||
dfp = xfs_defer_alloc(&tp->t_dfops, &xfs_barrier_defer_type);
|
||||
|
||||
trace_xfs_defer_add_item(tp->t_mountp, dfp, NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "xfs_rtalloc.h"
|
||||
#include "xfs_rtbitmap.h"
|
||||
#include "xfs_rtgroup.h"
|
||||
#include "xfs_bmap_util.h"
|
||||
#include "scrub/xfs_scrub.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/common.h"
|
||||
|
|
@ -80,12 +81,6 @@ struct xrep_cow {
|
|||
unsigned int next_bno;
|
||||
};
|
||||
|
||||
/* CoW staging extent. */
|
||||
struct xrep_cow_extent {
|
||||
xfs_fsblock_t fsbno;
|
||||
xfs_extlen_t len;
|
||||
};
|
||||
|
||||
/*
|
||||
* Mark the part of the file range that corresponds to the given physical
|
||||
* space. Caller must ensure that the physical range is within xc->irec.
|
||||
|
|
@ -230,6 +225,29 @@ xrep_cow_mark_missing_staging_rmap(
|
|||
xfs_gbno_to_fsb(cur->bc_group, rec_bno), rec_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Trim the start and end of the current mapping by up to 1/4 of the length
|
||||
* and mark that as "bad" to test the cow fork repair mechanism.
|
||||
*/
|
||||
static inline int
|
||||
xrep_cow_debug_replacement(
|
||||
struct xrep_cow *xc)
|
||||
{
|
||||
xfs_fsblock_t fsbno = xc->irec.br_startblock;
|
||||
xfs_extlen_t len = xc->irec.br_blockcount;
|
||||
uint32_t trim;
|
||||
|
||||
/* get_random_u32_below requires a nonzero argument */
|
||||
trim = len > 4 ? get_random_u32_below(len / 4) : 0;
|
||||
len -= trim;
|
||||
|
||||
trim = len > 4 ? get_random_u32_below(len / 4) : 0;
|
||||
fsbno += trim;
|
||||
len -= trim;
|
||||
|
||||
return xrep_cow_mark_file_range(xc, fsbno, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find any part of the CoW fork mapping that isn't a single-owner CoW staging
|
||||
* extent and mark the corresponding part of the file range in the bitmap.
|
||||
|
|
@ -299,8 +317,9 @@ xrep_cow_find_bad(
|
|||
* If userspace is forcing us to rebuild the CoW fork or someone turned
|
||||
* on the debugging knob, replace everything in the CoW fork.
|
||||
*/
|
||||
if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) ||
|
||||
XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
|
||||
if (XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
|
||||
error = xrep_cow_debug_replacement(xc);
|
||||
else if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD)
|
||||
error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock,
|
||||
xc->irec.br_blockcount);
|
||||
|
||||
|
|
@ -381,8 +400,9 @@ xrep_cow_find_bad_rt(
|
|||
* turned on the debugging knob, replace everything in the
|
||||
* CoW fork and then scan for staging extents in the refcountbt.
|
||||
*/
|
||||
if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) ||
|
||||
XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
|
||||
if (XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
|
||||
error = xrep_cow_debug_replacement(xc);
|
||||
else if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD)
|
||||
error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock,
|
||||
xc->irec.br_blockcount);
|
||||
|
||||
|
|
@ -401,22 +421,21 @@ xrep_cow_find_bad_rt(
|
|||
STATIC int
|
||||
xrep_cow_alloc(
|
||||
struct xfs_scrub *sc,
|
||||
xfs_extlen_t maxlen,
|
||||
struct xrep_cow_extent *repl)
|
||||
struct xfs_bmbt_irec *del)
|
||||
{
|
||||
struct xfs_alloc_arg args = {
|
||||
.tp = sc->tp,
|
||||
.mp = sc->mp,
|
||||
.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE,
|
||||
.minlen = 1,
|
||||
.maxlen = maxlen,
|
||||
.maxlen = del->br_blockcount,
|
||||
.prod = 1,
|
||||
.resv = XFS_AG_RESV_NONE,
|
||||
.datatype = XFS_ALLOC_USERDATA,
|
||||
};
|
||||
int error;
|
||||
|
||||
error = xfs_trans_reserve_more(sc->tp, maxlen, 0);
|
||||
error = xfs_trans_reserve_more(sc->tp, del->br_blockcount, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
|
@ -428,8 +447,8 @@ xrep_cow_alloc(
|
|||
|
||||
xfs_refcount_alloc_cow_extent(sc->tp, false, args.fsbno, args.len);
|
||||
|
||||
repl->fsbno = args.fsbno;
|
||||
repl->len = args.len;
|
||||
del->br_startblock = args.fsbno;
|
||||
del->br_blockcount = args.len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -440,10 +459,12 @@ xrep_cow_alloc(
|
|||
STATIC int
|
||||
xrep_cow_alloc_rt(
|
||||
struct xfs_scrub *sc,
|
||||
xfs_extlen_t maxlen,
|
||||
struct xrep_cow_extent *repl)
|
||||
struct xfs_bmbt_irec *del)
|
||||
{
|
||||
xfs_rtxlen_t maxrtx = xfs_rtb_to_rtx(sc->mp, maxlen);
|
||||
xfs_fsblock_t fsbno;
|
||||
xfs_rtxlen_t maxrtx =
|
||||
min(U32_MAX, xfs_blen_to_rtbxlen(sc->mp, del->br_blockcount));
|
||||
xfs_extlen_t len;
|
||||
int error;
|
||||
|
||||
error = xfs_trans_reserve_more(sc->tp, 0, maxrtx);
|
||||
|
|
@ -451,11 +472,14 @@ xrep_cow_alloc_rt(
|
|||
return error;
|
||||
|
||||
error = xfs_rtallocate_rtgs(sc->tp, NULLRTBLOCK, 1, maxrtx, 1, false,
|
||||
false, &repl->fsbno, &repl->len);
|
||||
false, &fsbno, &len);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_refcount_alloc_cow_extent(sc->tp, true, repl->fsbno, repl->len);
|
||||
xfs_refcount_alloc_cow_extent(sc->tp, true, fsbno, len);
|
||||
|
||||
del->br_startblock = fsbno;
|
||||
del->br_blockcount = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -469,19 +493,19 @@ static inline int
|
|||
xrep_cow_find_mapping(
|
||||
struct xrep_cow *xc,
|
||||
struct xfs_iext_cursor *icur,
|
||||
xfs_fileoff_t startoff,
|
||||
struct xfs_bmbt_irec *got)
|
||||
xfs_fileoff_t badoff,
|
||||
xfs_extlen_t badlen,
|
||||
struct xfs_bmbt_irec *got,
|
||||
struct xfs_bmbt_irec *rep)
|
||||
{
|
||||
struct xfs_inode *ip = xc->sc->ip;
|
||||
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
|
||||
|
||||
if (!xfs_iext_lookup_extent(ip, ifp, startoff, icur, got))
|
||||
if (!xfs_iext_lookup_extent(ip, ifp, badoff, icur, got))
|
||||
goto bad;
|
||||
memcpy(rep, got, sizeof(*rep));
|
||||
|
||||
if (got->br_startoff > startoff)
|
||||
goto bad;
|
||||
|
||||
if (got->br_blockcount == 0)
|
||||
if (got->br_startoff > badoff)
|
||||
goto bad;
|
||||
|
||||
if (isnullstartblock(got->br_startblock))
|
||||
|
|
@ -490,58 +514,30 @@ xrep_cow_find_mapping(
|
|||
if (xfs_bmap_is_written_extent(got))
|
||||
goto bad;
|
||||
|
||||
if (got->br_startoff < badoff) {
|
||||
const int64_t delta = badoff - got->br_startoff;
|
||||
|
||||
rep->br_blockcount -= delta;
|
||||
rep->br_startoff += delta;
|
||||
rep->br_startblock += delta;
|
||||
}
|
||||
|
||||
if (got->br_startoff + got->br_blockcount > badoff + badlen) {
|
||||
const int64_t delta = (got->br_startoff + got->br_blockcount) -
|
||||
(badoff + badlen);
|
||||
|
||||
rep->br_blockcount -= delta;
|
||||
}
|
||||
|
||||
if (got->br_blockcount == 0)
|
||||
goto bad;
|
||||
|
||||
return 0;
|
||||
bad:
|
||||
ASSERT(0);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
#define REPLACE_LEFT_SIDE (1U << 0)
|
||||
#define REPLACE_RIGHT_SIDE (1U << 1)
|
||||
|
||||
/*
|
||||
* Given a CoW fork mapping @got and a replacement mapping @repl, remap the
|
||||
* beginning of @got with the space described by @rep.
|
||||
*/
|
||||
static inline void
|
||||
xrep_cow_replace_mapping(
|
||||
struct xfs_inode *ip,
|
||||
struct xfs_iext_cursor *icur,
|
||||
const struct xfs_bmbt_irec *got,
|
||||
const struct xrep_cow_extent *repl)
|
||||
{
|
||||
struct xfs_bmbt_irec new = *got; /* struct copy */
|
||||
|
||||
ASSERT(repl->len > 0);
|
||||
ASSERT(!isnullstartblock(got->br_startblock));
|
||||
|
||||
trace_xrep_cow_replace_mapping(ip, got, repl->fsbno, repl->len);
|
||||
|
||||
if (got->br_blockcount == repl->len) {
|
||||
/*
|
||||
* The new extent is a complete replacement for the existing
|
||||
* extent. Update the COW fork record.
|
||||
*/
|
||||
new.br_startblock = repl->fsbno;
|
||||
xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The new extent can replace the beginning of the COW fork record.
|
||||
* Move the left side of @got upwards, then insert the new record.
|
||||
*/
|
||||
new.br_startoff += repl->len;
|
||||
new.br_startblock += repl->len;
|
||||
new.br_blockcount -= repl->len;
|
||||
xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
|
||||
|
||||
new.br_startoff = got->br_startoff;
|
||||
new.br_startblock = repl->fsbno;
|
||||
new.br_blockcount = repl->len;
|
||||
xfs_iext_insert(ip, icur, &new, BMAP_COWFORK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace the unwritten CoW staging extent backing the given file range with a
|
||||
* new space extent that isn't as problematic.
|
||||
|
|
@ -553,33 +549,30 @@ xrep_cow_replace_range(
|
|||
xfs_extlen_t *blockcount)
|
||||
{
|
||||
struct xfs_iext_cursor icur;
|
||||
struct xrep_cow_extent repl;
|
||||
struct xfs_bmbt_irec got;
|
||||
struct xfs_bmbt_irec got, rep;
|
||||
struct xfs_scrub *sc = xc->sc;
|
||||
xfs_fileoff_t nextoff;
|
||||
xfs_extlen_t alloc_len;
|
||||
xfs_fsblock_t old_fsbno;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Put the existing CoW fork mapping in @got. If @got ends before
|
||||
* @rep, truncate @rep so we only replace one extent mapping at a time.
|
||||
* Put the existing CoW fork mapping in @got, and put in @rep the
|
||||
* contents of @got trimmed to @startoff/@blockcount. We only want
|
||||
* to replace the bad region, and only one mapping at a time.
|
||||
*/
|
||||
error = xrep_cow_find_mapping(xc, &icur, startoff, &got);
|
||||
error = xrep_cow_find_mapping(xc, &icur, startoff, *blockcount, &got,
|
||||
&rep);
|
||||
if (error)
|
||||
return error;
|
||||
nextoff = min(startoff + *blockcount,
|
||||
got.br_startoff + got.br_blockcount);
|
||||
old_fsbno = rep.br_startblock;
|
||||
|
||||
/*
|
||||
* Allocate a replacement extent. If we don't fill all the blocks,
|
||||
* shorten the quantity that will be deleted in this step.
|
||||
*/
|
||||
alloc_len = min_t(xfs_fileoff_t, XFS_MAX_BMBT_EXTLEN,
|
||||
nextoff - startoff);
|
||||
if (XFS_IS_REALTIME_INODE(sc->ip))
|
||||
error = xrep_cow_alloc_rt(sc, alloc_len, &repl);
|
||||
error = xrep_cow_alloc_rt(sc, &rep);
|
||||
else
|
||||
error = xrep_cow_alloc(sc, alloc_len, &repl);
|
||||
error = xrep_cow_alloc(sc, &rep);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
|
@ -587,7 +580,7 @@ xrep_cow_replace_range(
|
|||
* Replace the old mapping with the new one, and commit the metadata
|
||||
* changes made so far.
|
||||
*/
|
||||
xrep_cow_replace_mapping(sc->ip, &icur, &got, &repl);
|
||||
xfs_bmap_replace_cow_mapping(sc->ip, &icur, &got, &rep);
|
||||
|
||||
xfs_inode_set_cowblocks_tag(sc->ip);
|
||||
error = xfs_defer_finish(&sc->tp);
|
||||
|
|
@ -596,15 +589,15 @@ xrep_cow_replace_range(
|
|||
|
||||
/* Note the old CoW staging extents; we'll reap them all later. */
|
||||
if (XFS_IS_REALTIME_INODE(sc->ip))
|
||||
error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks,
|
||||
got.br_startblock, repl.len);
|
||||
error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks, old_fsbno,
|
||||
rep.br_blockcount);
|
||||
else
|
||||
error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks,
|
||||
got.br_startblock, repl.len);
|
||||
error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks, old_fsbno,
|
||||
rep.br_blockcount);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
*blockcount = repl.len;
|
||||
*blockcount = rep.br_blockcount;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -383,6 +383,14 @@ xchk_dirpath_step_up(
|
|||
goto out_scanlock;
|
||||
}
|
||||
|
||||
/* The handle encoded in the parent pointer must match. */
|
||||
if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) {
|
||||
trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr,
|
||||
path->nr_steps, &dl->xname, &dl->pptr_rec);
|
||||
error = -EFSCORRUPTED;
|
||||
goto out_scanlock;
|
||||
}
|
||||
|
||||
/* We've reached the root directory; the path is ok. */
|
||||
if (parent_ino == dl->root_ino) {
|
||||
xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_OK);
|
||||
|
|
@ -411,14 +419,6 @@ xchk_dirpath_step_up(
|
|||
goto out_scanlock;
|
||||
}
|
||||
|
||||
/* The handle encoded in the parent pointer must match. */
|
||||
if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) {
|
||||
trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr,
|
||||
path->nr_steps, &dl->xname, &dl->pptr_rec);
|
||||
error = -EFSCORRUPTED;
|
||||
goto out_scanlock;
|
||||
}
|
||||
|
||||
/* Parent pointer must point up to a directory. */
|
||||
if (!S_ISDIR(VFS_I(dp)->i_mode)) {
|
||||
trace_xchk_dirpath_nondir_parent(dl->sc, dp, path->path_nr,
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ xchk_dquot_iter(
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
cursor->id = dq->q_id + 1;
|
||||
cursor->id = (uint64_t)dq->q_id + 1;
|
||||
*dqpp = dq;
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -921,7 +921,7 @@ xrep_dinode_bad_bmbt_fork(
|
|||
|
||||
if (nrecs == 0 || xfs_bmdr_space_calc(nrecs) > dfork_size)
|
||||
return true;
|
||||
if (level == 0 || level >= XFS_BM_MAXLEVELS(sc->mp, whichfork))
|
||||
if (level == 0 || level > XFS_BM_MAXLEVELS(sc->mp, whichfork))
|
||||
return true;
|
||||
|
||||
dmxr = xfs_bmdr_maxrecs(dfork_size, 0);
|
||||
|
|
@ -1757,7 +1757,7 @@ xrep_clamp_timestamp(
|
|||
struct xfs_inode *ip,
|
||||
struct timespec64 *ts)
|
||||
{
|
||||
ts->tv_nsec = clamp_t(long, ts->tv_nsec, 0, NSEC_PER_SEC);
|
||||
ts->tv_nsec = clamp_t(long, ts->tv_nsec, 0, NSEC_PER_SEC - 1);
|
||||
*ts = timestamp_truncate(*ts, VFS_I(ip));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ int
|
|||
xchk_setup_rgsuperblock(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
if (xchk_need_intent_drain(sc))
|
||||
xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
|
||||
return xchk_trans_alloc(sc, 0);
|
||||
}
|
||||
|
||||
|
|
@ -43,6 +45,7 @@ xchk_rgsuperblock(
|
|||
struct xfs_scrub *sc)
|
||||
{
|
||||
xfs_rgnumber_t rgno = sc->sm->sm_agno;
|
||||
unsigned int flags;
|
||||
int error;
|
||||
|
||||
/*
|
||||
|
|
@ -63,7 +66,12 @@ xchk_rgsuperblock(
|
|||
if (!xchk_xref_process_error(sc, 0, 0, &error))
|
||||
return error;
|
||||
|
||||
error = xchk_rtgroup_lock(sc, &sc->sr, XFS_RTGLOCK_BITMAP_SHARED);
|
||||
if (xfs_has_rtrmapbt(sc->mp))
|
||||
flags = XFS_RTGLOCK_BITMAP | XFS_RTGLOCK_RMAP;
|
||||
else
|
||||
flags = XFS_RTGLOCK_BITMAP_SHARED;
|
||||
|
||||
error = xchk_rtgroup_lock(sc, &sc->sr, flags);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
|
@ -80,9 +88,13 @@ int
|
|||
xrep_rgsuperblock(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
struct xfs_buf *sb_bp;
|
||||
|
||||
ASSERT(rtg_rgno(sc->sr.rtg) == 0);
|
||||
|
||||
sb_bp = xfs_trans_getsb(sc->tp);
|
||||
xfs_log_sb(sc->tp);
|
||||
xfs_log_rtsb(sc->tp, sb_bp);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_XFS_ONLINE_REPAIR */
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ xchk_rtbitmap(
|
|||
* the last free extent we saw and the last possible extent in the rt
|
||||
* group.
|
||||
*/
|
||||
last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize - 1;
|
||||
last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize;
|
||||
if (rtb->next_free_rgbno < last_rgbno)
|
||||
xchk_xref_has_rt_owner(sc, rtb->next_free_rgbno,
|
||||
last_rgbno - rtb->next_free_rgbno);
|
||||
|
|
|
|||
|
|
@ -607,7 +607,7 @@ xchk_xref_is_rt_cow_staging(
|
|||
|
||||
/* CoW lookup returned a shared extent record? */
|
||||
if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -87,6 +87,9 @@ xchk_rtrmapbt_is_shareable(
|
|||
return false;
|
||||
if (irec->rm_flags & XFS_RMAP_UNWRITTEN)
|
||||
return false;
|
||||
if (irec->rm_owner == XFS_RMAP_OWN_COW ||
|
||||
irec->rm_owner == XFS_RMAP_OWN_FS)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -146,6 +149,9 @@ xchk_rtrmap_mergeable(
|
|||
return false;
|
||||
if (r1->rm_flags != r2->rm_flags)
|
||||
return false;
|
||||
if (r1->rm_owner == XFS_RMAP_OWN_COW ||
|
||||
r1->rm_owner == XFS_RMAP_OWN_FS)
|
||||
return true;
|
||||
return r1->rm_offset + r1->rm_blockcount == r2->rm_offset;
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +215,7 @@ xchk_rtrmapbt_xref(
|
|||
xfs_rgbno_to_rtb(sc->sr.rtg, irec->rm_startblock),
|
||||
irec->rm_blockcount);
|
||||
if (irec->rm_owner == XFS_RMAP_OWN_COW)
|
||||
xchk_xref_is_cow_staging(sc, irec->rm_startblock,
|
||||
xchk_xref_is_rt_cow_staging(sc, irec->rm_startblock,
|
||||
irec->rm_blockcount);
|
||||
else
|
||||
xchk_rtrmapbt_xref_rtrefc(sc, irec);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ struct xfs_scrub;
|
|||
struct xchk_relax {
|
||||
unsigned long next_resched;
|
||||
unsigned int resched_nr;
|
||||
bool interruptible;
|
||||
bool killable;
|
||||
};
|
||||
|
||||
/* Yield to the scheduler at most 10x per second. */
|
||||
|
|
@ -21,7 +21,7 @@ struct xchk_relax {
|
|||
(struct xchk_relax){ \
|
||||
.next_resched = XCHK_RELAX_NEXT, \
|
||||
.resched_nr = 0, \
|
||||
.interruptible = true, \
|
||||
.killable = true, \
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -45,7 +45,7 @@ static inline int xchk_maybe_relax(struct xchk_relax *widget)
|
|||
widget->next_resched = XCHK_RELAX_NEXT;
|
||||
}
|
||||
|
||||
if (widget->interruptible && fatal_signal_pending(current))
|
||||
if (widget->killable && fatal_signal_pending(current))
|
||||
return -EINTR;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -2671,41 +2671,6 @@ TRACE_EVENT(xrep_cow_mark_file_range,
|
|||
__entry->blockcount)
|
||||
);
|
||||
|
||||
TRACE_EVENT(xrep_cow_replace_mapping,
|
||||
TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *irec,
|
||||
xfs_fsblock_t new_startblock, xfs_extlen_t new_blockcount),
|
||||
TP_ARGS(ip, irec, new_startblock, new_blockcount),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(xfs_ino_t, ino)
|
||||
__field(xfs_fsblock_t, startblock)
|
||||
__field(xfs_fileoff_t, startoff)
|
||||
__field(xfs_filblks_t, blockcount)
|
||||
__field(xfs_exntst_t, state)
|
||||
__field(xfs_fsblock_t, new_startblock)
|
||||
__field(xfs_extlen_t, new_blockcount)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dev = ip->i_mount->m_super->s_dev;
|
||||
__entry->ino = I_INO(ip);
|
||||
__entry->startoff = irec->br_startoff;
|
||||
__entry->startblock = irec->br_startblock;
|
||||
__entry->blockcount = irec->br_blockcount;
|
||||
__entry->state = irec->br_state;
|
||||
__entry->new_startblock = new_startblock;
|
||||
__entry->new_blockcount = new_blockcount;
|
||||
),
|
||||
TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startblock 0x%llx new_fsbcount 0x%x",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->ino,
|
||||
__entry->startoff,
|
||||
__entry->startblock,
|
||||
__entry->blockcount,
|
||||
__entry->state,
|
||||
__entry->new_startblock,
|
||||
__entry->new_blockcount)
|
||||
);
|
||||
|
||||
TRACE_EVENT(xrep_cow_free_staging,
|
||||
TP_PROTO(const struct xfs_perag *pag, xfs_agblock_t agbno,
|
||||
xfs_extlen_t blockcount),
|
||||
|
|
|
|||
|
|
@ -487,8 +487,7 @@ xfarray_sortinfo_alloc(
|
|||
xfarray_sortinfo_lo(si)[0] = 0;
|
||||
xfarray_sortinfo_hi(si)[0] = array->nr - 1;
|
||||
si->relax = INIT_XCHK_RELAX;
|
||||
if (flags & XFARRAY_SORT_KILLABLE)
|
||||
si->relax.interruptible = false;
|
||||
si->relax.killable = !!(flags & XFARRAY_SORT_KILLABLE);
|
||||
|
||||
trace_xfarray_sort(si, nr_bytes);
|
||||
*infop = si;
|
||||
|
|
|
|||
|
|
@ -1744,3 +1744,92 @@ xfs_swap_extents(
|
|||
xfs_trans_cancel(tp);
|
||||
goto out_unlock_ilock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a CoW fork mapping @got and a replacement mapping @rep, map the space
|
||||
* described by @rep into the cow fork, pushing aside @got as necessary. @icur
|
||||
* must point to iext tree leaf containing @got.
|
||||
*/
|
||||
void
|
||||
xfs_bmap_replace_cow_mapping(
|
||||
struct xfs_inode *ip,
|
||||
struct xfs_iext_cursor *icur,
|
||||
struct xfs_bmbt_irec *got,
|
||||
struct xfs_bmbt_irec *rep)
|
||||
{
|
||||
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
|
||||
xfs_fileoff_t rep_endoff =
|
||||
rep->br_startoff + rep->br_blockcount;
|
||||
xfs_fileoff_t got_endoff =
|
||||
got->br_startoff + got->br_blockcount;
|
||||
uint32_t state = BMAP_COWFORK;
|
||||
|
||||
ASSERT(rep->br_blockcount > 0);
|
||||
ASSERT(!isnullstartblock(got->br_startblock));
|
||||
ASSERT(got->br_startoff <= rep->br_startoff);
|
||||
ASSERT(got_endoff >= rep_endoff);
|
||||
|
||||
trace_xfs_bmap_replace_cow_mapping(ip, got, rep);
|
||||
|
||||
if (got->br_startoff == rep->br_startoff)
|
||||
state |= BMAP_LEFT_FILLING;
|
||||
if (got_endoff == rep_endoff)
|
||||
state |= BMAP_RIGHT_FILLING;
|
||||
|
||||
switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
|
||||
case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
|
||||
/*
|
||||
* Replacement matches the whole mapping, update the record.
|
||||
*/
|
||||
xfs_iext_update_extent(ip, state, icur, rep);
|
||||
break;
|
||||
case BMAP_LEFT_FILLING:
|
||||
/*
|
||||
* Replace the first part of the mapping: Update the cursor
|
||||
* position with the new mapping, then add a record with the
|
||||
* tail of the old mapping.
|
||||
*/
|
||||
got->br_startoff = rep_endoff;
|
||||
got->br_blockcount -= rep->br_blockcount;
|
||||
got->br_startblock += rep->br_blockcount;
|
||||
|
||||
xfs_iext_update_extent(ip, state, icur, rep);
|
||||
xfs_iext_next(ifp, icur);
|
||||
xfs_iext_insert(ip, icur, got, state);
|
||||
break;
|
||||
case BMAP_RIGHT_FILLING:
|
||||
/*
|
||||
* Replacing the last part of the mapping. Shorten the current
|
||||
* mapping then add a record with the new mapping.
|
||||
*/
|
||||
got->br_blockcount -= rep->br_blockcount;
|
||||
|
||||
xfs_iext_update_extent(ip, state, icur, got);
|
||||
xfs_iext_next(ifp, icur);
|
||||
xfs_iext_insert(ip, icur, rep, state);
|
||||
break;
|
||||
case 0:
|
||||
/*
|
||||
* Replacing the middle of the extent. Shorten the current
|
||||
* mapping, add a new record with the new mapping, and add a
|
||||
* second new record with the tail of the old mapping.
|
||||
*/
|
||||
got->br_blockcount = rep->br_startoff - got->br_startoff;
|
||||
|
||||
struct xfs_bmbt_irec new = {
|
||||
.br_startoff = rep_endoff,
|
||||
.br_blockcount = got_endoff - rep_endoff,
|
||||
.br_state = got->br_state,
|
||||
.br_startblock = got->br_startblock +
|
||||
rep->br_blockcount +
|
||||
got->br_blockcount,
|
||||
};
|
||||
|
||||
xfs_iext_update_extent(ip, state, icur, got);
|
||||
xfs_iext_next(ifp, icur);
|
||||
xfs_iext_insert(ip, icur, rep, state);
|
||||
xfs_iext_next(ifp, icur);
|
||||
xfs_iext_insert(ip, icur, &new, state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,4 +81,8 @@ int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
|
|||
int xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
|
||||
xfs_off_t len);
|
||||
|
||||
void xfs_bmap_replace_cow_mapping(struct xfs_inode *ip,
|
||||
struct xfs_iext_cursor *icur, struct xfs_bmbt_irec *got,
|
||||
struct xfs_bmbt_irec *rep);
|
||||
|
||||
#endif /* __XFS_BMAP_UTIL_H__ */
|
||||
|
|
|
|||
|
|
@ -1710,7 +1710,7 @@ xlog_cil_push_background(
|
|||
static void
|
||||
xlog_cil_push_now(
|
||||
struct xlog *log,
|
||||
xfs_lsn_t push_seq,
|
||||
xfs_csn_t push_seq,
|
||||
bool async)
|
||||
{
|
||||
struct xfs_cil *cil = log->l_cilp;
|
||||
|
|
|
|||
|
|
@ -1907,18 +1907,20 @@ xlog_recover_reorder_trans(
|
|||
list_for_each_entry_safe(item, n, &sort_list, ri_list) {
|
||||
enum xlog_recover_reorder fate = XLOG_REORDER_ITEM_LIST;
|
||||
|
||||
/* a committed item with no regions has a NULL ri_buf[0] */
|
||||
if (!item->ri_cnt || !item->ri_buf) {
|
||||
xfs_warn(log->l_mp,
|
||||
"%s: committed log item has no regions",
|
||||
__func__);
|
||||
error = -EFSCORRUPTED;
|
||||
break;
|
||||
}
|
||||
|
||||
item->ri_ops = xlog_find_item_ops(item);
|
||||
if (!item->ri_ops) {
|
||||
xfs_warn(log->l_mp,
|
||||
"%s: unrecognized type of log operation (%d)",
|
||||
__func__, ITEM_TYPE(item));
|
||||
ASSERT(0);
|
||||
/*
|
||||
* return the remaining items back to the transaction
|
||||
* item list so they can be freed in caller.
|
||||
*/
|
||||
if (!list_empty(&sort_list))
|
||||
list_splice_init(&sort_list, &trans->r_itemq);
|
||||
error = -EFSCORRUPTED;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1946,7 +1948,15 @@ xlog_recover_reorder_trans(
|
|||
}
|
||||
}
|
||||
|
||||
ASSERT(list_empty(&sort_list));
|
||||
/*
|
||||
* Return the remaining items back to the transaction item list so they
|
||||
* can be freed in caller. This should only happen when we encounter
|
||||
* an error.
|
||||
*/
|
||||
if (!list_empty(&sort_list)) {
|
||||
ASSERT(error);
|
||||
list_splice_init(&sort_list, &trans->r_itemq);
|
||||
}
|
||||
if (!list_empty(&buffer_list))
|
||||
list_splice(&buffer_list, &trans->r_itemq);
|
||||
if (!list_empty(&item_list))
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ xfs_reflink_fill_cow_hole(
|
|||
struct xfs_mount *mp = ip->i_mount;
|
||||
struct xfs_trans *tp;
|
||||
xfs_filblks_t resaligned;
|
||||
unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
|
||||
unsigned int dblocks = 0, rblocks = 0;
|
||||
int nimaps;
|
||||
int error;
|
||||
|
|
@ -465,6 +466,22 @@ xfs_reflink_fill_cow_hole(
|
|||
|
||||
*lockmode = XFS_ILOCK_EXCL;
|
||||
|
||||
/*
|
||||
* The data fork mapping may have changed while we dropped the ILOCK
|
||||
* (a racing O_DIRECT writer under IOLOCK_SHARED can complete a full
|
||||
* CoW cycle including xfs_reflink_end_cow(), which remaps this offset
|
||||
* and drops the refcount of the old shared block). Re-read it so the
|
||||
* shared-status recheck below and the caller's in-place iomap both
|
||||
* operate on the current mapping rather than a stale physical block.
|
||||
*/
|
||||
if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
|
||||
nimaps = 1;
|
||||
error = xfs_bmapi_read(ip, imap->br_startoff,
|
||||
imap->br_blockcount, imap, &nimaps, 0);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
}
|
||||
|
||||
error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found);
|
||||
if (error || !*shared)
|
||||
goto out_trans_cancel;
|
||||
|
|
@ -511,6 +528,8 @@ xfs_reflink_fill_delalloc(
|
|||
bool found;
|
||||
|
||||
do {
|
||||
unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
|
||||
|
||||
xfs_iunlock(ip, *lockmode);
|
||||
*lockmode = 0;
|
||||
|
||||
|
|
@ -521,6 +540,23 @@ xfs_reflink_fill_delalloc(
|
|||
|
||||
*lockmode = XFS_ILOCK_EXCL;
|
||||
|
||||
/*
|
||||
* The data fork mapping may have changed while we dropped the
|
||||
* ILOCK (a racing O_DIRECT writer under IOLOCK_SHARED can
|
||||
* complete a full CoW cycle including xfs_reflink_end_cow(),
|
||||
* which remaps this offset and drops the refcount of the old
|
||||
* shared block). Re-read it so the shared-status recheck
|
||||
* below and the caller's in-place iomap both operate on the
|
||||
* current mapping rather than a stale physical block.
|
||||
*/
|
||||
if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
|
||||
nimaps = 1;
|
||||
error = xfs_bmapi_read(ip, imap->br_startoff,
|
||||
imap->br_blockcount, imap, &nimaps, 0);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
}
|
||||
|
||||
error = xfs_find_trim_cow_extent(ip, imap, cmap, shared,
|
||||
&found);
|
||||
if (error || !*shared)
|
||||
|
|
|
|||
|
|
@ -780,6 +780,23 @@ static const struct kobj_type xfs_zoned_ktype = {
|
|||
.default_groups = xfs_zoned_groups,
|
||||
};
|
||||
|
||||
int
|
||||
xfs_zoned_sysfs_init(struct xfs_mount *mp)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_XFS_RT) || !xfs_has_zoned(mp))
|
||||
return 0;
|
||||
|
||||
return xfs_sysfs_init(&mp->m_zoned_kobj, &xfs_zoned_ktype,
|
||||
&mp->m_kobj, "zoned");
|
||||
}
|
||||
|
||||
void
|
||||
xfs_zoned_sysfs_del(struct xfs_mount *mp)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_XFS_RT) && xfs_has_zoned(mp))
|
||||
xfs_sysfs_del(&mp->m_zoned_kobj);
|
||||
}
|
||||
|
||||
int
|
||||
xfs_mount_sysfs_init(
|
||||
struct xfs_mount *mp)
|
||||
|
|
@ -820,14 +837,6 @@ xfs_mount_sysfs_init(
|
|||
if (error)
|
||||
goto out_remove_error_dir;
|
||||
|
||||
if (IS_ENABLED(CONFIG_XFS_RT) && xfs_has_zoned(mp)) {
|
||||
/* .../xfs/<dev>/zoned/ */
|
||||
error = xfs_sysfs_init(&mp->m_zoned_kobj, &xfs_zoned_ktype,
|
||||
&mp->m_kobj, "zoned");
|
||||
if (error)
|
||||
goto out_remove_error_dir;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_remove_error_dir:
|
||||
|
|
@ -846,9 +855,6 @@ xfs_mount_sysfs_del(
|
|||
struct xfs_error_cfg *cfg;
|
||||
int i, j;
|
||||
|
||||
if (IS_ENABLED(CONFIG_XFS_RT) && xfs_has_zoned(mp))
|
||||
xfs_sysfs_del(&mp->m_zoned_kobj);
|
||||
|
||||
for (i = 0; i < XFS_ERR_CLASS_MAX; i++) {
|
||||
for (j = 0; j < XFS_ERR_ERRNO_MAX; j++) {
|
||||
cfg = &mp->m_error_cfg[i][j];
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ xfs_sysfs_del(
|
|||
}
|
||||
|
||||
int xfs_mount_sysfs_init(struct xfs_mount *mp);
|
||||
int xfs_zoned_sysfs_init(struct xfs_mount *mp);
|
||||
void xfs_zoned_sysfs_del(struct xfs_mount *mp);
|
||||
void xfs_mount_sysfs_del(struct xfs_mount *mp);
|
||||
|
||||
#endif /* __XFS_SYSFS_H__ */
|
||||
|
|
|
|||
|
|
@ -6439,6 +6439,47 @@ TRACE_EVENT(xfs_verify_media_error,
|
|||
__entry->error)
|
||||
);
|
||||
|
||||
TRACE_EVENT(xfs_bmap_replace_cow_mapping,
|
||||
TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got,
|
||||
const struct xfs_bmbt_irec *rep),
|
||||
TP_ARGS(ip, got, rep),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(xfs_ino_t, ino)
|
||||
__field(xfs_fsblock_t, startblock)
|
||||
__field(xfs_fileoff_t, startoff)
|
||||
__field(xfs_filblks_t, blockcount)
|
||||
__field(xfs_exntst_t, state)
|
||||
__field(xfs_fileoff_t, new_startoff)
|
||||
__field(xfs_fsblock_t, new_startblock)
|
||||
__field(xfs_extlen_t, new_blockcount)
|
||||
__field(xfs_exntst_t, new_state)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dev = ip->i_mount->m_super->s_dev;
|
||||
__entry->ino = I_INO(ip);
|
||||
__entry->startoff = got->br_startoff;
|
||||
__entry->startblock = got->br_startblock;
|
||||
__entry->blockcount = got->br_blockcount;
|
||||
__entry->state = got->br_state;
|
||||
__entry->new_startoff = rep->br_startoff;
|
||||
__entry->new_startblock = rep->br_startblock;
|
||||
__entry->new_blockcount = rep->br_blockcount;
|
||||
__entry->new_state = rep->br_state;
|
||||
),
|
||||
TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->ino,
|
||||
__entry->startoff,
|
||||
__entry->startblock,
|
||||
__entry->blockcount,
|
||||
__entry->state,
|
||||
__entry->new_startoff,
|
||||
__entry->new_startblock,
|
||||
__entry->new_blockcount,
|
||||
__entry->new_state)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_XFS_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "xfs_rtbitmap.h"
|
||||
#include "xfs_rtrmap_btree.h"
|
||||
#include "xfs_zone_alloc.h"
|
||||
#include "xfs_sysfs.h"
|
||||
#include "xfs_zone_priv.h"
|
||||
#include "xfs_zones.h"
|
||||
#include "xfs_trace.h"
|
||||
|
|
@ -1420,11 +1421,17 @@ xfs_mount_zones(
|
|||
if (error)
|
||||
goto out_free_zone_info;
|
||||
|
||||
error = xfs_zoned_sysfs_init(mp);
|
||||
if (error)
|
||||
goto out_zone_gc_unmount;
|
||||
|
||||
xfs_info(mp, "%u zones of %u blocks (%u max open zones)",
|
||||
mp->m_sb.sb_rgcount, iz.zone_capacity, mp->m_max_open_zones);
|
||||
trace_xfs_zones_mount(mp);
|
||||
return 0;
|
||||
|
||||
out_zone_gc_unmount:
|
||||
xfs_zone_gc_unmount(mp);
|
||||
out_free_zone_info:
|
||||
xfs_free_zone_info(mp->m_zone_info);
|
||||
return error;
|
||||
|
|
@ -1434,6 +1441,7 @@ void
|
|||
xfs_unmount_zones(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
xfs_zoned_sysfs_del(mp);
|
||||
xfs_zone_gc_unmount(mp);
|
||||
xfs_free_zone_info(mp->m_zone_info);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user