Bug fixes for 6.11-rc6:

* Do not call out v1 inodes with non-zero di_nlink field as being corrupt.
   * Change xfs_finobt_count_blocks() to count "free inode btree" blocks rather
     than "inode btree" blocks.
   * Don't report the number of trimmed bytes via FITRIM because the underlying
     storage isn't required to do anything and failed discard IOs aren't
     reported to the caller anyway.
   * Fix incorrect setting of rm_owner field in an rmap query.
   * Report missing disk offset range in an fsmap query.
   * Obtain m_growlock when extending realtime section of the filesystem.
   * Reset rootdir extent size hint after extending realtime section of the
     filesystem.
 
 Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQQjMC4mbgVeU7MxEIYH7y4RirJu9AUCZs3OYgAKCRAH7y4RirJu
 9OF/AP9MXSSmBHmTfpqJZbKCI9j+EvAGyucbITi32ZBnbnNnKgEAr5FrueGcKS98
 H/FxMeNbSWZp0s5hUYsXsACtdo75YgE=
 =prEp
 -----END PGP SIGNATURE-----

Merge tag 'xfs-6.11-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Chandan Babu:

 - Do not call out v1 inodes with non-zero di_nlink field as being
   corrupt

 - Change xfs_finobt_count_blocks() to count "free inode btree" blocks
   rather than "inode btree" blocks

 - Don't report the number of trimmed bytes via FITRIM because the
   underlying storage isn't required to do anything and failed discard
   IOs aren't reported to the caller anyway

 - Fix incorrect setting of rm_owner field in an rmap query

 - Report missing disk offset range in an fsmap query

 - Obtain m_growlock when extending realtime section of the filesystem

 - Reset rootdir extent size hint after extending realtime section of
   the filesystem

* tag 'xfs-6.11-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: reset rootdir extent size hint after growfsrt
  xfs: take m_growlock when running growfsrt
  xfs: Fix missing interval for missing_owner in xfs fsmap
  xfs: use XFS_BUF_DADDR_NULL for daddrs in getfsmap code
  xfs: Fix the owner setting issue for rmap query in xfs fsmap
  xfs: don't bother reporting blocks trimmed via FITRIM
  xfs: xfs_finobt_count_blocks() walks the wrong btree
  xfs: fix folio dirtying for XFILE_ALLOC callers
  xfs: fix di_onlink checking for V1/V2 inodes
This commit is contained in:
Linus Torvalds 2024-09-01 06:48:37 +12:00
commit 0efdc09796
6 changed files with 114 additions and 48 deletions

View file

@ -749,7 +749,7 @@ xfs_finobt_count_blocks(
if (error)
return error;
cur = xfs_inobt_init_cursor(pag, tp, agbp);
cur = xfs_finobt_init_cursor(pag, tp, agbp);
error = xfs_btree_count_blocks(cur, tree_blocks);
xfs_btree_del_cursor(cur, error);
xfs_trans_brelse(tp, agbp);

View file

@ -514,12 +514,18 @@ xfs_dinode_verify(
return __this_address;
}
if (dip->di_version > 1) {
/*
* Historical note: xfsprogs in the 3.2 era set up its incore inodes to
* have di_nlink track the link count, even if the actual filesystem
* only supported V1 inodes (i.e. di_onlink). When writing out the
* ondisk inode, it would set both the ondisk di_nlink and di_onlink to
* the the incore di_nlink value, which is why we cannot check for
* di_nlink==0 on a V1 inode. V2/3 inodes would get written out with
* di_onlink==0, so we can check that.
*/
if (dip->di_version >= 2) {
if (dip->di_onlink)
return __this_address;
} else {
if (dip->di_nlink)
return __this_address;
}
/* don't allow invalid i_size */

View file

@ -293,7 +293,7 @@ xfile_get_folio(
* (potentially last) reference in xfile_put_folio.
*/
if (flags & XFILE_ALLOC)
folio_set_dirty(folio);
folio_mark_dirty(folio);
return folio;
}

View file

@ -158,8 +158,7 @@ static int
xfs_trim_gather_extents(
struct xfs_perag *pag,
struct xfs_trim_cur *tcur,
struct xfs_busy_extents *extents,
uint64_t *blocks_trimmed)
struct xfs_busy_extents *extents)
{
struct xfs_mount *mp = pag->pag_mount;
struct xfs_trans *tp;
@ -280,7 +279,6 @@ xfs_trim_gather_extents(
xfs_extent_busy_insert_discard(pag, fbno, flen,
&extents->extent_list);
*blocks_trimmed += flen;
next_extent:
if (tcur->by_bno)
error = xfs_btree_increment(cur, 0, &i);
@ -327,8 +325,7 @@ xfs_trim_perag_extents(
struct xfs_perag *pag,
xfs_agblock_t start,
xfs_agblock_t end,
xfs_extlen_t minlen,
uint64_t *blocks_trimmed)
xfs_extlen_t minlen)
{
struct xfs_trim_cur tcur = {
.start = start,
@ -354,8 +351,7 @@ xfs_trim_perag_extents(
extents->owner = extents;
INIT_LIST_HEAD(&extents->extent_list);
error = xfs_trim_gather_extents(pag, &tcur, extents,
blocks_trimmed);
error = xfs_trim_gather_extents(pag, &tcur, extents);
if (error) {
kfree(extents);
break;
@ -389,8 +385,7 @@ xfs_trim_datadev_extents(
struct xfs_mount *mp,
xfs_daddr_t start,
xfs_daddr_t end,
xfs_extlen_t minlen,
uint64_t *blocks_trimmed)
xfs_extlen_t minlen)
{
xfs_agnumber_t start_agno, end_agno;
xfs_agblock_t start_agbno, end_agbno;
@ -411,8 +406,7 @@ xfs_trim_datadev_extents(
if (start_agno == end_agno)
agend = end_agbno;
error = xfs_trim_perag_extents(pag, start_agbno, agend, minlen,
blocks_trimmed);
error = xfs_trim_perag_extents(pag, start_agbno, agend, minlen);
if (error)
last_error = error;
@ -431,9 +425,6 @@ struct xfs_trim_rtdev {
/* list of rt extents to free */
struct list_head extent_list;
/* pointer to count of blocks trimmed */
uint64_t *blocks_trimmed;
/* minimum length that caller allows us to trim */
xfs_rtblock_t minlen_fsb;
@ -551,7 +542,6 @@ xfs_trim_gather_rtextent(
busyp->length = rlen;
INIT_LIST_HEAD(&busyp->list);
list_add_tail(&busyp->list, &tr->extent_list);
*tr->blocks_trimmed += rlen;
tr->restart_rtx = rec->ar_startext + rec->ar_extcount;
return 0;
@ -562,13 +552,11 @@ xfs_trim_rtdev_extents(
struct xfs_mount *mp,
xfs_daddr_t start,
xfs_daddr_t end,
xfs_daddr_t minlen,
uint64_t *blocks_trimmed)
xfs_daddr_t minlen)
{
struct xfs_rtalloc_rec low = { };
struct xfs_rtalloc_rec high = { };
struct xfs_trim_rtdev tr = {
.blocks_trimmed = blocks_trimmed,
.minlen_fsb = XFS_BB_TO_FSB(mp, minlen),
};
struct xfs_trans *tp;
@ -634,7 +622,7 @@ xfs_trim_rtdev_extents(
return error;
}
#else
# define xfs_trim_rtdev_extents(m,s,e,n,b) (-EOPNOTSUPP)
# define xfs_trim_rtdev_extents(...) (-EOPNOTSUPP)
#endif /* CONFIG_XFS_RT */
/*
@ -661,7 +649,6 @@ xfs_ioc_trim(
xfs_daddr_t start, end;
xfs_extlen_t minlen;
xfs_rfsblock_t max_blocks;
uint64_t blocks_trimmed = 0;
int error, last_error = 0;
if (!capable(CAP_SYS_ADMIN))
@ -706,15 +693,13 @@ xfs_ioc_trim(
end = start + BTOBBT(range.len) - 1;
if (bdev_max_discard_sectors(mp->m_ddev_targp->bt_bdev)) {
error = xfs_trim_datadev_extents(mp, start, end, minlen,
&blocks_trimmed);
error = xfs_trim_datadev_extents(mp, start, end, minlen);
if (error)
last_error = error;
}
if (rt_bdev && !xfs_trim_should_stop()) {
error = xfs_trim_rtdev_extents(mp, start, end, minlen,
&blocks_trimmed);
error = xfs_trim_rtdev_extents(mp, start, end, minlen);
if (error)
last_error = error;
}
@ -722,7 +707,8 @@ xfs_ioc_trim(
if (last_error)
return last_error;
range.len = XFS_FSB_TO_B(mp, blocks_trimmed);
range.len = min_t(unsigned long long, range.len,
XFS_FSB_TO_B(mp, max_blocks));
if (copy_to_user(urange, &range, sizeof(range)))
return -EFAULT;
return 0;

View file

@ -71,7 +71,7 @@ xfs_fsmap_owner_to_rmap(
switch (src->fmr_owner) {
case 0: /* "lowest owner id possible" */
case -1ULL: /* "highest owner id possible" */
dest->rm_owner = 0;
dest->rm_owner = src->fmr_owner;
break;
case XFS_FMR_OWN_FREE:
dest->rm_owner = XFS_RMAP_OWN_NULL;
@ -162,6 +162,7 @@ struct xfs_getfsmap_info {
xfs_daddr_t next_daddr; /* next daddr we expect */
/* daddr of low fsmap key when we're using the rtbitmap */
xfs_daddr_t low_daddr;
xfs_daddr_t end_daddr; /* daddr of high fsmap key */
u64 missing_owner; /* owner of holes */
u32 dev; /* device id */
/*
@ -182,6 +183,7 @@ struct xfs_getfsmap_dev {
int (*fn)(struct xfs_trans *tp,
const struct xfs_fsmap *keys,
struct xfs_getfsmap_info *info);
sector_t nr_sectors;
};
/* Compare two getfsmap device handlers. */
@ -252,7 +254,7 @@ xfs_getfsmap_rec_before_start(
const struct xfs_rmap_irec *rec,
xfs_daddr_t rec_daddr)
{
if (info->low_daddr != -1ULL)
if (info->low_daddr != XFS_BUF_DADDR_NULL)
return rec_daddr < info->low_daddr;
if (info->low.rm_blockcount)
return xfs_rmap_compare(rec, &info->low) < 0;
@ -294,6 +296,18 @@ xfs_getfsmap_helper(
return 0;
}
/*
* For an info->last query, we're looking for a gap between the last
* mapping emitted and the high key specified by userspace. If the
* user's query spans less than 1 fsblock, then info->high and
* info->low will have the same rm_startblock, which causes rec_daddr
* and next_daddr to be the same. Therefore, use the end_daddr that
* we calculated from userspace's high key to synthesize the record.
* Note that if the btree query found a mapping, there won't be a gap.
*/
if (info->last && info->end_daddr != XFS_BUF_DADDR_NULL)
rec_daddr = info->end_daddr;
/* Are we just counting mappings? */
if (info->head->fmh_count == 0) {
if (info->head->fmh_entries == UINT_MAX)
@ -904,17 +918,21 @@ xfs_getfsmap(
/* Set up our device handlers. */
memset(handlers, 0, sizeof(handlers));
handlers[0].nr_sectors = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
handlers[0].dev = new_encode_dev(mp->m_ddev_targp->bt_dev);
if (use_rmap)
handlers[0].fn = xfs_getfsmap_datadev_rmapbt;
else
handlers[0].fn = xfs_getfsmap_datadev_bnobt;
if (mp->m_logdev_targp != mp->m_ddev_targp) {
handlers[1].nr_sectors = XFS_FSB_TO_BB(mp,
mp->m_sb.sb_logblocks);
handlers[1].dev = new_encode_dev(mp->m_logdev_targp->bt_dev);
handlers[1].fn = xfs_getfsmap_logdev;
}
#ifdef CONFIG_XFS_RT
if (mp->m_rtdev_targp) {
handlers[2].nr_sectors = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks);
handlers[2].dev = new_encode_dev(mp->m_rtdev_targp->bt_dev);
handlers[2].fn = xfs_getfsmap_rtdev_rtbitmap;
}
@ -946,6 +964,7 @@ xfs_getfsmap(
info.next_daddr = head->fmh_keys[0].fmr_physical +
head->fmh_keys[0].fmr_length;
info.end_daddr = XFS_BUF_DADDR_NULL;
info.fsmap_recs = fsmap_recs;
info.head = head;
@ -966,8 +985,11 @@ xfs_getfsmap(
* low key, zero out the low key so that we get
* everything from the beginning.
*/
if (handlers[i].dev == head->fmh_keys[1].fmr_device)
if (handlers[i].dev == head->fmh_keys[1].fmr_device) {
dkeys[1] = head->fmh_keys[1];
info.end_daddr = min(handlers[i].nr_sectors - 1,
dkeys[1].fmr_physical);
}
if (handlers[i].dev > head->fmh_keys[0].fmr_device)
memset(&dkeys[0], 0, sizeof(struct xfs_fsmap));
@ -983,7 +1005,7 @@ xfs_getfsmap(
info.dev = handlers[i].dev;
info.last = false;
info.pag = NULL;
info.low_daddr = -1ULL;
info.low_daddr = XFS_BUF_DADDR_NULL;
info.low.rm_blockcount = 0;
error = handlers[i].fn(tp, dkeys, &info);
if (error)

View file

@ -784,6 +784,39 @@ xfs_alloc_rsum_cache(
xfs_warn(mp, "could not allocate realtime summary cache");
}
/*
* If we changed the rt extent size (meaning there was no rt volume previously)
* and the root directory had EXTSZINHERIT and RTINHERIT set, it's possible
* that the extent size hint on the root directory is no longer congruent with
* the new rt extent size. Log the rootdir inode to fix this.
*/
static int
xfs_growfs_rt_fixup_extsize(
struct xfs_mount *mp)
{
struct xfs_inode *ip = mp->m_rootip;
struct xfs_trans *tp;
int error = 0;
xfs_ilock(ip, XFS_IOLOCK_EXCL);
if (!(ip->i_diflags & XFS_DIFLAG_RTINHERIT) ||
!(ip->i_diflags & XFS_DIFLAG_EXTSZINHERIT))
goto out_iolock;
error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_ichange, 0, 0, false,
&tp);
if (error)
goto out_iolock;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
out_iolock:
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
return error;
}
/*
* Visible (exported) functions.
*/
@ -812,6 +845,7 @@ xfs_growfs_rt(
xfs_extlen_t rsumblocks; /* current number of rt summary blks */
xfs_sb_t *sbp; /* old superblock */
uint8_t *rsum_cache; /* old summary cache */
xfs_agblock_t old_rextsize = mp->m_sb.sb_rextsize;
sbp = &mp->m_sb;
@ -821,34 +855,39 @@ xfs_growfs_rt(
/* Needs to have been mounted with an rt device. */
if (!XFS_IS_REALTIME_MOUNT(mp))
return -EINVAL;
if (!mutex_trylock(&mp->m_growlock))
return -EWOULDBLOCK;
/*
* Mount should fail if the rt bitmap/summary files don't load, but
* we'll check anyway.
*/
error = -EINVAL;
if (!mp->m_rbmip || !mp->m_rsumip)
return -EINVAL;
goto out_unlock;
/* Shrink not supported. */
if (in->newblocks <= sbp->sb_rblocks)
return -EINVAL;
goto out_unlock;
/* Can only change rt extent size when adding rt volume. */
if (sbp->sb_rblocks > 0 && in->extsize != sbp->sb_rextsize)
return -EINVAL;
goto out_unlock;
/* Range check the extent size. */
if (XFS_FSB_TO_B(mp, in->extsize) > XFS_MAX_RTEXTSIZE ||
XFS_FSB_TO_B(mp, in->extsize) < XFS_MIN_RTEXTSIZE)
return -EINVAL;
goto out_unlock;
/* Unsupported realtime features. */
error = -EOPNOTSUPP;
if (xfs_has_rmapbt(mp) || xfs_has_reflink(mp) || xfs_has_quota(mp))
return -EOPNOTSUPP;
goto out_unlock;
nrblocks = in->newblocks;
error = xfs_sb_validate_fsb_count(sbp, nrblocks);
if (error)
return error;
goto out_unlock;
/*
* Read in the last block of the device, make sure it exists.
*/
@ -856,7 +895,7 @@ xfs_growfs_rt(
XFS_FSB_TO_BB(mp, nrblocks - 1),
XFS_FSB_TO_BB(mp, 1), 0, &bp, NULL);
if (error)
return error;
goto out_unlock;
xfs_buf_relse(bp);
/*
@ -864,8 +903,10 @@ xfs_growfs_rt(
*/
nrextents = nrblocks;
do_div(nrextents, in->extsize);
if (!xfs_validate_rtextents(nrextents))
return -EINVAL;
if (!xfs_validate_rtextents(nrextents)) {
error = -EINVAL;
goto out_unlock;
}
nrbmblocks = xfs_rtbitmap_blockcount(mp, nrextents);
nrextslog = xfs_compute_rextslog(nrextents);
nrsumlevels = nrextslog + 1;
@ -876,8 +917,11 @@ xfs_growfs_rt(
* the log. This prevents us from getting a log overflow,
* since we'll log basically the whole summary file at once.
*/
if (nrsumblocks > (mp->m_sb.sb_logblocks >> 1))
return -EINVAL;
if (nrsumblocks > (mp->m_sb.sb_logblocks >> 1)) {
error = -EINVAL;
goto out_unlock;
}
/*
* Get the old block counts for bitmap and summary inodes.
* These can't change since other growfs callers are locked out.
@ -889,10 +933,10 @@ xfs_growfs_rt(
*/
error = xfs_growfs_rt_alloc(mp, rbmblocks, nrbmblocks, mp->m_rbmip);
if (error)
return error;
goto out_unlock;
error = xfs_growfs_rt_alloc(mp, rsumblocks, nrsumblocks, mp->m_rsumip);
if (error)
return error;
goto out_unlock;
rsum_cache = mp->m_rsum_cache;
if (nrbmblocks != sbp->sb_rbmblocks)
@ -1036,6 +1080,12 @@ xfs_growfs_rt(
if (error)
goto out_free;
if (old_rextsize != in->extsize) {
error = xfs_growfs_rt_fixup_extsize(mp);
if (error)
goto out_free;
}
/* Update secondary superblocks now the physical grow has completed */
error = xfs_update_secondary_sbs(mp);
@ -1059,6 +1109,8 @@ xfs_growfs_rt(
}
}
out_unlock:
mutex_unlock(&mp->m_growlock);
return error;
}