summaryrefslogtreecommitdiff
path: root/fs/xfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs')
-rw-r--r--fs/xfs/libxfs/xfs_ag.c10
-rw-r--r--fs/xfs/libxfs/xfs_ag.h2
-rw-r--r--fs/xfs/libxfs/xfs_bmap_btree.h2
-rw-r--r--fs/xfs/libxfs/xfs_defer.c2
-rw-r--r--fs/xfs/libxfs/xfs_dquot_buf.c14
-rw-r--r--fs/xfs/libxfs/xfs_exchmaps.c2
-rw-r--r--fs/xfs/scrub/dirtree.c16
-rw-r--r--fs/xfs/scrub/dqiterate.c2
-rw-r--r--fs/xfs/scrub/inode_repair.c4
-rw-r--r--fs/xfs/scrub/rgsuper.c14
-rw-r--r--fs/xfs/scrub/rtbitmap.c2
-rw-r--r--fs/xfs/scrub/rtrefcount.c2
-rw-r--r--fs/xfs/scrub/rtrmap.c8
-rw-r--r--fs/xfs/scrub/scrub.h6
-rw-r--r--fs/xfs/scrub/xfarray.c3
-rw-r--r--fs/xfs/xfs_dquot.c16
-rw-r--r--fs/xfs/xfs_fsops.c2
-rw-r--r--fs/xfs/xfs_iomap.c2
-rw-r--r--fs/xfs/xfs_log_recover.c15
-rw-r--r--fs/xfs/xfs_qm.c5
-rw-r--r--fs/xfs/xfs_reflink.c36
-rw-r--r--fs/xfs/xfs_rtalloc.c8
22 files changed, 122 insertions, 51 deletions
diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c
index dcd2f93b6a6c..0c5f0548021f 100644
--- a/fs/xfs/libxfs/xfs_ag.c
+++ b/fs/xfs/libxfs/xfs_ag.c
@@ -866,7 +866,7 @@ resv_err:
void
xfs_growfs_compute_deltas(
struct xfs_mount *mp,
- xfs_rfsblock_t nb,
+ xfs_rfsblock_t *nb,
int64_t *deltap,
xfs_agnumber_t *nagcountp)
{
@@ -874,19 +874,19 @@ xfs_growfs_compute_deltas(
int64_t delta;
xfs_agnumber_t nagcount;
- nb_div = nb;
+ nb_div = *nb;
nb_mod = do_div(nb_div, mp->m_sb.sb_agblocks);
if (nb_mod && nb_mod >= XFS_MIN_AG_BLOCKS)
nb_div++;
else if (nb_mod)
- nb = nb_div * mp->m_sb.sb_agblocks;
+ *nb = nb_div * mp->m_sb.sb_agblocks;
if (nb_div > XFS_MAX_AGNUMBER + 1) {
nb_div = XFS_MAX_AGNUMBER + 1;
- nb = nb_div * mp->m_sb.sb_agblocks;
+ *nb = nb_div * mp->m_sb.sb_agblocks;
}
nagcount = nb_div;
- delta = nb - mp->m_sb.sb_dblocks;
+ delta = *nb - mp->m_sb.sb_dblocks;
*deltap = delta;
*nagcountp = nagcount;
}
diff --git a/fs/xfs/libxfs/xfs_ag.h b/fs/xfs/libxfs/xfs_ag.h
index 16a9b43a3c27..8aa4266c5571 100644
--- a/fs/xfs/libxfs/xfs_ag.h
+++ b/fs/xfs/libxfs/xfs_ag.h
@@ -330,7 +330,7 @@ int xfs_ag_init_headers(struct xfs_mount *mp, struct aghdr_init_data *id);
int xfs_ag_shrink_space(struct xfs_perag *pag, struct xfs_trans **tpp,
xfs_extlen_t delta);
void
-xfs_growfs_compute_deltas(struct xfs_mount *mp, xfs_rfsblock_t nb,
+xfs_growfs_compute_deltas(struct xfs_mount *mp, xfs_rfsblock_t *nb,
int64_t *deltap, xfs_agnumber_t *nagcountp);
int xfs_ag_extend_space(struct xfs_perag *pag, struct xfs_trans *tp,
xfs_extlen_t len);
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
index b238d559ab03..e0c870beaf67 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.h
+++ b/fs/xfs/libxfs/xfs_bmap_btree.h
@@ -89,7 +89,7 @@ xfs_bmbt_key_addr(
{
return (struct xfs_bmbt_key *)
((char *)block + xfs_bmbt_block_len(mp) +
- (index - 1) * sizeof(struct xfs_bmbt_key *));
+ (index - 1) * sizeof(struct xfs_bmbt_key));
}
static inline xfs_bmbt_ptr_t *
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index c6909716b041..89501e8bd2f8 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -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);
}
diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c
index ce767b40482f..bbada0d3cc08 100644
--- a/fs/xfs/libxfs/xfs_dquot_buf.c
+++ b/fs/xfs/libxfs/xfs_dquot_buf.c
@@ -436,17 +436,27 @@ xfs_dqinode_metadir_create(
error = xfs_metadir_create(&upd, S_IFREG);
if (error)
- return error;
+ goto out_cancel;
xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
error = xfs_metadir_commit(&upd);
if (error)
- return error;
+ goto out_irele;
xfs_finish_inode_setup(upd.ip);
*ipp = upd.ip;
return 0;
+
+out_cancel:
+ xfs_metadir_cancel(&upd, error);
+out_irele:
+ /* Have to finish setting up the inode to ensure it's deleted. */
+ if (upd.ip) {
+ xfs_finish_inode_setup(upd.ip);
+ xfs_irele(upd.ip);
+ }
+ return error;
}
#ifndef __KERNEL__
diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c
index 5d28f4eac527..541e33f33167 100644
--- a/fs/xfs/libxfs/xfs_exchmaps.c
+++ b/fs/xfs/libxfs/xfs_exchmaps.c
@@ -711,7 +711,7 @@ xfs_exchmaps_estimate_overhead(
return -ENOSPC;
/* Can't actually reserve more than UINT_MAX blocks. */
- if (req->resblks > UINT_MAX)
+ if (resblks > UINT_MAX)
return -ENOSPC;
req->resblks = resblks;
diff --git a/fs/xfs/scrub/dirtree.c b/fs/xfs/scrub/dirtree.c
index 143114718d75..19f014e2ece6 100644
--- a/fs/xfs/scrub/dirtree.c
+++ b/fs/xfs/scrub/dirtree.c
@@ -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,
diff --git a/fs/xfs/scrub/dqiterate.c b/fs/xfs/scrub/dqiterate.c
index 10950e4bd4c3..079dc4e691a0 100644
--- a/fs/xfs/scrub/dqiterate.c
+++ b/fs/xfs/scrub/dqiterate.c
@@ -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;
}
diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c
index 9738b9ce3f2d..2ac66163263b 100644
--- a/fs/xfs/scrub/inode_repair.c
+++ b/fs/xfs/scrub/inode_repair.c
@@ -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);
@@ -1753,7 +1753,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));
}
diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c
index 482f899a518a..2bd2c0351b35 100644
--- a/fs/xfs/scrub/rgsuper.c
+++ b/fs/xfs/scrub/rgsuper.c
@@ -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 */
diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c
index b3a4972d423e..88b438d8fd89 100644
--- a/fs/xfs/scrub/rtbitmap.c
+++ b/fs/xfs/scrub/rtbitmap.c
@@ -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);
diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c
index 4951cc271788..5f1d5ce7c268 100644
--- a/fs/xfs/scrub/rtrefcount.c
+++ b/fs/xfs/scrub/rtrefcount.c
@@ -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)
diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c
index 8b1a8389d32f..8c9247923a62 100644
--- a/fs/xfs/scrub/rtrmap.c
+++ b/fs/xfs/scrub/rtrmap.c
@@ -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);
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index a3f1abc91390..6d7d3523b71f 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -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;
diff --git a/fs/xfs/scrub/xfarray.c b/fs/xfs/scrub/xfarray.c
index c7c4a71b6fa7..2ce24bfe4c0f 100644
--- a/fs/xfs/scrub/xfarray.c
+++ b/fs/xfs/scrub/xfarray.c
@@ -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;
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index 69e9bc588c8b..c311f61d9554 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -1216,6 +1216,14 @@ xfs_qm_dqflush_check(
type != XFS_DQTYPE_PROJ)
return __this_address;
+ /* bigtime flag should never be set on root dquots */
+ if (dqp->q_type & XFS_DQTYPE_BIGTIME) {
+ if (!xfs_has_bigtime(dqp->q_mount))
+ return __this_address;
+ if (dqp->q_id == 0)
+ return __this_address;
+ }
+
if (dqp->q_id == 0)
return NULL;
@@ -1231,14 +1239,6 @@ xfs_qm_dqflush_check(
!dqp->q_rtb.timer)
return __this_address;
- /* bigtime flag should never be set on root dquots */
- if (dqp->q_type & XFS_DQTYPE_BIGTIME) {
- if (!xfs_has_bigtime(dqp->q_mount))
- return __this_address;
- if (dqp->q_id == 0)
- return __this_address;
- }
-
return NULL;
}
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 8d64d904d73c..436857356a0a 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -124,7 +124,7 @@ xfs_growfs_data_private(
mp->m_sb.sb_rextsize);
if (error)
return error;
- xfs_growfs_compute_deltas(mp, nb, &delta, &nagcount);
+ xfs_growfs_compute_deltas(mp, &nb, &delta, &nagcount);
/*
* Reject filesystems with a single AG because they are not
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index f20a02f49ed9..1403d568a442 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -113,6 +113,7 @@ xfs_bmbt_to_iomap(
return xfs_alert_fsblock_zero(ip, imap);
}
+ iomap->flags = iomap_flags;
if (imap->br_startblock == HOLESTARTBLOCK) {
iomap->addr = IOMAP_NULL_ADDR;
iomap->type = IOMAP_HOLE;
@@ -143,7 +144,6 @@ xfs_bmbt_to_iomap(
}
iomap->offset = XFS_FSB_TO_B(mp, imap->br_startoff);
iomap->length = XFS_FSB_TO_B(mp, imap->br_blockcount);
- iomap->flags = iomap_flags;
if (mapping_flags & IOMAP_DAX) {
iomap->dax_dev = target->bt_daxdev;
} else {
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 09e6678ca487..7f60d33f5d49 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1028,7 +1028,7 @@ xlog_verify_head(
{
struct xlog_rec_header *tmp_rhead;
char *tmp_buffer;
- xfs_daddr_t first_bad;
+ xfs_daddr_t first_bad = XFS_BUF_DADDR_NULL;
xfs_daddr_t tmp_rhead_blk;
int found;
int error;
@@ -1057,7 +1057,8 @@ xlog_verify_head(
*/
error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk,
XLOG_RECOVER_CRCPASS, &first_bad);
- if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) {
+ if ((error == -EFSBADCRC || error == -EFSCORRUPTED) &&
+ first_bad != XFS_BUF_DADDR_NULL) {
/*
* We've hit a potential torn write. Reset the error and warn
* about it.
@@ -1906,6 +1907,15 @@ 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,
@@ -3575,4 +3585,3 @@ xlog_recover_cancel(
if (xlog_recovery_needed(log))
xlog_recover_cancel_intents(log);
}
-
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 7bd15d9e7fba..02d23226611e 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -166,10 +166,9 @@ xfs_qm_dqpurge(
* does it on success.
*/
error = xfs_qm_dqflush(dqp, bp);
- if (!error) {
+ if (!error)
error = xfs_bwrite(bp);
- xfs_buf_relse(bp);
- }
+ xfs_buf_relse(bp);
xfs_dqflock(dqp);
}
xfs_dquot_detach_buf(dqp);
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 251dec48f0e3..e7db02579f28 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -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)
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
index 153f3c378f9f..419140dd1d51 100644
--- a/fs/xfs/xfs_rtalloc.c
+++ b/fs/xfs/xfs_rtalloc.c
@@ -890,8 +890,7 @@ xfs_growfs_rt_sb_fields(
static int
xfs_growfs_rt_zoned(
- struct xfs_rtgroup *rtg,
- xfs_rfsblock_t nrblocks)
+ struct xfs_rtgroup *rtg)
{
struct xfs_mount *mp = rtg_mount(rtg);
struct xfs_mount *nmp;
@@ -903,7 +902,8 @@ xfs_growfs_rt_zoned(
* Calculate new sb and mount fields for this round. Also ensure the
* rtg_extents value is uptodate as the rtbitmap code relies on it.
*/
- nmp = xfs_growfs_rt_alloc_fake_mount(mp, nrblocks,
+ nmp = xfs_growfs_rt_alloc_fake_mount(mp,
+ xfs_rtgs_to_rfsbs(mp, rtg_rgno(rtg) + 1),
mp->m_sb.sb_rextsize);
if (!nmp)
return -ENOMEM;
@@ -1218,7 +1218,7 @@ xfs_growfs_rtg(
}
if (xfs_has_zoned(mp)) {
- error = xfs_growfs_rt_zoned(rtg, nrblocks);
+ error = xfs_growfs_rt_zoned(rtg);
goto out_rele;
}