This commit is contained in:
Mark Brown
2026-07-31 13:45:15 +01:00
20 changed files with 369 additions and 153 deletions
+1 -2
View File
@@ -2354,8 +2354,7 @@ xfs_da_grow_inode_int(
* If we didn't get it and the block might work if fragmented,
* try without the CONTIG flag. Loop until we get it all.
*/
mapp = kmalloc(sizeof(*mapp) * count,
GFP_KERNEL | __GFP_NOFAIL);
mapp = kmalloc_objs(*mapp, count, GFP_KERNEL | __GFP_NOFAIL);
for (b = *bno, mapi = 0; b < *bno + count; ) {
c = (int)(*bno + count - b);
nmap = min(XFS_BMAP_MAX_NMAP, c);
+11 -28
View File
@@ -416,6 +416,15 @@ xfs_dqinode_load(
return 0;
}
static int
xfs_dqinode_init(
struct xfs_metadir_update *upd,
void *priv)
{
xfs_trans_log_inode(upd->tp, upd->ip, XFS_ILOG_CORE);
return 0;
}
/* Create a metadata directory quota inode. */
int
xfs_dqinode_metadir_create(
@@ -428,35 +437,9 @@ xfs_dqinode_metadir_create(
.metafile_type = xfs_dqinode_metafile_type(type),
.path = xfs_dqinode_path(type),
};
int error;
error = xfs_metadir_start_create(&upd);
if (error)
return error;
error = xfs_metadir_create(&upd, S_IFREG);
if (error)
goto out_cancel;
xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
error = xfs_metadir_commit(&upd);
if (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;
return xfs_metadir_create_file(&upd, S_IFREG, xfs_dqinode_init, NULL,
ipp);
}
#ifndef __KERNEL__
+50 -34
View File
@@ -182,7 +182,7 @@ xfs_metadir_teardown(
* Begin the process of creating a metadata file by allocating transactions
* and taking whatever resources we're going to need.
*/
int
static int
xfs_metadir_start_create(
struct xfs_metadir_update *upd)
{
@@ -236,7 +236,7 @@ out_teardown:
* a negative error code. If an inode is passed back, the caller must finish
* setting up the inode before releasing it.
*/
int
static int
xfs_metadir_create(
struct xfs_metadir_update *upd,
umode_t mode)
@@ -425,7 +425,7 @@ xfs_metadir_commit(
}
/* Cancel a metadir update and unlock/drop all resources. */
void
static void
xfs_metadir_cancel(
struct xfs_metadir_update *upd,
int error)
@@ -438,6 +438,52 @@ xfs_metadir_cancel(
xfs_metadir_teardown(upd, error);
}
int
xfs_metadir_create_file(
struct xfs_metadir_update *upd,
umode_t mode,
xfs_metadir_createfn create,
void *priv,
struct xfs_inode **ipp)
{
int error;
if (xfs_is_shutdown(upd->dp->i_mount))
return -EIO;
error = xfs_metadir_start_create(upd);
if (error)
return error;
error = xfs_metadir_create(upd, mode);
if (error)
goto out_cancel;
if (create) {
error = create(upd, priv);
if (error)
goto out_cancel;
}
error = xfs_metadir_commit(upd);
if (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;
}
/* Create a metadata for the last component of the path. */
int
xfs_metadir_mkdir(
@@ -450,36 +496,6 @@ xfs_metadir_mkdir(
.path = path,
.metafile_type = XFS_METAFILE_DIR,
};
int error;
if (xfs_is_shutdown(dp->i_mount))
return -EIO;
/* Allocate a transaction to create the last directory. */
error = xfs_metadir_start_create(&upd);
if (error)
return error;
/* Create the subdirectory and take our reference. */
error = xfs_metadir_create(&upd, S_IFDIR);
if (error)
goto out_cancel;
error = xfs_metadir_commit(&upd);
if (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;
return xfs_metadir_create_file(&upd, S_IFDIR, NULL, NULL, ipp);
}
+5 -3
View File
@@ -32,14 +32,16 @@ int xfs_metadir_load(struct xfs_trans *tp, struct xfs_inode *dp,
const char *path, enum xfs_metafile_type metafile_type,
struct xfs_inode **ipp);
int xfs_metadir_start_create(struct xfs_metadir_update *upd);
int xfs_metadir_create(struct xfs_metadir_update *upd, umode_t mode);
typedef int (*xfs_metadir_createfn)(struct xfs_metadir_update *upd, void *priv);
int xfs_metadir_create_file(struct xfs_metadir_update *upd, umode_t mode,
xfs_metadir_createfn create, void *priv,
struct xfs_inode **ipp);
int xfs_metadir_start_link(struct xfs_metadir_update *upd);
int xfs_metadir_link(struct xfs_metadir_update *upd);
int xfs_metadir_commit(struct xfs_metadir_update *upd);
void xfs_metadir_cancel(struct xfs_metadir_update *upd, int error);
int xfs_metadir_mkdir(struct xfs_inode *dp, const char *path,
struct xfs_inode **ipp);
+26 -32
View File
@@ -517,6 +517,25 @@ xfs_rtginode_irele(
*ipp = NULL;
}
struct xfs_rtginode_create {
struct xfs_rtgroup *rtg;
enum xfs_rtg_inodes type;
bool init;
};
static int
xfs_rtginode_init(
struct xfs_metadir_update *upd,
void *priv)
{
struct xfs_rtginode_create *rc = priv;
const struct xfs_rtginode_ops *ops = &xfs_rtginode_ops[rc->type];
xfs_rtginode_lockdep_setup(upd->ip, rtg_rgno(rc->rtg), rc->type);
upd->ip->i_projid = rtg_rgno(rc->rtg);
return ops->create(rc->rtg, upd->ip, upd->tp, rc->init);
}
/* Add a metadata inode for a realtime rmap btree. */
int
xfs_rtginode_create(
@@ -526,6 +545,11 @@ xfs_rtginode_create(
{
const struct xfs_rtginode_ops *ops = &xfs_rtginode_ops[type];
struct xfs_mount *mp = rtg_mount(rtg);
struct xfs_rtginode_create rc = {
.rtg = rtg,
.type = type,
.init = init,
};
struct xfs_metadir_update upd = {
.dp = mp->m_rtdirip,
.metafile_type = ops->metafile_type,
@@ -544,38 +568,8 @@ xfs_rtginode_create(
if (!upd.path)
return -ENOMEM;
error = xfs_metadir_start_create(&upd);
if (error)
goto out_path;
error = xfs_metadir_create(&upd, S_IFREG);
if (error)
goto out_cancel;
xfs_rtginode_lockdep_setup(upd.ip, rtg_rgno(rtg), type);
upd.ip->i_projid = rtg_rgno(rtg);
error = ops->create(rtg, upd.ip, upd.tp, init);
if (error)
goto out_cancel;
error = xfs_metadir_commit(&upd);
if (error)
goto out_path;
kfree(upd.path);
xfs_finish_inode_setup(upd.ip);
rtg->rtg_inodes[type] = upd.ip;
return 0;
out_cancel:
xfs_metadir_cancel(&upd, error);
/* 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);
}
out_path:
error = xfs_metadir_create_file(&upd, S_IFREG, xfs_rtginode_init, &rc,
&rtg->rtg_inodes[type]);
kfree(upd.path);
return error;
}
+2 -2
View File
@@ -201,7 +201,7 @@ xfs_rtrefcountbt_verify(
if (fa)
return fa;
level = be16_to_cpu(block->bb_level);
if (level > mp->m_rtrefc_maxlevels)
if (level >= mp->m_rtrefc_maxlevels)
return __this_address;
return xfs_btree_fsblock_verify(bp, mp->m_rtrefc_mxr[level != 0]);
@@ -651,7 +651,7 @@ xfs_iformat_rtrefcount(
numrecs = be16_to_cpu(dfp->bb_numrecs);
level = be16_to_cpu(dfp->bb_level);
if (level > mp->m_rtrefc_maxlevels ||
if (level >= mp->m_rtrefc_maxlevels ||
xfs_rtrefcount_droot_space_calc(level, numrecs) > dsize) {
xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE);
return -EFSCORRUPTED;
+1 -1
View File
@@ -266,7 +266,7 @@ xchk_superblock(
xchk_block_set_corrupt(sc, bp);
if (sb->sb_gquotino != cpu_to_be64(0))
xchk_block_set_preen(sc, bp);
xchk_block_set_corrupt(sc, bp);
} else {
if (sb->sb_uquotino != cpu_to_be64(mp->m_sb.sb_uquotino))
xchk_block_set_preen(sc, bp);
+5
View File
@@ -1170,6 +1170,11 @@ xchk_bmap_attr(
}
error = xchk_bmap(sc, XFS_ATTR_FORK);
/* A repaired, empty attr fork no longer has mappings to check. */
if (error == -ENOENT && (sc->flags & XREP_ALREADY_FIXED)) {
xchk_mark_healthy_if_clean(sc, XFS_SICK_INO_BMBTA_ZAPPED);
return 0;
}
if (error)
return error;
+1 -1
View File
@@ -1960,7 +1960,7 @@ xrep_inode_cowextsize(
/* Fix misaligned CoW extent size hints on a directory. */
if ((sc->ip->i_diflags & XFS_DIFLAG_RTINHERIT) &&
(sc->ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE) &&
sc->ip->i_extsize % sc->mp->m_sb.sb_rextsize > 0) {
xfs_extlen_to_rtxmod(sc->mp, sc->ip->i_cowextsize) > 0) {
sc->ip->i_cowextsize = 0;
sc->ip->i_diflags2 &= ~XFS_DIFLAG2_COWEXTSIZE;
}
+6 -1
View File
@@ -232,9 +232,14 @@ xrep_nlinks_repair_inode(
* unlinked list, put it on the unlinked list.
*/
if (total_links == 0 && !xfs_inode_on_unlinked_list(ip)) {
if (actual_nlink)
clear_nlink(VFS_I(ip));
error = xfs_iunlink(sc->tp, ip);
if (error)
if (error) {
if (actual_nlink)
set_nlink(VFS_I(ip), actual_nlink);
goto out_trans;
}
dirty = true;
}
+28 -18
View File
@@ -36,6 +36,24 @@
/* rt bitmap content repairs */
/*
* Reserve enough blocks to write out a completely new bitmap file, plus twice
* as many blocks as we would need if we can only allocate one block per data
* fork mapping. This should cover the preallocation of the temporary file and
* exchanging the extent mappings.
*
* We cannot use xfs_exchmaps_estimate because we have not yet constructed the
* replacement bitmap and therefore do not know how many extents it will use.
* By the time we do, we will have a dirty transaction (which we cannot drop
* because we cannot drop the rtbitmap ILOCK) and cannot ask for more
* reservation.
*/
static inline unsigned long long
xrep_rtbitmap_calc_blocks(struct xfs_mount *mp, unsigned long long blocks)
{
return blocks + (xfs_bmbt_calc_size(mp, blocks) * 2);
}
/* Set up to repair the realtime bitmap for this group. */
int
xrep_setup_rtbitmap(
@@ -56,20 +74,7 @@ xrep_setup_rtbitmap(
if (error)
return error;
/*
* Reserve enough blocks to write out a completely new bitmap file,
* plus twice as many blocks as we would need if we can only allocate
* one block per data fork mapping. This should cover the
* preallocation of the temporary file and exchanging the extent
* mappings.
*
* We cannot use xfs_exchmaps_estimate because we have not yet
* constructed the replacement bitmap and therefore do not know how
* many extents it will use. By the time we do, we will have a dirty
* transaction (which we cannot drop because we cannot drop the
* rtbitmap ILOCK) and cannot ask for more reservation.
*/
blocks += xfs_bmbt_calc_size(mp, blocks) * 2;
blocks = xrep_rtbitmap_calc_blocks(mp, mp->m_sb.sb_rbmblocks);
if (blocks > UINT_MAX)
return -EOPNOTSUPP;
@@ -512,7 +517,7 @@ xrep_rtbitmap(
struct xchk_rtbitmap *rtb = sc->buf;
struct xfs_mount *mp = sc->mp;
struct xfs_group *xg = rtg_group(sc->sr.rtg);
unsigned long long blocks = 0;
unsigned long long blocks;
unsigned int busy_gen;
int error;
@@ -532,15 +537,20 @@ xrep_rtbitmap(
* figure out if we need to adjust the block reservation in the
* transaction.
*/
blocks = xfs_bmbt_calc_size(mp, rtb->rbmblocks);
blocks = xrep_rtbitmap_calc_blocks(mp, rtb->rbmblocks);
if (blocks > UINT_MAX)
return -EOPNOTSUPP;
if (blocks > rtb->resblks) {
error = xfs_trans_reserve_more(sc->tp, blocks, 0);
uint64_t delta = blocks - rtb->resblks;
if (delta > UINT_MAX)
return -EOPNOTSUPP;
error = xfs_trans_reserve_more(sc->tp, delta, 0);
if (error)
return error;
rtb->resblks += blocks;
rtb->resblks += delta;
}
/* Fix inode core and forks. */
+1 -1
View File
@@ -358,7 +358,7 @@ xchk_rtsummary(
* EFSCORRUPTED means the rtbitmap is corrupt, which is an xref
* error since we're checking the summary file.
*/
xchk_ip_set_corrupt(sc, rbmip);
xchk_ip_xref_set_corrupt(sc, rbmip);
return 0;
}
if (error)
+36 -6
View File
@@ -642,11 +642,26 @@ out_unlock:
return error;
}
/*
* Allocate space or convert extents for a file according to @mode:
*
* XFS_ALLOC_FILE_SPACE_PREALLOC:
* Preallocate unwritten extents over holes across the range and mark the inode
* as preallocated.
*
* XFS_ALLOC_FILE_SPACE_WRITE_ZEROES:
* Allocate written extents over holes and convert unwritten extents in the
* range to written extents, initialising both to contain zeroes.
*
* This function does not update the file size; callers that extend the file
* are responsible for updating it once the extents are allocated.
*/
int
xfs_alloc_file_space(
struct xfs_inode *ip,
xfs_off_t offset,
xfs_off_t len)
xfs_off_t len,
enum xfs_alloc_file_space_mode mode)
{
xfs_mount_t *mp = ip->i_mount;
xfs_off_t count;
@@ -657,6 +672,7 @@ xfs_alloc_file_space(
int rt;
xfs_trans_t *tp;
xfs_bmbt_irec_t imaps[1], *imapp;
uint32_t bmapi_flags, nr_exts;
int error;
if (xfs_is_always_cow_inode(ip))
@@ -674,6 +690,19 @@ xfs_alloc_file_space(
if (len <= 0)
return -EINVAL;
switch (mode) {
case XFS_ALLOC_FILE_SPACE_PREALLOC:
bmapi_flags = XFS_BMAPI_PREALLOC;
nr_exts = XFS_IEXT_ADD_NOSPLIT_CNT;
break;
case XFS_ALLOC_FILE_SPACE_WRITE_ZEROES:
bmapi_flags = XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO;
nr_exts = XFS_IEXT_WRITE_UNWRITTEN_CNT;
break;
default:
return -EINVAL;
}
rt = XFS_IS_REALTIME_INODE(ip);
extsz = xfs_get_extsz_hint(ip);
@@ -733,8 +762,7 @@ xfs_alloc_file_space(
if (error)
break;
error = xfs_iext_count_extend(tp, ip, XFS_DATA_FORK,
XFS_IEXT_ADD_NOSPLIT_CNT);
error = xfs_iext_count_extend(tp, ip, XFS_DATA_FORK, nr_exts);
if (error)
goto error;
@@ -748,7 +776,7 @@ xfs_alloc_file_space(
* will eventually reach the requested range.
*/
error = xfs_bmapi_write(tp, ip, startoffset_fsb,
allocatesize_fsb, XFS_BMAPI_PREALLOC, 0, imapp,
allocatesize_fsb, bmapi_flags, 0, imapp,
&nimaps);
if (error) {
if (error != -ENOSR)
@@ -759,8 +787,10 @@ xfs_alloc_file_space(
allocatesize_fsb -= imapp->br_blockcount;
}
ip->i_diflags |= XFS_DIFLAG_PREALLOC;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
if (mode == XFS_ALLOC_FILE_SPACE_PREALLOC) {
ip->i_diflags |= XFS_DIFLAG_PREALLOC;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
}
error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
+6 -1
View File
@@ -55,8 +55,13 @@ int xfs_bmap_last_extent(struct xfs_trans *tp, struct xfs_inode *ip,
int *is_empty);
/* preallocation and hole punch interface */
enum xfs_alloc_file_space_mode {
XFS_ALLOC_FILE_SPACE_PREALLOC,
XFS_ALLOC_FILE_SPACE_WRITE_ZEROES,
};
int xfs_alloc_file_space(struct xfs_inode *ip, xfs_off_t offset,
xfs_off_t len);
xfs_off_t len, enum xfs_alloc_file_space_mode mode);
int xfs_free_file_space(struct xfs_inode *ip, xfs_off_t offset,
xfs_off_t len, struct xfs_zone_alloc_ctx *ac);
int xfs_collapse_file_space(struct xfs_inode *, xfs_off_t offset,
+1 -1
View File
@@ -114,7 +114,7 @@ xfs_buf_free(
vfree(bp->b_addr);
else if (bp->b_flags & _XBF_KMEM)
kfree(bp->b_addr);
else
else if (bp->b_addr)
folio_put(virt_to_folio(bp->b_addr));
call_rcu(&bp->b_rcu, xfs_buf_free_callback);
+41 -16
View File
@@ -461,7 +461,7 @@ xlog_recover_validate_buf_type(
* given buffer. The bitmap in the buf log format structure indicates
* where to place the logged data.
*/
STATIC void
STATIC int
xlog_recover_do_reg_buffer(
struct xfs_mount *mp,
struct xlog_recover_item *item,
@@ -489,8 +489,24 @@ xlog_recover_do_reg_buffer(
ASSERT(nbits > 0);
ASSERT(item->ri_buf[i].iov_base != NULL);
ASSERT(item->ri_buf[i].iov_len % XFS_BLF_CHUNK == 0);
ASSERT(BBTOB(bp->b_length) >=
((uint)bit << XFS_BLF_SHIFT) + (nbits << XFS_BLF_SHIFT));
/*
* The bitmap is only trustworthy to the extent that it
* describes a region that actually fits inside the buffer we
* read in based on the (attacker-controlled) blf_len. Do not
* rely on an ASSERT() for this -- it compiles away entirely on
* non-DEBUG kernels, which is exactly where this matters, so
* validate it for real and abort recovery of this buffer rather
* than copying past the end of it.
*/
if (XFS_IS_CORRUPT(mp, BBTOB(bp->b_length) <
((uint)bit << XFS_BLF_SHIFT) +
(nbits << XFS_BLF_SHIFT))) {
xfs_alert(mp,
"Bad buffer log item dirty bitmap (bit %d, nbits %d) for %d-byte buffer at daddr 0x%llx.",
bit, nbits, BBTOB(bp->b_length),
xfs_buf_daddr(bp));
return -EFSCORRUPTED;
}
/*
* The dirty regions logged in the buffer, even though
@@ -544,6 +560,7 @@ xlog_recover_do_reg_buffer(
ASSERT(i == item->ri_total);
xlog_recover_validate_buf_type(mp, bp, buf_f, current_lsn);
return 0;
}
/*
@@ -552,10 +569,10 @@ xlog_recover_do_reg_buffer(
* (ie. USR or GRP), then just toss this buffer away; don't recover it.
* Else, treat it as a regular buffer and do recovery.
*
* Return false if the buffer was tossed and true if we recovered the buffer to
* indicate to the caller if the buffer needs writing.
* Return 0 if the buffer was not recovered (tossed), 1 if it was recovered and
* needs writing, or a negative errno if recovery of the buffer failed.
*/
STATIC bool
STATIC int
xlog_recover_do_dquot_buffer(
struct xfs_mount *mp,
struct xlog *log,
@@ -564,6 +581,7 @@ xlog_recover_do_dquot_buffer(
struct xfs_buf_log_format *buf_f)
{
uint type;
int error;
trace_xfs_log_recover_buf_dquot_buf(log, buf_f);
@@ -571,7 +589,7 @@ xlog_recover_do_dquot_buffer(
* Filesystems are required to send in quota flags at mount time.
*/
if (!mp->m_qflags)
return false;
return 0;
type = 0;
if (buf_f->blf_flags & XFS_BLF_UDQUOT_BUF)
@@ -584,10 +602,12 @@ xlog_recover_do_dquot_buffer(
* This type of quotas was turned off, so ignore this buffer
*/
if (log->l_quotaoffs_flag & type)
return false;
return 0;
xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN);
return true;
error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN);
if (error)
return error;
return 1;
}
/*
@@ -724,7 +744,9 @@ xlog_recover_do_primary_sb_buffer(
xfs_rgnumber_t orig_rgcount = mp->m_sb.sb_rgcount;
int error;
xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
if (error)
return error;
if (orig_agcount == 0) {
xfs_alert(mp, "Trying to grow file system without AGs");
@@ -1081,11 +1103,11 @@ xlog_recover_buf_commit_pass2(
goto out_release;
} else if (buf_f->blf_flags &
(XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) {
bool dirty;
dirty = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
if (!dirty)
error = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
if (error <= 0)
goto out_release;
/* write dirty buffer */
error = 0;
} else if ((xfs_blft_from_flags(buf_f) & XFS_BLFT_SB_BUF) &&
xfs_buf_daddr(bp) == 0) {
error = xlog_recover_do_primary_sb_buffer(mp, item, bp, buf_f,
@@ -1105,7 +1127,10 @@ xlog_recover_buf_commit_pass2(
xfs_buf_relse(rtsb_bp);
}
} else {
xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f,
current_lsn);
if (error)
goto out_release;
}
/*
+88 -4
View File
@@ -1368,6 +1368,84 @@ xfs_falloc_force_zero(
return XFS_TEST_ERROR(ip->i_mount, XFS_ERRTAG_FORCE_ZERO_RANGE);
}
static int
xfs_falloc_write_zeroes(
struct file *file,
int mode,
loff_t offset,
loff_t len,
struct xfs_zone_alloc_ctx *ac)
{
struct inode *inode = file_inode(file);
struct xfs_inode *ip = XFS_I(inode);
loff_t new_size = 0;
int error;
/*
* XXX: There is an issue with bigrtalloc inodes where there can be blocks
* that are written after the EOF block. This breaks the promise of no
* written blocks past EOF. Return EOPNOTSUPP until it is fixed.
*/
if (xfs_is_always_cow_inode(ip) || xfs_inode_has_bigrtalloc(ip) ||
!bdev_write_zeroes_unmap_sectors(xfs_inode_buftarg(ip)->bt_bdev))
return -EOPNOTSUPP;
error = xfs_falloc_newsize(file, mode, offset, len, &new_size);
if (error)
return error;
/*
*
* |----------|----------|----------|----------|----------|
* ^ ^ ^ ^ ^ ^
* | | | | | |
* | offset | | end |
* | | | |
* offset_rd offset_ru end_rd end_ru
*
* xfs_free_file_space() punches the aligned interior offset_ru -> end_rd
* to holes and byte-zeroes the in-range parts of the partial edge blocks,
* offset -> offset_ru and end_rd -> end. xfs_zero_range() only touches
* already-written blocks here; it skips holes and unwritten extents, so
* unallocated/unwritten edge blocks are left for the allocation below.
*/
error = xfs_free_file_space(ip, offset, len, ac);
if (error)
return error;
/*
* Publish the new size while the punched range is still a hole, then
* fill it with written zeroes. Like the other fallocate modes we use
* xfs_falloc_setsize(), but it must run *before* we convert the range
* to written extents: xfs_setattr_size() zeroes [old EOF, new size) via
* xfs_zero_range(), which skips holes, so there is nothing to re-zero.
* It will also writeback partial EOF block before the on-disk size is
* logged.
* Note: extending the size before allocating means a failure below
* leaves the file larger with unallocated holes in the new range.
* That is safe as holes within i_size read back as zeroes and expose
* no stale data while the error is propagated to the caller.
*/
error = xfs_falloc_setsize(file, new_size);
if (error)
return error;
/*
* Allocate written, zeroed extents across the range. xfs_alloc_file_space()
* rounds outward to block granularity:
* - holes (the punched interior and any unallocated edge block) are
* allocated and zeroed;
* - unwritten extents (including unwritten edge blocks) are converted to
* written and zeroed;
* - Already written edge blocks are skipped. The out-of-range bytes of
* a written edge block keep their data (offset_rd -> offset and
* end -> end_rd); their in-range bytes (offset -> offset_ru and
* end_ru -> end were already zeroed by xfs_free_file_space().
*/
return xfs_alloc_file_space(ip, offset, len,
XFS_ALLOC_FILE_SPACE_WRITE_ZEROES);
}
/*
* Punch a hole and prealloc the range. We use a hole punch rather than
* unwritten extent conversion for two reasons:
@@ -1406,7 +1484,8 @@ xfs_falloc_zero_range(
len = round_up(offset + len, blksize) -
round_down(offset, blksize);
offset = round_down(offset, blksize);
error = xfs_alloc_file_space(ip, offset, len);
error = xfs_alloc_file_space(ip, offset, len,
XFS_ALLOC_FILE_SPACE_PREALLOC);
}
if (error)
return error;
@@ -1432,7 +1511,8 @@ xfs_falloc_unshare_range(
if (error)
return error;
error = xfs_alloc_file_space(XFS_I(inode), offset, len);
error = xfs_alloc_file_space(XFS_I(inode), offset, len,
XFS_ALLOC_FILE_SPACE_PREALLOC);
if (error)
return error;
return xfs_falloc_setsize(file, new_size);
@@ -1460,7 +1540,8 @@ xfs_falloc_allocate_range(
if (error)
return error;
error = xfs_alloc_file_space(XFS_I(inode), offset, len);
error = xfs_alloc_file_space(XFS_I(inode), offset, len,
XFS_ALLOC_FILE_SPACE_PREALLOC);
if (error)
return error;
return xfs_falloc_setsize(file, new_size);
@@ -1470,7 +1551,7 @@ xfs_falloc_allocate_range(
(FALLOC_FL_ALLOCATE_RANGE | FALLOC_FL_KEEP_SIZE | \
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE | \
FALLOC_FL_ZERO_RANGE | FALLOC_FL_INSERT_RANGE | \
FALLOC_FL_UNSHARE_RANGE)
FALLOC_FL_UNSHARE_RANGE | FALLOC_FL_WRITE_ZEROES)
STATIC long
__xfs_file_fallocate(
@@ -1522,6 +1603,9 @@ __xfs_file_fallocate(
case FALLOC_FL_ALLOCATE_RANGE:
error = xfs_falloc_allocate_range(file, mode, offset, len);
break;
case FALLOC_FL_WRITE_ZEROES:
error = xfs_falloc_write_zeroes(file, mode, offset, len, ac);
break;
default:
error = -EOPNOTSUPP;
break;
+7
View File
@@ -349,6 +349,13 @@ typedef struct xfs_mount {
/* Index of uuid record in the uuid xarray. */
unsigned int m_uuid_table_index;
/*
* Old io_pages/ra_pages valued in the main bdev BDI, and our initial
* calculated values.
*/
unsigned long m_old_io_pages, m_initial_io_pages;
unsigned long m_old_ra_pages, m_initial_ra_pages;
} xfs_mount_t;
#define M_IGEO(mp) (&(mp)->m_ino_geo)
+1 -1
View File
@@ -737,7 +737,7 @@ xfs_rtginode_ensure(
xfs_trans_cancel(tp);
if (error != -ENOENT)
return 0;
return error;
return xfs_rtginode_create(rtg, type, true);
}
+52 -1
View File
@@ -548,6 +548,52 @@ xfs_open_devices(
return error;
}
/*
* When using a RT device some or all data I/O is using the RT device, but
* the BDI is inherited from the main data device. When the underlying block
* device for the RT device has larger I/O sizes, the BDI settings might be
* incorrect, which is especially bad if the main device is a SSD and the
* RT device is a HDD, as the io_opt fixup in blk_apply_bdi_limits is missing
* for this case.
*
* Update the BDI values to the max of the data and RT device to cover our
* bases.
*/
static void
xfs_update_bdi_rahead(
struct xfs_mount *mp)
{
struct backing_dev_info *rt_bdi =
mp->m_rtdev_targp->bt_bdev->bd_disk->bdi;
struct backing_dev_info *sb_bdi = mp->m_super->s_bdi;
mp->m_old_io_pages = sb_bdi->io_pages;
mp->m_old_ra_pages = sb_bdi->ra_pages;
sb_bdi->io_pages = mp->m_initial_io_pages =
max(sb_bdi->io_pages, rt_bdi->io_pages);
sb_bdi->ra_pages = mp->m_initial_ra_pages =
max(sb_bdi->ra_pages, rt_bdi->ra_pages);
}
static void
xfs_restore_bdi_rahead(
struct xfs_mount *mp)
{
struct backing_dev_info *sb_bdi = mp->m_super->s_bdi;
if (sb_bdi->io_pages == mp->m_initial_io_pages)
sb_bdi->io_pages = mp->m_old_io_pages;
else
xfs_info(mp, "io_pages changed from %lu to %lu, not restoring.",
mp->m_initial_io_pages, sb_bdi->io_pages);
if (sb_bdi->ra_pages == mp->m_initial_ra_pages)
sb_bdi->ra_pages = mp->m_old_ra_pages;
else
xfs_info(mp, "ra_pages changed from %lu to %lu, not restoring.",
mp->m_initial_ra_pages, sb_bdi->ra_pages);
}
/*
* Setup xfs_mount buffer target pointers based on superblock
*/
@@ -585,6 +631,7 @@ xfs_setup_devices(
mp->m_sb.sb_sectsize, mp->m_sb.sb_rblocks);
if (error)
return error;
xfs_update_bdi_rahead(mp);
}
return 0;
@@ -2283,8 +2330,12 @@ static void
xfs_kill_sb(
struct super_block *sb)
{
struct xfs_mount *mp = XFS_M(sb);
if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp)
xfs_restore_bdi_rahead(mp);
kill_block_super(sb);
xfs_mount_free(XFS_M(sb));
xfs_mount_free(mp);
}
static struct file_system_type xfs_fs_type = {