Commit 4f86bb4b authored by Chandan Babu R's avatar Chandan Babu R

xfs: Conditionally upgrade existing inodes to use large extent counters

This commit enables upgrading existing inodes to use large extent counters
provided that underlying filesystem's superblock has large extent counter
feature enabled.
Reviewed-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Signed-off-by: default avatarChandan Babu R <chandan.babu@oracle.com>
parent 83a21c18
...@@ -776,6 +776,9 @@ xfs_attr_set( ...@@ -776,6 +776,9 @@ xfs_attr_set(
if (args->value || xfs_inode_hasattr(dp)) { if (args->value || xfs_inode_hasattr(dp)) {
error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK, error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK,
XFS_IEXT_ATTR_MANIP_CNT(rmt_blks)); XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
if (error == -EFBIG)
error = xfs_iext_count_upgrade(args->trans, dp,
XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
} }
......
...@@ -4524,14 +4524,16 @@ xfs_bmapi_convert_delalloc( ...@@ -4524,14 +4524,16 @@ xfs_bmapi_convert_delalloc(
return error; return error;
xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, 0);
error = xfs_iext_count_may_overflow(ip, whichfork, error = xfs_iext_count_may_overflow(ip, whichfork,
XFS_IEXT_ADD_NOSPLIT_CNT); XFS_IEXT_ADD_NOSPLIT_CNT);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip,
XFS_IEXT_ADD_NOSPLIT_CNT);
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
xfs_trans_ijoin(tp, ip, 0);
if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &bma.icur, &bma.got) || if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &bma.icur, &bma.got) ||
bma.got.br_startoff > offset_fsb) { bma.got.br_startoff > offset_fsb) {
/* /*
......
...@@ -934,6 +934,17 @@ enum xfs_dinode_fmt { ...@@ -934,6 +934,17 @@ enum xfs_dinode_fmt {
#define XFS_MAX_EXTCNT_DATA_FORK_SMALL ((xfs_extnum_t)((1ULL << 31) - 1)) #define XFS_MAX_EXTCNT_DATA_FORK_SMALL ((xfs_extnum_t)((1ULL << 31) - 1))
#define XFS_MAX_EXTCNT_ATTR_FORK_SMALL ((xfs_extnum_t)((1ULL << 15) - 1)) #define XFS_MAX_EXTCNT_ATTR_FORK_SMALL ((xfs_extnum_t)((1ULL << 15) - 1))
/*
* When we upgrade an inode to the large extent counts, the maximum value by
* which the extent count can increase is bound by the change in size of the
* on-disk field. No upgrade operation should ever be adding more than a few
* tens of extents, so if we get a really large value it is a sign of a code bug
* or corruption.
*/
#define XFS_MAX_EXTCNT_UPGRADE_NR \
min(XFS_MAX_EXTCNT_ATTR_FORK_LARGE - XFS_MAX_EXTCNT_ATTR_FORK_SMALL, \
XFS_MAX_EXTCNT_DATA_FORK_LARGE - XFS_MAX_EXTCNT_DATA_FORK_SMALL)
/* /*
* Inode minimum and maximum sizes. * Inode minimum and maximum sizes.
*/ */
......
...@@ -756,3 +756,27 @@ xfs_iext_count_may_overflow( ...@@ -756,3 +756,27 @@ xfs_iext_count_may_overflow(
return 0; return 0;
} }
/*
* Upgrade this inode's extent counter fields to be able to handle a potential
* increase in the extent count by nr_to_add. Normally this is the same
* quantity that caused xfs_iext_count_may_overflow() to return -EFBIG.
*/
int
xfs_iext_count_upgrade(
struct xfs_trans *tp,
struct xfs_inode *ip,
uint nr_to_add)
{
ASSERT(nr_to_add <= XFS_MAX_EXTCNT_UPGRADE_NR);
if (!xfs_has_large_extent_counts(ip->i_mount) ||
xfs_inode_has_large_extent_counts(ip) ||
XFS_TEST_ERROR(false, ip->i_mount, XFS_ERRTAG_REDUCE_MAX_IEXTENTS))
return -EFBIG;
ip->i_diflags2 |= XFS_DIFLAG2_NREXT64;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
return 0;
}
...@@ -275,6 +275,8 @@ int xfs_ifork_verify_local_data(struct xfs_inode *ip); ...@@ -275,6 +275,8 @@ int xfs_ifork_verify_local_data(struct xfs_inode *ip);
int xfs_ifork_verify_local_attr(struct xfs_inode *ip); int xfs_ifork_verify_local_attr(struct xfs_inode *ip);
int xfs_iext_count_may_overflow(struct xfs_inode *ip, int whichfork, int xfs_iext_count_may_overflow(struct xfs_inode *ip, int whichfork,
int nr_to_add); int nr_to_add);
int xfs_iext_count_upgrade(struct xfs_trans *tp, struct xfs_inode *ip,
uint nr_to_add);
/* returns true if the fork has extents but they are not read in yet. */ /* returns true if the fork has extents but they are not read in yet. */
static inline bool xfs_need_iread_extents(struct xfs_ifork *ifp) static inline bool xfs_need_iread_extents(struct xfs_ifork *ifp)
......
...@@ -506,6 +506,8 @@ xfs_bui_item_recover( ...@@ -506,6 +506,8 @@ xfs_bui_item_recover(
iext_delta = XFS_IEXT_PUNCH_HOLE_CNT; iext_delta = XFS_IEXT_PUNCH_HOLE_CNT;
error = xfs_iext_count_may_overflow(ip, whichfork, iext_delta); error = xfs_iext_count_may_overflow(ip, whichfork, iext_delta);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip, iext_delta);
if (error) if (error)
goto err_cancel; goto err_cancel;
......
...@@ -859,6 +859,9 @@ xfs_alloc_file_space( ...@@ -859,6 +859,9 @@ xfs_alloc_file_space(
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK, error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK,
XFS_IEXT_ADD_NOSPLIT_CNT); XFS_IEXT_ADD_NOSPLIT_CNT);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip,
XFS_IEXT_ADD_NOSPLIT_CNT);
if (error) if (error)
goto error; goto error;
...@@ -914,6 +917,8 @@ xfs_unmap_extent( ...@@ -914,6 +917,8 @@ xfs_unmap_extent(
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK, error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK,
XFS_IEXT_PUNCH_HOLE_CNT); XFS_IEXT_PUNCH_HOLE_CNT);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip, XFS_IEXT_PUNCH_HOLE_CNT);
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
...@@ -1195,6 +1200,8 @@ xfs_insert_file_space( ...@@ -1195,6 +1200,8 @@ xfs_insert_file_space(
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK, error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK,
XFS_IEXT_PUNCH_HOLE_CNT); XFS_IEXT_PUNCH_HOLE_CNT);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip, XFS_IEXT_PUNCH_HOLE_CNT);
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
...@@ -1423,6 +1430,9 @@ xfs_swap_extent_rmap( ...@@ -1423,6 +1430,9 @@ xfs_swap_extent_rmap(
error = xfs_iext_count_may_overflow(ip, error = xfs_iext_count_may_overflow(ip,
XFS_DATA_FORK, XFS_DATA_FORK,
XFS_IEXT_SWAP_RMAP_CNT); XFS_IEXT_SWAP_RMAP_CNT);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip,
XFS_IEXT_SWAP_RMAP_CNT);
if (error) if (error)
goto out; goto out;
} }
...@@ -1431,6 +1441,9 @@ xfs_swap_extent_rmap( ...@@ -1431,6 +1441,9 @@ xfs_swap_extent_rmap(
error = xfs_iext_count_may_overflow(tip, error = xfs_iext_count_may_overflow(tip,
XFS_DATA_FORK, XFS_DATA_FORK,
XFS_IEXT_SWAP_RMAP_CNT); XFS_IEXT_SWAP_RMAP_CNT);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip,
XFS_IEXT_SWAP_RMAP_CNT);
if (error) if (error)
goto out; goto out;
} }
......
...@@ -322,6 +322,9 @@ xfs_dquot_disk_alloc( ...@@ -322,6 +322,9 @@ xfs_dquot_disk_alloc(
error = xfs_iext_count_may_overflow(quotip, XFS_DATA_FORK, error = xfs_iext_count_may_overflow(quotip, XFS_DATA_FORK,
XFS_IEXT_ADD_NOSPLIT_CNT); XFS_IEXT_ADD_NOSPLIT_CNT);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, quotip,
XFS_IEXT_ADD_NOSPLIT_CNT);
if (error) if (error)
goto err_cancel; goto err_cancel;
......
...@@ -251,6 +251,8 @@ xfs_iomap_write_direct( ...@@ -251,6 +251,8 @@ xfs_iomap_write_direct(
return error; return error;
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK, nr_exts); error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK, nr_exts);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip, nr_exts);
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
...@@ -555,6 +557,9 @@ xfs_iomap_write_unwritten( ...@@ -555,6 +557,9 @@ xfs_iomap_write_unwritten(
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK, error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK,
XFS_IEXT_WRITE_UNWRITTEN_CNT); XFS_IEXT_WRITE_UNWRITTEN_CNT);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip,
XFS_IEXT_WRITE_UNWRITTEN_CNT);
if (error) if (error)
goto error_on_bmapi_transaction; goto error_on_bmapi_transaction;
......
...@@ -620,6 +620,9 @@ xfs_reflink_end_cow_extent( ...@@ -620,6 +620,9 @@ xfs_reflink_end_cow_extent(
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK, error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK,
XFS_IEXT_REFLINK_END_COW_CNT); XFS_IEXT_REFLINK_END_COW_CNT);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip,
XFS_IEXT_REFLINK_END_COW_CNT);
if (error) if (error)
goto out_cancel; goto out_cancel;
...@@ -1121,6 +1124,8 @@ xfs_reflink_remap_extent( ...@@ -1121,6 +1124,8 @@ xfs_reflink_remap_extent(
++iext_delta; ++iext_delta;
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK, iext_delta); error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK, iext_delta);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip, iext_delta);
if (error) if (error)
goto out_cancel; goto out_cancel;
......
...@@ -806,6 +806,9 @@ xfs_growfs_rt_alloc( ...@@ -806,6 +806,9 @@ xfs_growfs_rt_alloc(
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK, error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK,
XFS_IEXT_ADD_NOSPLIT_CNT); XFS_IEXT_ADD_NOSPLIT_CNT);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip,
XFS_IEXT_ADD_NOSPLIT_CNT);
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment