mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branch 'for-next' of https://git.kernel.org/pub/scm/fs/xfs/xfs-linux.git
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user