xfs: clean up the rtbitmap code [v4.2 3/8]

Here are some cleanups and reorganization of the realtime bitmap code to share
 more of that code between userspace and the kernel.
 
 With a bit of luck, this should all go splendidly.
 
 Signed-off-by: Darrick J. Wong <djwong@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQ2qTKExjcn+O1o2YRKO3ySh0YRpgUCZtX/YwAKCRBKO3ySh0YR
 plcbAP9AlHyTFF8Ba3ELFh9XD3CIAqi7i5mKVn4p8klXJI5E1wEAw0g315kuIJWz
 v+6qMd7fVCRIhuEwni6Pw/obkclMogA=
 =+Wof
 -----END PGP SIGNATURE-----

Merge tag 'rtbitmap-cleanups-6.12_2024-09-02' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.12-mergeA

xfs: clean up the rtbitmap code [v4.2 3/8]

Here are some cleanups and reorganization of the realtime bitmap code to share
more of that code between userspace and the kernel.

With a bit of luck, this should all go splendidly.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>

* tag 'rtbitmap-cleanups-6.12_2024-09-02' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux:
  xfs: push transaction join out of xfs_rtbitmap_lock and xfs_rtgroup_lock
  xfs: factor out rtbitmap/summary initialization helpers
  xfs: factor out a xfs_last_rt_bmblock helper
  xfs: factor out a xfs_growfs_rt_bmblock helper
  xfs: push the calls to xfs_rtallocate_range out to xfs_bmap_rtalloc
  xfs: cleanup the calling convention for xfs_rtpick_extent
  xfs: add bounds checking to xfs_rt{bitmap,summary}_read_buf
  xfs: assert a valid limit in xfs_rtfind_forw
  xfs: remove the limit argument to xfs_rtfind_back
  xfs: make the RT rsum_cache mandatory
  xfs: factor out a xfs_validate_rt_geometry helper
  xfs: remove xfs_validate_rtextents
This commit is contained in:
Chandan Babu R 2024-09-03 09:13:18 +05:30
commit 0879dee5ce
7 changed files with 440 additions and 404 deletions

View File

@ -5376,7 +5376,8 @@ xfs_bmap_del_extent_real(
*/
if (!(tp->t_flags & XFS_TRANS_RTBITMAP_LOCKED)) {
tp->t_flags |= XFS_TRANS_RTBITMAP_LOCKED;
xfs_rtbitmap_lock(tp, mp);
xfs_rtbitmap_lock(mp);
xfs_rtbitmap_trans_join(tp);
}
error = xfs_rtfree_blocks(tp, del->br_startblock,
del->br_blockcount);

View File

@ -13,6 +13,8 @@
#include "xfs_mount.h"
#include "xfs_inode.h"
#include "xfs_bmap.h"
#include "xfs_bmap_btree.h"
#include "xfs_trans_space.h"
#include "xfs_trans.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
@ -69,7 +71,7 @@ xfs_rtbuf_cache_relse(
* Get a buffer for the bitmap or summary file block specified.
* The buffer is returned read and locked.
*/
int
static int
xfs_rtbuf_get(
struct xfs_rtalloc_args *args,
xfs_fileoff_t block, /* block number in bitmap or summary */
@ -138,15 +140,43 @@ xfs_rtbuf_get(
return 0;
}
int
xfs_rtbitmap_read_buf(
struct xfs_rtalloc_args *args,
xfs_fileoff_t block)
{
struct xfs_mount *mp = args->mp;
if (XFS_IS_CORRUPT(mp, block >= mp->m_sb.sb_rbmblocks)) {
xfs_rt_mark_sick(mp, XFS_SICK_RT_BITMAP);
return -EFSCORRUPTED;
}
return xfs_rtbuf_get(args, block, 0);
}
int
xfs_rtsummary_read_buf(
struct xfs_rtalloc_args *args,
xfs_fileoff_t block)
{
struct xfs_mount *mp = args->mp;
if (XFS_IS_CORRUPT(mp, block >= XFS_B_TO_FSB(mp, mp->m_rsumsize))) {
xfs_rt_mark_sick(args->mp, XFS_SICK_RT_SUMMARY);
return -EFSCORRUPTED;
}
return xfs_rtbuf_get(args, block, 1);
}
/*
* Searching backward from start to limit, find the first block whose
* allocated/free state is different from start's.
* Searching backward from start find the first block whose allocated/free state
* is different from start's.
*/
int
xfs_rtfind_back(
struct xfs_rtalloc_args *args,
xfs_rtxnum_t start, /* starting rtext to look at */
xfs_rtxnum_t limit, /* last rtext to look at */
xfs_rtxnum_t *rtx) /* out: start rtext found */
{
struct xfs_mount *mp = args->mp;
@ -175,7 +205,7 @@ xfs_rtfind_back(
*/
word = xfs_rtx_to_rbmword(mp, start);
bit = (int)(start & (XFS_NBWORD - 1));
len = start - limit + 1;
len = start + 1;
/*
* Compute match value, based on the bit at start: if 1 (free)
* then all-ones, else all-zeroes.
@ -316,6 +346,8 @@ xfs_rtfind_forw(
xfs_rtword_t incore;
unsigned int word; /* word number in the buffer */
ASSERT(start <= limit);
/*
* Compute and read in starting bitmap block for starting block.
*/
@ -698,7 +730,7 @@ xfs_rtfree_range(
* We need to find the beginning and end of the extent so we can
* properly update the summary.
*/
error = xfs_rtfind_back(args, start, 0, &preblock);
error = xfs_rtfind_back(args, start, &preblock);
if (error) {
return error;
}
@ -1169,23 +1201,25 @@ xfs_rtsummary_wordcount(
return XFS_FSB_TO_B(mp, blocks) >> XFS_WORDLOG;
}
/*
* Lock both realtime free space metadata inodes for a freespace update. If a
* transaction is given, the inodes will be joined to the transaction and the
* ILOCKs will be released on transaction commit.
*/
/* Lock both realtime free space metadata inodes for a freespace update. */
void
xfs_rtbitmap_lock(
struct xfs_trans *tp,
struct xfs_mount *mp)
{
xfs_ilock(mp->m_rbmip, XFS_ILOCK_EXCL | XFS_ILOCK_RTBITMAP);
if (tp)
xfs_trans_ijoin(tp, mp->m_rbmip, XFS_ILOCK_EXCL);
xfs_ilock(mp->m_rsumip, XFS_ILOCK_EXCL | XFS_ILOCK_RTSUM);
if (tp)
xfs_trans_ijoin(tp, mp->m_rsumip, XFS_ILOCK_EXCL);
}
/*
* Join both realtime free space metadata inodes to the transaction. The
* ILOCKs will be released on transaction commit.
*/
void
xfs_rtbitmap_trans_join(
struct xfs_trans *tp)
{
xfs_trans_ijoin(tp, tp->t_mountp->m_rbmip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, tp->t_mountp->m_rsumip, XFS_ILOCK_EXCL);
}
/* Unlock both realtime free space metadata inodes after a freespace update. */
@ -1225,3 +1259,127 @@ xfs_rtbitmap_unlock_shared(
if (rbmlock_flags & XFS_RBMLOCK_BITMAP)
xfs_iunlock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
}
static int
xfs_rtfile_alloc_blocks(
struct xfs_inode *ip,
xfs_fileoff_t offset_fsb,
xfs_filblks_t count_fsb,
struct xfs_bmbt_irec *map)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
int nmap = 1;
int error;
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growrtalloc,
XFS_GROWFSRT_SPACE_RES(mp, count_fsb), 0, 0, &tp);
if (error)
return error;
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
error = xfs_iext_count_extend(tp, ip, XFS_DATA_FORK,
XFS_IEXT_ADD_NOSPLIT_CNT);
if (error)
goto out_trans_cancel;
error = xfs_bmapi_write(tp, ip, offset_fsb, count_fsb,
XFS_BMAPI_METADATA, 0, map, &nmap);
if (error)
goto out_trans_cancel;
return xfs_trans_commit(tp);
out_trans_cancel:
xfs_trans_cancel(tp);
return error;
}
/* Get a buffer for the block. */
static int
xfs_rtfile_initialize_block(
struct xfs_inode *ip,
xfs_fsblock_t fsbno,
void *data)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
struct xfs_buf *bp;
const size_t copylen = mp->m_blockwsize << XFS_WORDLOG;
enum xfs_blft buf_type;
int error;
if (ip == mp->m_rsumip)
buf_type = XFS_BLFT_RTSUMMARY_BUF;
else
buf_type = XFS_BLFT_RTBITMAP_BUF;
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growrtzero, 0, 0, 0, &tp);
if (error)
return error;
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
error = xfs_trans_get_buf(tp, mp->m_ddev_targp,
XFS_FSB_TO_DADDR(mp, fsbno), mp->m_bsize, 0, &bp);
if (error) {
xfs_trans_cancel(tp);
return error;
}
xfs_trans_buf_set_type(tp, bp, buf_type);
bp->b_ops = &xfs_rtbuf_ops;
if (data)
memcpy(bp->b_addr, data, copylen);
else
memset(bp->b_addr, 0, copylen);
xfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
return xfs_trans_commit(tp);
}
/*
* Allocate space to the bitmap or summary file, and zero it, for growfs.
* @data must be a contiguous buffer large enough to fill all blocks in the
* file; or NULL to initialize the contents to zeroes.
*/
int
xfs_rtfile_initialize_blocks(
struct xfs_inode *ip, /* inode (bitmap/summary) */
xfs_fileoff_t offset_fsb, /* offset to start from */
xfs_fileoff_t end_fsb, /* offset to allocate to */
void *data) /* data to fill the blocks */
{
struct xfs_mount *mp = ip->i_mount;
const size_t copylen = mp->m_blockwsize << XFS_WORDLOG;
while (offset_fsb < end_fsb) {
struct xfs_bmbt_irec map;
xfs_filblks_t i;
int error;
error = xfs_rtfile_alloc_blocks(ip, offset_fsb,
end_fsb - offset_fsb, &map);
if (error)
return error;
/*
* Now we need to clear the allocated blocks.
*
* Do this one block per transaction, to keep it simple.
*/
for (i = 0; i < map.br_blockcount; i++) {
error = xfs_rtfile_initialize_block(ip,
map.br_startblock + i, data);
if (error)
return error;
if (data)
data += copylen;
}
offset_fsb = map.br_startoff + map.br_blockcount;
}
return 0;
}

View File

@ -293,30 +293,12 @@ typedef int (*xfs_rtalloc_query_range_fn)(
#ifdef CONFIG_XFS_RT
void xfs_rtbuf_cache_relse(struct xfs_rtalloc_args *args);
int xfs_rtbuf_get(struct xfs_rtalloc_args *args, xfs_fileoff_t block,
int issum);
static inline int
xfs_rtbitmap_read_buf(
struct xfs_rtalloc_args *args,
xfs_fileoff_t block)
{
return xfs_rtbuf_get(args, block, 0);
}
static inline int
xfs_rtsummary_read_buf(
struct xfs_rtalloc_args *args,
xfs_fileoff_t block)
{
return xfs_rtbuf_get(args, block, 1);
}
int xfs_rtbitmap_read_buf(struct xfs_rtalloc_args *args, xfs_fileoff_t block);
int xfs_rtsummary_read_buf(struct xfs_rtalloc_args *args, xfs_fileoff_t block);
int xfs_rtcheck_range(struct xfs_rtalloc_args *args, xfs_rtxnum_t start,
xfs_rtxlen_t len, int val, xfs_rtxnum_t *new, int *stat);
int xfs_rtfind_back(struct xfs_rtalloc_args *args, xfs_rtxnum_t start,
xfs_rtxnum_t limit, xfs_rtxnum_t *rtblock);
xfs_rtxnum_t *rtblock);
int xfs_rtfind_forw(struct xfs_rtalloc_args *args, xfs_rtxnum_t start,
xfs_rtxnum_t limit, xfs_rtxnum_t *rtblock);
int xfs_rtmodify_range(struct xfs_rtalloc_args *args, xfs_rtxnum_t start,
@ -361,8 +343,12 @@ xfs_filblks_t xfs_rtsummary_blockcount(struct xfs_mount *mp,
unsigned long long xfs_rtsummary_wordcount(struct xfs_mount *mp,
unsigned int rsumlevels, xfs_extlen_t rbmblocks);
void xfs_rtbitmap_lock(struct xfs_trans *tp, struct xfs_mount *mp);
int xfs_rtfile_initialize_blocks(struct xfs_inode *ip,
xfs_fileoff_t offset_fsb, xfs_fileoff_t end_fsb, void *data);
void xfs_rtbitmap_lock(struct xfs_mount *mp);
void xfs_rtbitmap_unlock(struct xfs_mount *mp);
void xfs_rtbitmap_trans_join(struct xfs_trans *tp);
/* Lock the rt bitmap inode in shared mode */
#define XFS_RBMLOCK_BITMAP (1U << 0)
@ -391,7 +377,8 @@ xfs_rtbitmap_blockcount(struct xfs_mount *mp, xfs_rtbxlen_t rtextents)
# define xfs_rtbitmap_wordcount(mp, r) (0)
# define xfs_rtsummary_blockcount(mp, l, b) (0)
# define xfs_rtsummary_wordcount(mp, l, b) (0)
# define xfs_rtbitmap_lock(tp, mp) do { } while (0)
# define xfs_rtbitmap_lock(mp) do { } while (0)
# define xfs_rtbitmap_trans_join(tp) do { } while (0)
# define xfs_rtbitmap_unlock(mp) do { } while (0)
# define xfs_rtbitmap_lock_shared(mp, lf) do { } while (0)
# define xfs_rtbitmap_unlock_shared(mp, lf) do { } while (0)

View File

@ -232,6 +232,38 @@ xfs_validate_sb_read(
return 0;
}
static uint64_t
xfs_sb_calc_rbmblocks(
struct xfs_sb *sbp)
{
return howmany_64(sbp->sb_rextents, NBBY * sbp->sb_blocksize);
}
/* Validate the realtime geometry */
bool
xfs_validate_rt_geometry(
struct xfs_sb *sbp)
{
if (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE ||
sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE)
return false;
if (sbp->sb_rblocks == 0) {
if (sbp->sb_rextents != 0 || sbp->sb_rbmblocks != 0 ||
sbp->sb_rextslog != 0 || sbp->sb_frextents != 0)
return false;
return true;
}
if (sbp->sb_rextents == 0 ||
sbp->sb_rextents != div_u64(sbp->sb_rblocks, sbp->sb_rextsize) ||
sbp->sb_rextslog != xfs_compute_rextslog(sbp->sb_rextents) ||
sbp->sb_rbmblocks != xfs_sb_calc_rbmblocks(sbp))
return false;
return true;
}
/* Check all the superblock fields we care about when writing one out. */
STATIC int
xfs_validate_sb_write(
@ -491,39 +523,13 @@ xfs_validate_sb_common(
}
}
/* Validate the realtime geometry; stolen from xfs_repair */
if (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE ||
sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) {
if (!xfs_validate_rt_geometry(sbp)) {
xfs_notice(mp,
"realtime extent sanity check failed");
"realtime %sgeometry check failed",
sbp->sb_rblocks ? "" : "zeroed ");
return -EFSCORRUPTED;
}
if (sbp->sb_rblocks == 0) {
if (sbp->sb_rextents != 0 || sbp->sb_rbmblocks != 0 ||
sbp->sb_rextslog != 0 || sbp->sb_frextents != 0) {
xfs_notice(mp,
"realtime zeroed geometry check failed");
return -EFSCORRUPTED;
}
} else {
uint64_t rexts;
uint64_t rbmblocks;
rexts = div_u64(sbp->sb_rblocks, sbp->sb_rextsize);
rbmblocks = howmany_64(sbp->sb_rextents,
NBBY * sbp->sb_blocksize);
if (!xfs_validate_rtextents(rexts) ||
sbp->sb_rextents != rexts ||
sbp->sb_rextslog != xfs_compute_rextslog(rexts) ||
sbp->sb_rbmblocks != rbmblocks) {
xfs_notice(mp,
"realtime geometry sanity check failed");
return -EFSCORRUPTED;
}
}
/*
* Either (sb_unit and !hasdalign) or (!sb_unit and hasdalign)
* would imply the image is corrupted.

View File

@ -38,6 +38,7 @@ extern int xfs_sb_get_secondary(struct xfs_mount *mp,
bool xfs_validate_stripe_geometry(struct xfs_mount *mp,
__s64 sunit, __s64 swidth, int sectorsize, bool may_repair,
bool silent);
bool xfs_validate_rt_geometry(struct xfs_sb *sbp);
uint8_t xfs_compute_rextslog(xfs_rtbxlen_t rtextents);

View File

@ -235,16 +235,4 @@ bool xfs_verify_fileoff(struct xfs_mount *mp, xfs_fileoff_t off);
bool xfs_verify_fileext(struct xfs_mount *mp, xfs_fileoff_t off,
xfs_fileoff_t len);
/* Do we support an rt volume having this number of rtextents? */
static inline bool
xfs_validate_rtextents(
xfs_rtbxlen_t rtextents)
{
/* No runt rt volumes */
if (rtextents == 0)
return false;
return true;
}
#endif /* __XFS_TYPES_H__ */

View File

@ -142,7 +142,7 @@ xfs_rtallocate_range(
* We need to find the beginning and end of the extent so we can
* properly update the summary.
*/
error = xfs_rtfind_back(args, start, 0, &preblock);
error = xfs_rtfind_back(args, start, &preblock);
if (error)
return error;
@ -259,9 +259,9 @@ xfs_rtallocate_extent_block(
/*
* i for maxlen is all free, allocate and return that.
*/
bestlen = maxlen;
besti = i;
goto allocate;
*len = maxlen;
*rtx = i;
return 0;
}
/*
@ -312,12 +312,8 @@ xfs_rtallocate_extent_block(
}
/*
* Allocate besti for bestlen & return that.
* Pick besti for bestlen & return that.
*/
allocate:
error = xfs_rtallocate_range(args, besti, bestlen);
if (error)
return error;
*len = bestlen;
*rtx = besti;
return 0;
@ -371,12 +367,6 @@ xfs_rtallocate_extent_exact(
}
}
/*
* Allocate what we can and return it.
*/
error = xfs_rtallocate_range(args, start, maxlen);
if (error)
return error;
*len = maxlen;
*rtx = start;
return 0;
@ -429,7 +419,6 @@ xfs_rtallocate_extent_near(
if (error != -ENOSPC)
return error;
bbno = xfs_rtx_to_rbmblock(mp, start);
i = 0;
j = -1;
@ -552,11 +541,11 @@ xfs_rtalloc_sumlevel(
xfs_rtxnum_t *rtx) /* out: start rtext allocated */
{
xfs_fileoff_t i; /* bitmap block number */
int error;
for (i = 0; i < args->mp->m_sb.sb_rbmblocks; i++) {
xfs_suminfo_t sum; /* summary information for extents */
xfs_rtxnum_t n; /* next rtext to be tried */
int error;
error = xfs_rtget_summary(args, l, i, &sum);
if (error)
@ -652,136 +641,20 @@ xfs_rtallocate_extent_size(
return -ENOSPC;
}
/*
* Allocate space to the bitmap or summary file, and zero it, for growfs.
*/
STATIC int
xfs_growfs_rt_alloc(
struct xfs_mount *mp, /* file system mount point */
xfs_extlen_t oblocks, /* old count of blocks */
xfs_extlen_t nblocks, /* new count of blocks */
struct xfs_inode *ip) /* inode (bitmap/summary) */
{
xfs_fileoff_t bno; /* block number in file */
struct xfs_buf *bp; /* temporary buffer for zeroing */
xfs_daddr_t d; /* disk block address */
int error; /* error return value */
xfs_fsblock_t fsbno; /* filesystem block for bno */
struct xfs_bmbt_irec map; /* block map output */
int nmap; /* number of block maps */
int resblks; /* space reservation */
enum xfs_blft buf_type;
struct xfs_trans *tp;
if (ip == mp->m_rsumip)
buf_type = XFS_BLFT_RTSUMMARY_BUF;
else
buf_type = XFS_BLFT_RTBITMAP_BUF;
/*
* Allocate space to the file, as necessary.
*/
while (oblocks < nblocks) {
resblks = XFS_GROWFSRT_SPACE_RES(mp, nblocks - oblocks);
/*
* Reserve space & log for one extent added to the file.
*/
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growrtalloc, resblks,
0, 0, &tp);
if (error)
return error;
/*
* Lock the inode.
*/
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
error = xfs_iext_count_extend(tp, ip, XFS_DATA_FORK,
XFS_IEXT_ADD_NOSPLIT_CNT);
if (error)
goto out_trans_cancel;
/*
* Allocate blocks to the bitmap file.
*/
nmap = 1;
error = xfs_bmapi_write(tp, ip, oblocks, nblocks - oblocks,
XFS_BMAPI_METADATA, 0, &map, &nmap);
if (error)
goto out_trans_cancel;
/*
* Free any blocks freed up in the transaction, then commit.
*/
error = xfs_trans_commit(tp);
if (error)
return error;
/*
* Now we need to clear the allocated blocks.
* Do this one block per transaction, to keep it simple.
*/
for (bno = map.br_startoff, fsbno = map.br_startblock;
bno < map.br_startoff + map.br_blockcount;
bno++, fsbno++) {
/*
* Reserve log for one block zeroing.
*/
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growrtzero,
0, 0, 0, &tp);
if (error)
return error;
/*
* Lock the bitmap inode.
*/
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
/*
* Get a buffer for the block.
*/
d = XFS_FSB_TO_DADDR(mp, fsbno);
error = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
mp->m_bsize, 0, &bp);
if (error)
goto out_trans_cancel;
xfs_trans_buf_set_type(tp, bp, buf_type);
bp->b_ops = &xfs_rtbuf_ops;
memset(bp->b_addr, 0, mp->m_sb.sb_blocksize);
xfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
/*
* Commit the transaction.
*/
error = xfs_trans_commit(tp);
if (error)
return error;
}
/*
* Go on to the next extent, if any.
*/
oblocks = map.br_startoff + map.br_blockcount;
}
return 0;
out_trans_cancel:
xfs_trans_cancel(tp);
return error;
}
static void
static int
xfs_alloc_rsum_cache(
xfs_mount_t *mp, /* file system mount structure */
xfs_extlen_t rbmblocks) /* number of rt bitmap blocks */
struct xfs_mount *mp,
xfs_extlen_t rbmblocks)
{
/*
* The rsum cache is initialized to the maximum value, which is
* trivially an upper bound on the maximum level with any free extents.
* We can continue without the cache if it couldn't be allocated.
*/
mp->m_rsum_cache = kvmalloc(rbmblocks, GFP_KERNEL);
if (mp->m_rsum_cache)
memset(mp->m_rsum_cache, -1, rbmblocks);
else
xfs_warn(mp, "could not allocate realtime summary cache");
if (!mp->m_rsum_cache)
return -ENOMEM;
memset(mp->m_rsum_cache, -1, rbmblocks);
return 0;
}
/*
@ -817,9 +690,166 @@ xfs_growfs_rt_fixup_extsize(
return error;
}
static int
xfs_growfs_rt_bmblock(
struct xfs_mount *mp,
xfs_rfsblock_t nrblocks,
xfs_agblock_t rextsize,
xfs_fileoff_t bmbno)
{
struct xfs_inode *rbmip = mp->m_rbmip;
struct xfs_inode *rsumip = mp->m_rsumip;
struct xfs_rtalloc_args args = {
.mp = mp,
};
struct xfs_rtalloc_args nargs = {
};
struct xfs_mount *nmp;
xfs_rfsblock_t nrblocks_step;
xfs_rtbxlen_t freed_rtx;
int error;
nrblocks_step = (bmbno + 1) * NBBY * mp->m_sb.sb_blocksize * rextsize;
nmp = nargs.mp = kmemdup(mp, sizeof(*mp), GFP_KERNEL);
if (!nmp)
return -ENOMEM;
/*
* Calculate new sb and mount fields for this round.
*/
nmp->m_rtxblklog = -1; /* don't use shift or masking */
nmp->m_sb.sb_rextsize = rextsize;
nmp->m_sb.sb_rbmblocks = bmbno + 1;
nmp->m_sb.sb_rblocks = min(nrblocks, nrblocks_step);
nmp->m_sb.sb_rextents = xfs_rtb_to_rtx(nmp, nmp->m_sb.sb_rblocks);
nmp->m_sb.sb_rextslog = xfs_compute_rextslog(nmp->m_sb.sb_rextents);
nmp->m_rsumlevels = nmp->m_sb.sb_rextslog + 1;
nmp->m_rsumsize = XFS_FSB_TO_B(mp,
xfs_rtsummary_blockcount(mp, nmp->m_rsumlevels,
nmp->m_sb.sb_rbmblocks));
/* recompute growfsrt reservation from new rsumsize */
xfs_trans_resv_calc(nmp, &nmp->m_resv);
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growrtfree, 0, 0, 0,
&args.tp);
if (error)
goto out_free;
nargs.tp = args.tp;
xfs_rtbitmap_lock(mp);
xfs_rtbitmap_trans_join(args.tp);
/*
* Update the bitmap inode's size ondisk and incore. We need to update
* the incore size so that inode inactivation won't punch what it thinks
* are "posteof" blocks.
*/
rbmip->i_disk_size = nmp->m_sb.sb_rbmblocks * nmp->m_sb.sb_blocksize;
i_size_write(VFS_I(rbmip), rbmip->i_disk_size);
xfs_trans_log_inode(args.tp, rbmip, XFS_ILOG_CORE);
/*
* Update the summary inode's size. We need to update the incore size
* so that inode inactivation won't punch what it thinks are "posteof"
* blocks.
*/
rsumip->i_disk_size = nmp->m_rsumsize;
i_size_write(VFS_I(rsumip), rsumip->i_disk_size);
xfs_trans_log_inode(args.tp, rsumip, XFS_ILOG_CORE);
/*
* Copy summary data from old to new sizes when the real size (not
* block-aligned) changes.
*/
if (mp->m_sb.sb_rbmblocks != nmp->m_sb.sb_rbmblocks ||
mp->m_rsumlevels != nmp->m_rsumlevels) {
error = xfs_rtcopy_summary(&args, &nargs);
if (error)
goto out_cancel;
}
/*
* Update superblock fields.
*/
if (nmp->m_sb.sb_rextsize != mp->m_sb.sb_rextsize)
xfs_trans_mod_sb(args.tp, XFS_TRANS_SB_REXTSIZE,
nmp->m_sb.sb_rextsize - mp->m_sb.sb_rextsize);
if (nmp->m_sb.sb_rbmblocks != mp->m_sb.sb_rbmblocks)
xfs_trans_mod_sb(args.tp, XFS_TRANS_SB_RBMBLOCKS,
nmp->m_sb.sb_rbmblocks - mp->m_sb.sb_rbmblocks);
if (nmp->m_sb.sb_rblocks != mp->m_sb.sb_rblocks)
xfs_trans_mod_sb(args.tp, XFS_TRANS_SB_RBLOCKS,
nmp->m_sb.sb_rblocks - mp->m_sb.sb_rblocks);
if (nmp->m_sb.sb_rextents != mp->m_sb.sb_rextents)
xfs_trans_mod_sb(args.tp, XFS_TRANS_SB_REXTENTS,
nmp->m_sb.sb_rextents - mp->m_sb.sb_rextents);
if (nmp->m_sb.sb_rextslog != mp->m_sb.sb_rextslog)
xfs_trans_mod_sb(args.tp, XFS_TRANS_SB_REXTSLOG,
nmp->m_sb.sb_rextslog - mp->m_sb.sb_rextslog);
/*
* Free the new extent.
*/
freed_rtx = nmp->m_sb.sb_rextents - mp->m_sb.sb_rextents;
error = xfs_rtfree_range(&nargs, mp->m_sb.sb_rextents, freed_rtx);
xfs_rtbuf_cache_relse(&nargs);
if (error)
goto out_cancel;
/*
* Mark more blocks free in the superblock.
*/
xfs_trans_mod_sb(args.tp, XFS_TRANS_SB_FREXTENTS, freed_rtx);
/*
* Update mp values into the real mp structure.
*/
mp->m_rsumlevels = nmp->m_rsumlevels;
mp->m_rsumsize = nmp->m_rsumsize;
/*
* Recompute the growfsrt reservation from the new rsumsize.
*/
xfs_trans_resv_calc(mp, &mp->m_resv);
error = xfs_trans_commit(args.tp);
if (error)
goto out_free;
/*
* Ensure the mount RT feature flag is now set.
*/
mp->m_features |= XFS_FEAT_REALTIME;
kfree(nmp);
return 0;
out_cancel:
xfs_trans_cancel(args.tp);
out_free:
kfree(nmp);
return error;
}
/*
* Visible (exported) functions.
* Calculate the last rbmblock currently used.
*
* This also deals with the case where there were no rtextents before.
*/
static xfs_fileoff_t
xfs_last_rt_bmblock(
struct xfs_mount *mp)
{
xfs_fileoff_t bmbno = mp->m_sb.sb_rbmblocks;
/* Skip the current block if it is exactly full. */
if (xfs_rtx_to_rbmword(mp, mp->m_sb.sb_rextents) != 0)
bmbno--;
return bmbno;
}
/*
* Grow the realtime area of the filesystem.
@ -832,23 +862,14 @@ xfs_growfs_rt(
xfs_fileoff_t bmbno; /* bitmap block number */
struct xfs_buf *bp; /* temporary buffer */
int error; /* error return value */
xfs_mount_t *nmp; /* new (fake) mount structure */
xfs_rfsblock_t nrblocks; /* new number of realtime blocks */
xfs_extlen_t nrbmblocks; /* new number of rt bitmap blocks */
xfs_rtxnum_t nrextents; /* new number of realtime extents */
uint8_t nrextslog; /* new log2 of sb_rextents */
xfs_extlen_t nrsumblocks; /* new number of summary blocks */
uint nrsumlevels; /* new rt summary levels */
uint nrsumsize; /* new size of rt summary, bytes */
xfs_sb_t *nsbp; /* new superblock */
xfs_extlen_t rbmblocks; /* current number of rt bitmap blocks */
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;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@ -867,11 +888,10 @@ xfs_growfs_rt(
goto out_unlock;
/* Shrink not supported. */
if (in->newblocks <= sbp->sb_rblocks)
if (in->newblocks <= mp->m_sb.sb_rblocks)
goto out_unlock;
/* Can only change rt extent size when adding rt volume. */
if (sbp->sb_rblocks > 0 && in->extsize != sbp->sb_rextsize)
if (mp->m_sb.sb_rblocks > 0 && in->extsize != mp->m_sb.sb_rextsize)
goto out_unlock;
/* Range check the extent size. */
@ -884,15 +904,14 @@ xfs_growfs_rt(
if (xfs_has_rmapbt(mp) || xfs_has_reflink(mp) || xfs_has_quota(mp))
goto out_unlock;
nrblocks = in->newblocks;
error = xfs_sb_validate_fsb_count(sbp, nrblocks);
error = xfs_sb_validate_fsb_count(&mp->m_sb, in->newblocks);
if (error)
goto out_unlock;
/*
* Read in the last block of the device, make sure it exists.
*/
error = xfs_buf_read_uncached(mp->m_rtdev_targp,
XFS_FSB_TO_BB(mp, nrblocks - 1),
XFS_FSB_TO_BB(mp, in->newblocks - 1),
XFS_FSB_TO_BB(mp, 1), 0, &bp, NULL);
if (error)
goto out_unlock;
@ -901,17 +920,15 @@ xfs_growfs_rt(
/*
* Calculate new parameters. These are the final values to be reached.
*/
nrextents = nrblocks;
do_div(nrextents, in->extsize);
if (!xfs_validate_rtextents(nrextents)) {
nrextents = div_u64(in->newblocks, in->extsize);
if (nrextents == 0) {
error = -EINVAL;
goto out_unlock;
}
nrbmblocks = xfs_rtbitmap_blockcount(mp, nrextents);
nrextslog = xfs_compute_rextslog(nrextents);
nrsumlevels = nrextslog + 1;
nrsumblocks = xfs_rtsummary_blockcount(mp, nrsumlevels, nrbmblocks);
nrsumsize = XFS_FSB_TO_B(mp, nrsumblocks);
nrsumblocks = xfs_rtsummary_blockcount(mp,
xfs_compute_rextslog(nrextents) + 1, nrbmblocks);
/*
* New summary size can't be more than half the size of
* the log. This prevents us from getting a log overflow,
@ -931,154 +948,29 @@ xfs_growfs_rt(
/*
* Allocate space to the bitmap and summary files, as necessary.
*/
error = xfs_growfs_rt_alloc(mp, rbmblocks, nrbmblocks, mp->m_rbmip);
error = xfs_rtfile_initialize_blocks(mp->m_rbmip, rbmblocks,
nrbmblocks, NULL);
if (error)
goto out_unlock;
error = xfs_growfs_rt_alloc(mp, rsumblocks, nrsumblocks, mp->m_rsumip);
error = xfs_rtfile_initialize_blocks(mp->m_rsumip, rsumblocks,
nrsumblocks, NULL);
if (error)
goto out_unlock;
rsum_cache = mp->m_rsum_cache;
if (nrbmblocks != sbp->sb_rbmblocks)
xfs_alloc_rsum_cache(mp, nrbmblocks);
/*
* Allocate a new (fake) mount/sb.
*/
nmp = kmalloc(sizeof(*nmp), GFP_KERNEL | __GFP_NOFAIL);
/*
* Loop over the bitmap blocks.
* We will do everything one bitmap block at a time.
* Skip the current block if it is exactly full.
* This also deals with the case where there were no rtextents before.
*/
for (bmbno = sbp->sb_rbmblocks -
((sbp->sb_rextents & ((1 << mp->m_blkbit_log) - 1)) != 0);
bmbno < nrbmblocks;
bmbno++) {
struct xfs_rtalloc_args args = {
.mp = mp,
};
struct xfs_rtalloc_args nargs = {
.mp = nmp,
};
struct xfs_trans *tp;
xfs_rfsblock_t nrblocks_step;
*nmp = *mp;
nsbp = &nmp->m_sb;
/*
* Calculate new sb and mount fields for this round.
*/
nsbp->sb_rextsize = in->extsize;
nmp->m_rtxblklog = -1; /* don't use shift or masking */
nsbp->sb_rbmblocks = bmbno + 1;
nrblocks_step = (bmbno + 1) * NBBY * nsbp->sb_blocksize *
nsbp->sb_rextsize;
nsbp->sb_rblocks = min(nrblocks, nrblocks_step);
nsbp->sb_rextents = xfs_rtb_to_rtx(nmp, nsbp->sb_rblocks);
ASSERT(nsbp->sb_rextents != 0);
nsbp->sb_rextslog = xfs_compute_rextslog(nsbp->sb_rextents);
nrsumlevels = nmp->m_rsumlevels = nsbp->sb_rextslog + 1;
nrsumblocks = xfs_rtsummary_blockcount(mp, nrsumlevels,
nsbp->sb_rbmblocks);
nmp->m_rsumsize = nrsumsize = XFS_FSB_TO_B(mp, nrsumblocks);
/* recompute growfsrt reservation from new rsumsize */
xfs_trans_resv_calc(nmp, &nmp->m_resv);
/*
* Start a transaction, get the log reservation.
*/
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growrtfree, 0, 0, 0,
&tp);
if (nrbmblocks != mp->m_sb.sb_rbmblocks) {
error = xfs_alloc_rsum_cache(mp, nrbmblocks);
if (error)
break;
args.tp = tp;
nargs.tp = tp;
/*
* Lock out other callers by grabbing the bitmap and summary
* inode locks and joining them to the transaction.
*/
xfs_rtbitmap_lock(tp, mp);
/*
* Update the bitmap inode's size ondisk and incore. We need
* to update the incore size so that inode inactivation won't
* punch what it thinks are "posteof" blocks.
*/
mp->m_rbmip->i_disk_size =
nsbp->sb_rbmblocks * nsbp->sb_blocksize;
i_size_write(VFS_I(mp->m_rbmip), mp->m_rbmip->i_disk_size);
xfs_trans_log_inode(tp, mp->m_rbmip, XFS_ILOG_CORE);
/*
* Update the summary inode's size. We need to update the
* incore size so that inode inactivation won't punch what it
* thinks are "posteof" blocks.
*/
mp->m_rsumip->i_disk_size = nmp->m_rsumsize;
i_size_write(VFS_I(mp->m_rsumip), mp->m_rsumip->i_disk_size);
xfs_trans_log_inode(tp, mp->m_rsumip, XFS_ILOG_CORE);
/*
* Copy summary data from old to new sizes.
* Do this when the real size (not block-aligned) changes.
*/
if (sbp->sb_rbmblocks != nsbp->sb_rbmblocks ||
mp->m_rsumlevels != nmp->m_rsumlevels) {
error = xfs_rtcopy_summary(&args, &nargs);
if (error)
goto error_cancel;
}
/*
* Update superblock fields.
*/
if (nsbp->sb_rextsize != sbp->sb_rextsize)
xfs_trans_mod_sb(tp, XFS_TRANS_SB_REXTSIZE,
nsbp->sb_rextsize - sbp->sb_rextsize);
if (nsbp->sb_rbmblocks != sbp->sb_rbmblocks)
xfs_trans_mod_sb(tp, XFS_TRANS_SB_RBMBLOCKS,
nsbp->sb_rbmblocks - sbp->sb_rbmblocks);
if (nsbp->sb_rblocks != sbp->sb_rblocks)
xfs_trans_mod_sb(tp, XFS_TRANS_SB_RBLOCKS,
nsbp->sb_rblocks - sbp->sb_rblocks);
if (nsbp->sb_rextents != sbp->sb_rextents)
xfs_trans_mod_sb(tp, XFS_TRANS_SB_REXTENTS,
nsbp->sb_rextents - sbp->sb_rextents);
if (nsbp->sb_rextslog != sbp->sb_rextslog)
xfs_trans_mod_sb(tp, XFS_TRANS_SB_REXTSLOG,
nsbp->sb_rextslog - sbp->sb_rextslog);
/*
* Free new extent.
*/
error = xfs_rtfree_range(&nargs, sbp->sb_rextents,
nsbp->sb_rextents - sbp->sb_rextents);
xfs_rtbuf_cache_relse(&nargs);
if (error) {
error_cancel:
xfs_trans_cancel(tp);
break;
}
/*
* Mark more blocks free in the superblock.
*/
xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS,
nsbp->sb_rextents - sbp->sb_rextents);
/*
* Update mp values into the real mp structure.
*/
mp->m_rsumlevels = nrsumlevels;
mp->m_rsumsize = nrsumsize;
/* recompute growfsrt reservation from new rsumsize */
xfs_trans_resv_calc(mp, &mp->m_resv);
error = xfs_trans_commit(tp);
if (error)
break;
/* Ensure the mount RT feature flag is now set. */
mp->m_features |= XFS_FEAT_REALTIME;
goto out_unlock;
}
/* Initialize the free space bitmap one bitmap block at a time. */
for (bmbno = xfs_last_rt_bmblock(mp); bmbno < nrbmblocks; bmbno++) {
error = xfs_growfs_rt_bmblock(mp, in->newblocks, in->extsize,
bmbno);
if (error)
goto out_free;
}
if (error)
goto out_free;
if (old_rextsize != in->extsize) {
error = xfs_growfs_rt_fixup_extsize(mp);
@ -1090,11 +982,6 @@ xfs_growfs_rt(
error = xfs_update_secondary_sbs(mp);
out_free:
/*
* Free the fake mp structure.
*/
kfree(nmp);
/*
* If we had to allocate a new rsum_cache, we either need to free the
* old one (if we succeeded) or free the new one and restore the old one
@ -1268,7 +1155,9 @@ xfs_rtmount_inodes(
if (error)
goto out_rele_summary;
xfs_alloc_rsum_cache(mp, sbp->sb_rbmblocks);
error = xfs_alloc_rsum_cache(mp, sbp->sb_rbmblocks);
if (error)
goto out_rele_summary;
return 0;
out_rele_summary:
@ -1296,12 +1185,11 @@ xfs_rtunmount_inodes(
* of rtextents and the fraction.
* The fraction sequence is 0, 1/2, 1/4, 3/4, 1/8, ..., 7/8, 1/16, ...
*/
static int
static xfs_rtxnum_t
xfs_rtpick_extent(
xfs_mount_t *mp, /* file system mount point */
xfs_trans_t *tp, /* transaction pointer */
xfs_rtxlen_t len, /* allocation length (rtextents) */
xfs_rtxnum_t *pick) /* result rt extent */
xfs_rtxlen_t len) /* allocation length (rtextents) */
{
xfs_rtxnum_t b; /* result rtext */
int log2; /* log of sequence number */
@ -1332,8 +1220,7 @@ xfs_rtpick_extent(
ts.tv_sec = seq + 1;
inode_set_atime_to_ts(VFS_I(mp->m_rbmip), ts);
xfs_trans_log_inode(tp, mp->m_rbmip, XFS_ILOG_CORE);
*pick = b;
return 0;
return b;
}
static void
@ -1427,7 +1314,8 @@ xfs_bmap_rtalloc(
* Lock out modifications to both the RT bitmap and summary inodes
*/
if (!rtlocked) {
xfs_rtbitmap_lock(ap->tp, mp);
xfs_rtbitmap_lock(mp);
xfs_rtbitmap_trans_join(ap->tp);
rtlocked = true;
}
@ -1440,9 +1328,7 @@ xfs_bmap_rtalloc(
* If it's an allocation to an empty file at offset 0, pick an
* extent that will space things out in the rt area.
*/
error = xfs_rtpick_extent(mp, ap->tp, ralen, &start);
if (error)
return error;
start = xfs_rtpick_extent(mp, ap->tp, ralen);
} else {
start = 0;
}
@ -1467,9 +1353,12 @@ xfs_bmap_rtalloc(
error = xfs_rtallocate_extent_size(&args, raminlen,
ralen, &ralen, prod, &rtx);
}
xfs_rtbuf_cache_relse(&args);
if (error == -ENOSPC) {
if (error) {
xfs_rtbuf_cache_relse(&args);
if (error != -ENOSPC)
return error;
if (align > mp->m_sb.sb_rextsize) {
/*
* We previously enlarged the request length to try to
@ -1497,14 +1386,20 @@ xfs_bmap_rtalloc(
ap->length = 0;
return 0;
}
error = xfs_rtallocate_range(&args, rtx, ralen);
if (error)
return error;
goto out_release;
xfs_trans_mod_sb(ap->tp, ap->wasdel ?
XFS_TRANS_SB_RES_FREXTENTS : XFS_TRANS_SB_FREXTENTS,
-(long)ralen);
ap->blkno = xfs_rtx_to_rtb(mp, rtx);
ap->length = xfs_rtxlen_to_extlen(mp, ralen);
xfs_bmap_alloc_account(ap);
return 0;
out_release:
xfs_rtbuf_cache_relse(&args);
return error;
}