#include "xfs_platform.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
#include "xfs_bit.h"
#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"
#include "xfs_rtbitmap.h"
#include "xfs_health.h"
#include "xfs_sb.h"
#include "xfs_errortag.h"
#include "xfs_log.h"
#include "xfs_buf_item.h"
#include "xfs_extent_busy.h"
static xfs_failaddr_t
xfs_rtbuf_verify(
struct xfs_buf *bp)
{
struct xfs_mount *mp = bp->b_mount;
struct xfs_rtbuf_blkinfo *hdr = bp->b_addr;
if (!xfs_verify_magic(bp, hdr->rt_magic))
return __this_address;
if (!xfs_has_rtgroups(mp))
return __this_address;
if (!xfs_has_crc(mp))
return __this_address;
if (!uuid_equal(&hdr->rt_uuid, &mp->m_sb.sb_meta_uuid))
return __this_address;
if (hdr->rt_blkno != cpu_to_be64(xfs_buf_daddr(bp)))
return __this_address;
return NULL;
}
static void
xfs_rtbuf_verify_read(
struct xfs_buf *bp)
{
struct xfs_mount *mp = bp->b_mount;
struct xfs_rtbuf_blkinfo *hdr = bp->b_addr;
xfs_failaddr_t fa;
if (!xfs_has_rtgroups(mp))
return;
if (!xfs_log_check_lsn(mp, be64_to_cpu(hdr->rt_lsn))) {
fa = __this_address;
goto fail;
}
if (!xfs_buf_verify_cksum(bp, XFS_RTBUF_CRC_OFF)) {
fa = __this_address;
goto fail;
}
fa = xfs_rtbuf_verify(bp);
if (fa)
goto fail;
return;
fail:
xfs_verifier_error(bp, -EFSCORRUPTED, fa);
}
static void
xfs_rtbuf_verify_write(
struct xfs_buf *bp)
{
struct xfs_mount *mp = bp->b_mount;
struct xfs_rtbuf_blkinfo *hdr = bp->b_addr;
struct xfs_buf_log_item *bip = bp->b_log_item;
xfs_failaddr_t fa;
if (!xfs_has_rtgroups(mp))
return;
fa = xfs_rtbuf_verify(bp);
if (fa) {
xfs_verifier_error(bp, -EFSCORRUPTED, fa);
return;
}
if (bip)
hdr->rt_lsn = cpu_to_be64(bip->bli_item.li_lsn);
xfs_buf_update_cksum(bp, XFS_RTBUF_CRC_OFF);
}
const struct xfs_buf_ops xfs_rtbuf_ops = {
.name = "rtbuf",
.verify_read = xfs_rtbuf_verify_read,
.verify_write = xfs_rtbuf_verify_write,
};
const struct xfs_buf_ops xfs_rtbitmap_buf_ops = {
.name = "xfs_rtbitmap",
.magic = { 0, cpu_to_be32(XFS_RTBITMAP_MAGIC) },
.verify_read = xfs_rtbuf_verify_read,
.verify_write = xfs_rtbuf_verify_write,
.verify_struct = xfs_rtbuf_verify,
};
const struct xfs_buf_ops xfs_rtsummary_buf_ops = {
.name = "xfs_rtsummary",
.magic = { 0, cpu_to_be32(XFS_RTSUMMARY_MAGIC) },
.verify_read = xfs_rtbuf_verify_read,
.verify_write = xfs_rtbuf_verify_write,
.verify_struct = xfs_rtbuf_verify,
};
void
xfs_rtbuf_cache_relse(
struct xfs_rtalloc_args *args)
{
if (args->rbmbp) {
xfs_trans_brelse(args->tp, args->rbmbp);
args->rbmbp = NULL;
args->rbmoff = NULLFILEOFF;
}
if (args->sumbp) {
xfs_trans_brelse(args->tp, args->sumbp);
args->sumbp = NULL;
args->sumoff = NULLFILEOFF;
}
}
static int
xfs_rtbuf_get(
struct xfs_rtalloc_args *args,
xfs_fileoff_t block,
enum xfs_rtg_inodes type)
{
struct xfs_inode *ip = args->rtg->rtg_inodes[type];
struct xfs_mount *mp = args->mp;
struct xfs_buf **cbpp;
xfs_fileoff_t *coffp;
struct xfs_buf *bp;
struct xfs_bmbt_irec map;
enum xfs_blft buf_type;
int nmap = 1;
int error;
switch (type) {
case XFS_RTGI_SUMMARY:
cbpp = &args->sumbp;
coffp = &args->sumoff;
buf_type = XFS_BLFT_RTSUMMARY_BUF;
break;
case XFS_RTGI_BITMAP:
cbpp = &args->rbmbp;
coffp = &args->rbmoff;
buf_type = XFS_BLFT_RTBITMAP_BUF;
break;
default:
return -EINVAL;
}
if (*cbpp && *coffp == block)
return 0;
if (*cbpp) {
xfs_trans_brelse(args->tp, *cbpp);
*cbpp = NULL;
}
error = xfs_bmapi_read(ip, block, 1, &map, &nmap, 0);
if (error)
return error;
if (XFS_IS_CORRUPT(mp, nmap == 0 || !xfs_bmap_is_written_extent(&map))) {
xfs_rtginode_mark_sick(args->rtg, type);
return -EFSCORRUPTED;
}
ASSERT(map.br_startblock != NULLFSBLOCK);
error = xfs_trans_read_buf(mp, args->tp, mp->m_ddev_targp,
XFS_FSB_TO_DADDR(mp, map.br_startblock),
mp->m_bsize, 0, &bp,
xfs_rtblock_ops(mp, type));
if (xfs_metadata_is_sick(error))
xfs_rtginode_mark_sick(args->rtg, type);
if (error)
return error;
if (xfs_has_rtgroups(mp)) {
struct xfs_rtbuf_blkinfo *hdr = bp->b_addr;
if (hdr->rt_owner != cpu_to_be64(ip->i_ino)) {
xfs_buf_mark_corrupt(bp);
xfs_trans_brelse(args->tp, bp);
xfs_rtginode_mark_sick(args->rtg, type);
return -EFSCORRUPTED;
}
}
xfs_trans_buf_set_type(args->tp, bp, buf_type);
*cbpp = bp;
*coffp = block;
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_rtginode_mark_sick(args->rtg, XFS_RTGI_BITMAP);
return -EFSCORRUPTED;
}
return xfs_rtbuf_get(args, block, XFS_RTGI_BITMAP);
}
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 >= mp->m_rsumblocks)) {
xfs_rtginode_mark_sick(args->rtg, XFS_RTGI_SUMMARY);
return -EFSCORRUPTED;
}
return xfs_rtbuf_get(args, block, XFS_RTGI_SUMMARY);
}
int
xfs_rtfind_back(
struct xfs_rtalloc_args *args,
xfs_rtxnum_t start,
xfs_rtxnum_t *rtx)
{
struct xfs_mount *mp = args->mp;
int bit;
xfs_fileoff_t block;
int error;
xfs_rtxnum_t firstbit;
xfs_rtxnum_t i;
xfs_rtxnum_t len;
xfs_rtword_t mask;
xfs_rtword_t want;
xfs_rtword_t wdiff;
xfs_rtword_t incore;
unsigned int word;
block = xfs_rtx_to_rbmblock(mp, start);
error = xfs_rtbitmap_read_buf(args, block);
if (error)
return error;
word = xfs_rtx_to_rbmword(mp, start);
bit = (int)(start & (XFS_NBWORD - 1));
len = start + 1;
incore = xfs_rtbitmap_getword(args, word);
want = (incore & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
if (bit < XFS_NBWORD - 1) {
firstbit = max_t(xfs_srtblock_t, bit - len + 1, 0);
mask = (((xfs_rtword_t)1 << (bit - firstbit + 1)) - 1) <<
firstbit;
if ((wdiff = (incore ^ want) & mask)) {
i = bit - xfs_highbit32(wdiff);
*rtx = start - i + 1;
return 0;
}
i = bit - firstbit + 1;
if (--word == -1 && i < len) {
error = xfs_rtbitmap_read_buf(args, --block);
if (error)
return error;
word = mp->m_blockwsize - 1;
}
} else {
i = 0;
}
while (len - i >= XFS_NBWORD) {
incore = xfs_rtbitmap_getword(args, word);
if ((wdiff = incore ^ want)) {
i += XFS_NBWORD - 1 - xfs_highbit32(wdiff);
*rtx = start - i + 1;
return 0;
}
i += XFS_NBWORD;
if (--word == -1 && i < len) {
error = xfs_rtbitmap_read_buf(args, --block);
if (error)
return error;
word = mp->m_blockwsize - 1;
}
}
if (len - i) {
firstbit = XFS_NBWORD - (len - i);
mask = (((xfs_rtword_t)1 << (len - i)) - 1) << firstbit;
incore = xfs_rtbitmap_getword(args, word);
if ((wdiff = (incore ^ want) & mask)) {
i += XFS_NBWORD - 1 - xfs_highbit32(wdiff);
*rtx = start - i + 1;
return 0;
} else
i = len;
}
*rtx = start - i + 1;
return 0;
}
int
xfs_rtfind_forw(
struct xfs_rtalloc_args *args,
xfs_rtxnum_t start,
xfs_rtxnum_t limit,
xfs_rtxnum_t *rtx)
{
struct xfs_mount *mp = args->mp;
int bit;
xfs_fileoff_t block;
int error;
xfs_rtxnum_t i;
xfs_rtxnum_t lastbit;
xfs_rtxnum_t len;
xfs_rtword_t mask;
xfs_rtword_t want;
xfs_rtword_t wdiff;
xfs_rtword_t incore;
unsigned int word;
ASSERT(start <= limit);
block = xfs_rtx_to_rbmblock(mp, start);
error = xfs_rtbitmap_read_buf(args, block);
if (error)
return error;
word = xfs_rtx_to_rbmword(mp, start);
bit = (int)(start & (XFS_NBWORD - 1));
len = limit - start + 1;
incore = xfs_rtbitmap_getword(args, word);
want = (incore & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
if (bit) {
lastbit = min(bit + len, XFS_NBWORD);
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
if ((wdiff = (incore ^ want) & mask)) {
i = xfs_lowbit32(wdiff) - bit;
*rtx = start + i - 1;
return 0;
}
i = lastbit - bit;
if (++word == mp->m_blockwsize && i < len) {
error = xfs_rtbitmap_read_buf(args, ++block);
if (error)
return error;
word = 0;
}
} else {
i = 0;
}
while (len - i >= XFS_NBWORD) {
incore = xfs_rtbitmap_getword(args, word);
if ((wdiff = incore ^ want)) {
i += xfs_lowbit32(wdiff);
*rtx = start + i - 1;
return 0;
}
i += XFS_NBWORD;
if (++word == mp->m_blockwsize && i < len) {
error = xfs_rtbitmap_read_buf(args, ++block);
if (error)
return error;
word = 0;
}
}
if ((lastbit = len - i)) {
mask = ((xfs_rtword_t)1 << lastbit) - 1;
incore = xfs_rtbitmap_getword(args, word);
if ((wdiff = (incore ^ want) & mask)) {
i += xfs_lowbit32(wdiff);
*rtx = start + i - 1;
return 0;
} else
i = len;
}
*rtx = start + i - 1;
return 0;
}
static inline void
xfs_trans_log_rtsummary(
struct xfs_rtalloc_args *args,
unsigned int infoword)
{
struct xfs_buf *bp = args->sumbp;
size_t first, last;
first = (void *)xfs_rsumblock_infoptr(args, infoword) - bp->b_addr;
last = first + sizeof(xfs_suminfo_t) - 1;
xfs_trans_log_buf(args->tp, bp, first, last);
}
int
xfs_rtmodify_summary(
struct xfs_rtalloc_args *args,
int log,
xfs_fileoff_t bbno,
int delta)
{
struct xfs_mount *mp = args->mp;
xfs_rtsumoff_t so = xfs_rtsumoffs(mp, log, bbno);
uint8_t *rsum_cache = args->rtg->rtg_rsum_cache;
unsigned int infoword;
xfs_suminfo_t val;
int error;
error = xfs_rtsummary_read_buf(args, xfs_rtsumoffs_to_block(mp, so));
if (error)
return error;
infoword = xfs_rtsumoffs_to_infoword(mp, so);
val = xfs_suminfo_add(args, infoword, delta);
if (rsum_cache) {
if (val == 0 && log + 1 == rsum_cache[bbno])
rsum_cache[bbno] = log;
if (val != 0 && log >= rsum_cache[bbno])
rsum_cache[bbno] = log + 1;
}
xfs_trans_log_rtsummary(args, infoword);
return 0;
}
int
xfs_rtget_summary(
struct xfs_rtalloc_args *args,
int log,
xfs_fileoff_t bbno,
xfs_suminfo_t *sum)
{
struct xfs_mount *mp = args->mp;
xfs_rtsumoff_t so = xfs_rtsumoffs(mp, log, bbno);
int error;
error = xfs_rtsummary_read_buf(args, xfs_rtsumoffs_to_block(mp, so));
if (!error)
*sum = xfs_suminfo_get(args, xfs_rtsumoffs_to_infoword(mp, so));
return error;
}
static inline void
xfs_trans_log_rtbitmap(
struct xfs_rtalloc_args *args,
unsigned int from,
unsigned int next)
{
struct xfs_buf *bp = args->rbmbp;
size_t first, last;
first = (void *)xfs_rbmblock_wordptr(args, from) - bp->b_addr;
last = ((void *)xfs_rbmblock_wordptr(args, next) - 1) - bp->b_addr;
xfs_trans_log_buf(args->tp, bp, first, last);
}
int
xfs_rtmodify_range(
struct xfs_rtalloc_args *args,
xfs_rtxnum_t start,
xfs_rtxlen_t len,
int val)
{
struct xfs_mount *mp = args->mp;
int bit;
xfs_fileoff_t block;
int error;
int i;
int lastbit;
xfs_rtword_t mask;
xfs_rtword_t incore;
unsigned int firstword;
unsigned int word;
block = xfs_rtx_to_rbmblock(mp, start);
error = xfs_rtbitmap_read_buf(args, block);
if (error)
return error;
firstword = word = xfs_rtx_to_rbmword(mp, start);
bit = (int)(start & (XFS_NBWORD - 1));
val = -val;
if (bit) {
lastbit = min(bit + len, XFS_NBWORD);
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
incore = xfs_rtbitmap_getword(args, word);
if (val)
incore |= mask;
else
incore &= ~mask;
xfs_rtbitmap_setword(args, word, incore);
i = lastbit - bit;
if (++word == mp->m_blockwsize && i < len) {
xfs_trans_log_rtbitmap(args, firstword, word);
error = xfs_rtbitmap_read_buf(args, ++block);
if (error)
return error;
firstword = word = 0;
}
} else {
i = 0;
}
while (len - i >= XFS_NBWORD) {
xfs_rtbitmap_setword(args, word, val);
i += XFS_NBWORD;
if (++word == mp->m_blockwsize && i < len) {
xfs_trans_log_rtbitmap(args, firstword, word);
error = xfs_rtbitmap_read_buf(args, ++block);
if (error)
return error;
firstword = word = 0;
}
}
if ((lastbit = len - i)) {
mask = ((xfs_rtword_t)1 << lastbit) - 1;
incore = xfs_rtbitmap_getword(args, word);
if (val)
incore |= mask;
else
incore &= ~mask;
xfs_rtbitmap_setword(args, word, incore);
word++;
}
if (word > firstword)
xfs_trans_log_rtbitmap(args, firstword, word);
return 0;
}
int
xfs_rtfree_range(
struct xfs_rtalloc_args *args,
xfs_rtxnum_t start,
xfs_rtxlen_t len)
{
struct xfs_mount *mp = args->mp;
xfs_rtxnum_t end;
int error;
xfs_rtxnum_t postblock;
xfs_rtxnum_t preblock;
end = start + len - 1;
error = xfs_rtmodify_range(args, start, len, 1);
if (error) {
return error;
}
error = xfs_rtfind_back(args, start, &preblock);
if (error) {
return error;
}
error = xfs_rtfind_forw(args, end, args->rtg->rtg_extents - 1,
&postblock);
if (error)
return error;
if (preblock < start) {
error = xfs_rtmodify_summary(args,
xfs_highbit64(start - preblock),
xfs_rtx_to_rbmblock(mp, preblock), -1);
if (error) {
return error;
}
}
if (postblock > end) {
error = xfs_rtmodify_summary(args,
xfs_highbit64(postblock - end),
xfs_rtx_to_rbmblock(mp, end + 1), -1);
if (error) {
return error;
}
}
return xfs_rtmodify_summary(args,
xfs_highbit64(postblock + 1 - preblock),
xfs_rtx_to_rbmblock(mp, preblock), 1);
}
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)
{
struct xfs_mount *mp = args->mp;
int bit;
xfs_fileoff_t block;
int error;
xfs_rtxnum_t i;
xfs_rtxnum_t lastbit;
xfs_rtword_t mask;
xfs_rtword_t wdiff;
xfs_rtword_t incore;
unsigned int word;
block = xfs_rtx_to_rbmblock(mp, start);
error = xfs_rtbitmap_read_buf(args, block);
if (error)
return error;
word = xfs_rtx_to_rbmword(mp, start);
bit = (int)(start & (XFS_NBWORD - 1));
val = -val;
if (bit) {
lastbit = min(bit + len, XFS_NBWORD);
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
incore = xfs_rtbitmap_getword(args, word);
if ((wdiff = (incore ^ val) & mask)) {
i = xfs_lowbit32(wdiff) - bit;
*new = start + i;
*stat = 0;
return 0;
}
i = lastbit - bit;
if (++word == mp->m_blockwsize && i < len) {
error = xfs_rtbitmap_read_buf(args, ++block);
if (error)
return error;
word = 0;
}
} else {
i = 0;
}
while (len - i >= XFS_NBWORD) {
incore = xfs_rtbitmap_getword(args, word);
if ((wdiff = incore ^ val)) {
i += xfs_lowbit32(wdiff);
*new = start + i;
*stat = 0;
return 0;
}
i += XFS_NBWORD;
if (++word == mp->m_blockwsize && i < len) {
error = xfs_rtbitmap_read_buf(args, ++block);
if (error)
return error;
word = 0;
}
}
if ((lastbit = len - i)) {
mask = ((xfs_rtword_t)1 << lastbit) - 1;
incore = xfs_rtbitmap_getword(args, word);
if ((wdiff = (incore ^ val) & mask)) {
i += xfs_lowbit32(wdiff);
*new = start + i;
*stat = 0;
return 0;
} else
i = len;
}
*new = start + i;
*stat = 1;
return 0;
}
#ifdef DEBUG
STATIC int
xfs_rtcheck_alloc_range(
struct xfs_rtalloc_args *args,
xfs_rtxnum_t start,
xfs_rtxlen_t len)
{
xfs_rtxnum_t new;
int stat;
int error;
error = xfs_rtcheck_range(args, start, len, 0, &new, &stat);
if (error)
return error;
ASSERT(stat);
return 0;
}
#else
#define xfs_rtcheck_alloc_range(a,b,l) (0)
#endif
int
xfs_rtfree_extent(
struct xfs_trans *tp,
struct xfs_rtgroup *rtg,
xfs_rtxnum_t start,
xfs_rtxlen_t len)
{
struct xfs_mount *mp = tp->t_mountp;
struct xfs_inode *rbmip = rtg_bitmap(rtg);
struct xfs_rtalloc_args args = {
.mp = mp,
.tp = tp,
.rtg = rtg,
};
int error;
struct timespec64 atime;
ASSERT(rbmip->i_itemp != NULL);
xfs_assert_ilocked(rbmip, XFS_ILOCK_EXCL);
if (XFS_TEST_ERROR(mp, XFS_ERRTAG_FREE_EXTENT))
return -EIO;
error = xfs_rtcheck_alloc_range(&args, start, len);
if (error)
return error;
error = xfs_rtfree_range(&args, start, len);
if (error)
goto out;
xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS, (long)len);
if (!xfs_has_rtgroups(mp) &&
tp->t_frextents_delta + mp->m_sb.sb_frextents ==
mp->m_sb.sb_rextents) {
if (!(rbmip->i_diflags & XFS_DIFLAG_NEWRTBM))
rbmip->i_diflags |= XFS_DIFLAG_NEWRTBM;
atime = inode_get_atime(VFS_I(rbmip));
atime.tv_sec = 0;
inode_set_atime_to_ts(VFS_I(rbmip), atime);
xfs_trans_log_inode(tp, rbmip, XFS_ILOG_CORE);
}
error = 0;
out:
xfs_rtbuf_cache_relse(&args);
return error;
}
int
xfs_rtfree_blocks(
struct xfs_trans *tp,
struct xfs_rtgroup *rtg,
xfs_fsblock_t rtbno,
xfs_filblks_t rtlen)
{
struct xfs_mount *mp = tp->t_mountp;
xfs_extlen_t mod;
int error;
ASSERT(!xfs_has_zoned(mp));
ASSERT(rtlen <= XFS_MAX_BMBT_EXTLEN);
mod = xfs_blen_to_rtxoff(mp, rtlen);
if (mod) {
ASSERT(mod == 0);
return -EIO;
}
mod = xfs_rtb_to_rtxoff(mp, rtbno);
if (mod) {
ASSERT(mod == 0);
return -EIO;
}
error = xfs_rtfree_extent(tp, rtg, xfs_rtb_to_rtx(mp, rtbno),
xfs_extlen_to_rtxlen(mp, rtlen));
if (error)
return error;
if (xfs_has_rtgroups(mp))
xfs_extent_busy_insert(tp, rtg_group(rtg),
xfs_rtb_to_rgbno(mp, rtbno), rtlen, 0);
return 0;
}
int
xfs_rtalloc_query_range(
struct xfs_rtgroup *rtg,
struct xfs_trans *tp,
xfs_rtxnum_t start,
xfs_rtxnum_t end,
xfs_rtalloc_query_range_fn fn,
void *priv)
{
struct xfs_mount *mp = rtg_mount(rtg);
struct xfs_rtalloc_args args = {
.rtg = rtg,
.mp = mp,
.tp = tp,
};
int error = 0;
if (start > end)
return -EINVAL;
if (start == end || start >= rtg->rtg_extents)
return 0;
end = min(end, rtg->rtg_extents - 1);
if (xfs_has_zoned(mp))
return -EINVAL;
while (start <= end) {
struct xfs_rtalloc_rec rec;
int is_free;
xfs_rtxnum_t rtend;
error = xfs_rtcheck_range(&args, start, 1, 1, &rtend,
&is_free);
if (error)
break;
error = xfs_rtfind_forw(&args, start, end, &rtend);
if (error)
break;
if (is_free) {
rec.ar_startext = start;
rec.ar_extcount = rtend - start + 1;
error = fn(rtg, tp, &rec, priv);
if (error)
break;
}
start = rtend + 1;
}
xfs_rtbuf_cache_relse(&args);
return error;
}
int
xfs_rtalloc_query_all(
struct xfs_rtgroup *rtg,
struct xfs_trans *tp,
xfs_rtalloc_query_range_fn fn,
void *priv)
{
return xfs_rtalloc_query_range(rtg, tp, 0, rtg->rtg_extents - 1, fn,
priv);
}
int
xfs_rtalloc_extent_is_free(
struct xfs_rtgroup *rtg,
struct xfs_trans *tp,
xfs_rtxnum_t start,
xfs_rtxlen_t len,
bool *is_free)
{
struct xfs_rtalloc_args args = {
.mp = rtg_mount(rtg),
.rtg = rtg,
.tp = tp,
};
xfs_rtxnum_t end;
int matches;
int error;
error = xfs_rtcheck_range(&args, start, len, 1, &end, &matches);
xfs_rtbuf_cache_relse(&args);
if (error)
return error;
*is_free = matches;
return 0;
}
xfs_rtxnum_t
xfs_rtbitmap_rtx_per_rbmblock(
struct xfs_mount *mp)
{
unsigned int rbmblock_bytes = mp->m_sb.sb_blocksize;
if (xfs_has_rtgroups(mp))
rbmblock_bytes -= sizeof(struct xfs_rtbuf_blkinfo);
return rbmblock_bytes * NBBY;
}
xfs_filblks_t
xfs_rtbitmap_blockcount_len(
struct xfs_mount *mp,
xfs_rtbxlen_t rtextents)
{
if (xfs_has_zoned(mp))
return 0;
return howmany_64(rtextents, xfs_rtbitmap_rtx_per_rbmblock(mp));
}
static inline xfs_rtbxlen_t
xfs_rtbitmap_bitcount(
struct xfs_mount *mp)
{
if (!mp->m_sb.sb_rextents)
return 0;
if (xfs_has_rtgroups(mp))
return mp->m_sb.sb_rgextents;
return mp->m_sb.sb_rextents;
}
xfs_filblks_t
xfs_rtbitmap_blockcount(
struct xfs_mount *mp)
{
return xfs_rtbitmap_blockcount_len(mp, xfs_rtbitmap_bitcount(mp));
}
xfs_filblks_t
xfs_rtsummary_blockcount(
struct xfs_mount *mp,
unsigned int *rsumlevels)
{
xfs_rtbxlen_t rextents = xfs_rtbitmap_bitcount(mp);
unsigned long long rsumwords;
if (xfs_has_zoned(mp)) {
*rsumlevels = 0;
return 0;
}
*rsumlevels = xfs_compute_rextslog(rextents) + 1;
rsumwords = xfs_rtbitmap_blockcount_len(mp, rextents) * (*rsumlevels);
return howmany_64(rsumwords, mp->m_blockwsize);
}
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;
}
static int
xfs_rtfile_initialize_block(
struct xfs_rtgroup *rtg,
enum xfs_rtg_inodes type,
xfs_fsblock_t fsbno,
void *data)
{
struct xfs_mount *mp = rtg_mount(rtg);
struct xfs_inode *ip = rtg->rtg_inodes[type];
struct xfs_trans *tp;
struct xfs_buf *bp;
void *bufdata;
const size_t copylen = mp->m_blockwsize << XFS_WORDLOG;
enum xfs_blft buf_type;
int error;
if (type == XFS_RTGI_BITMAP)
buf_type = XFS_BLFT_RTBITMAP_BUF;
else if (type == XFS_RTGI_SUMMARY)
buf_type = XFS_BLFT_RTSUMMARY_BUF;
else
return -EINVAL;
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;
}
bufdata = bp->b_addr;
xfs_trans_buf_set_type(tp, bp, buf_type);
bp->b_ops = xfs_rtblock_ops(mp, type);
if (xfs_has_rtgroups(mp)) {
struct xfs_rtbuf_blkinfo *hdr = bp->b_addr;
if (type == XFS_RTGI_BITMAP)
hdr->rt_magic = cpu_to_be32(XFS_RTBITMAP_MAGIC);
else
hdr->rt_magic = cpu_to_be32(XFS_RTSUMMARY_MAGIC);
hdr->rt_owner = cpu_to_be64(ip->i_ino);
hdr->rt_blkno = cpu_to_be64(XFS_FSB_TO_DADDR(mp, fsbno));
hdr->rt_lsn = 0;
uuid_copy(&hdr->rt_uuid, &mp->m_sb.sb_meta_uuid);
bufdata += sizeof(*hdr);
}
if (data)
memcpy(bufdata, data, copylen);
else
memset(bufdata, 0, copylen);
xfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
return xfs_trans_commit(tp);
}
int
xfs_rtfile_initialize_blocks(
struct xfs_rtgroup *rtg,
enum xfs_rtg_inodes type,
xfs_fileoff_t offset_fsb,
xfs_fileoff_t end_fsb,
void *data)
{
struct xfs_mount *mp = rtg_mount(rtg);
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(rtg->rtg_inodes[type],
offset_fsb, end_fsb - offset_fsb, &map);
if (error)
return error;
for (i = 0; i < map.br_blockcount; i++) {
error = xfs_rtfile_initialize_block(rtg, type,
map.br_startblock + i, data);
if (error)
return error;
if (data)
data += copylen;
}
offset_fsb = map.br_startoff + map.br_blockcount;
}
return 0;
}
int
xfs_rtbitmap_create(
struct xfs_rtgroup *rtg,
struct xfs_inode *ip,
struct xfs_trans *tp,
bool init)
{
struct xfs_mount *mp = rtg_mount(rtg);
ip->i_disk_size = mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize;
if (init && !xfs_has_rtgroups(mp)) {
ip->i_diflags |= XFS_DIFLAG_NEWRTBM;
inode_set_atime(VFS_I(ip), 0, 0);
}
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
return 0;
}
int
xfs_rtsummary_create(
struct xfs_rtgroup *rtg,
struct xfs_inode *ip,
struct xfs_trans *tp,
bool init)
{
struct xfs_mount *mp = rtg_mount(rtg);
ip->i_disk_size = mp->m_rsumblocks * mp->m_sb.sb_blocksize;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
return 0;
}