#include "xfs.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_trans.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
static void
xfs_rtbuf_verify_read(
struct xfs_buf *bp)
{
return;
}
static void
xfs_rtbuf_verify_write(
struct xfs_buf *bp)
{
return;
}
const struct xfs_buf_ops xfs_rtbuf_ops = {
.name = "rtbuf",
.verify_read = xfs_rtbuf_verify_read,
.verify_write = xfs_rtbuf_verify_write,
};
int
xfs_rtbuf_get(
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_rtblock_t block,
int issum,
struct xfs_buf **bpp)
{
struct xfs_buf *bp;
xfs_inode_t *ip;
xfs_bmbt_irec_t map;
int nmap = 1;
int error;
ip = issum ? mp->m_rsumip : mp->m_rbmip;
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)))
return -EFSCORRUPTED;
ASSERT(map.br_startblock != NULLFSBLOCK);
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
XFS_FSB_TO_DADDR(mp, map.br_startblock),
mp->m_bsize, 0, &bp, &xfs_rtbuf_ops);
if (error)
return error;
xfs_trans_buf_set_type(tp, bp, issum ? XFS_BLFT_RTSUMMARY_BUF
: XFS_BLFT_RTBITMAP_BUF);
*bpp = bp;
return 0;
}
int
xfs_rtfind_back(
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_rtblock_t start,
xfs_rtblock_t limit,
xfs_rtblock_t *rtblock)
{
xfs_rtword_t *b;
int bit;
xfs_rtblock_t block;
struct xfs_buf *bp;
xfs_rtword_t *bufp;
int error;
xfs_rtblock_t firstbit;
xfs_rtblock_t i;
xfs_rtblock_t len;
xfs_rtword_t mask;
xfs_rtword_t want;
xfs_rtword_t wdiff;
int word;
block = XFS_BITTOBLOCK(mp, start);
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
if (error) {
return error;
}
bufp = bp->b_addr;
word = XFS_BITTOWORD(mp, start);
b = &bufp[word];
bit = (int)(start & (XFS_NBWORD - 1));
len = start - limit + 1;
want = (*b & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
if (bit < XFS_NBWORD - 1) {
firstbit = XFS_RTMAX((xfs_srtblock_t)(bit - len + 1), 0);
mask = (((xfs_rtword_t)1 << (bit - firstbit + 1)) - 1) <<
firstbit;
if ((wdiff = (*b ^ want) & mask)) {
xfs_trans_brelse(tp, bp);
i = bit - XFS_RTHIBIT(wdiff);
*rtblock = start - i + 1;
return 0;
}
i = bit - firstbit + 1;
if (--word == -1 && i < len) {
xfs_trans_brelse(tp, bp);
error = xfs_rtbuf_get(mp, tp, --block, 0, &bp);
if (error) {
return error;
}
bufp = bp->b_addr;
word = XFS_BLOCKWMASK(mp);
b = &bufp[word];
} else {
b--;
}
} else {
i = 0;
}
while (len - i >= XFS_NBWORD) {
if ((wdiff = *b ^ want)) {
xfs_trans_brelse(tp, bp);
i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
*rtblock = start - i + 1;
return 0;
}
i += XFS_NBWORD;
if (--word == -1 && i < len) {
xfs_trans_brelse(tp, bp);
error = xfs_rtbuf_get(mp, tp, --block, 0, &bp);
if (error) {
return error;
}
bufp = bp->b_addr;
word = XFS_BLOCKWMASK(mp);
b = &bufp[word];
} else {
b--;
}
}
if (len - i) {
firstbit = XFS_NBWORD - (len - i);
mask = (((xfs_rtword_t)1 << (len - i)) - 1) << firstbit;
if ((wdiff = (*b ^ want) & mask)) {
xfs_trans_brelse(tp, bp);
i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
*rtblock = start - i + 1;
return 0;
} else
i = len;
}
xfs_trans_brelse(tp, bp);
*rtblock = start - i + 1;
return 0;
}
int
xfs_rtfind_forw(
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_rtblock_t start,
xfs_rtblock_t limit,
xfs_rtblock_t *rtblock)
{
xfs_rtword_t *b;
int bit;
xfs_rtblock_t block;
struct xfs_buf *bp;
xfs_rtword_t *bufp;
int error;
xfs_rtblock_t i;
xfs_rtblock_t lastbit;
xfs_rtblock_t len;
xfs_rtword_t mask;
xfs_rtword_t want;
xfs_rtword_t wdiff;
int word;
block = XFS_BITTOBLOCK(mp, start);
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
if (error) {
return error;
}
bufp = bp->b_addr;
word = XFS_BITTOWORD(mp, start);
b = &bufp[word];
bit = (int)(start & (XFS_NBWORD - 1));
len = limit - start + 1;
want = (*b & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
if (bit) {
lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
if ((wdiff = (*b ^ want) & mask)) {
xfs_trans_brelse(tp, bp);
i = XFS_RTLOBIT(wdiff) - bit;
*rtblock = start + i - 1;
return 0;
}
i = lastbit - bit;
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
xfs_trans_brelse(tp, bp);
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
if (error) {
return error;
}
b = bufp = bp->b_addr;
word = 0;
} else {
b++;
}
} else {
i = 0;
}
while (len - i >= XFS_NBWORD) {
if ((wdiff = *b ^ want)) {
xfs_trans_brelse(tp, bp);
i += XFS_RTLOBIT(wdiff);
*rtblock = start + i - 1;
return 0;
}
i += XFS_NBWORD;
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
xfs_trans_brelse(tp, bp);
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
if (error) {
return error;
}
b = bufp = bp->b_addr;
word = 0;
} else {
b++;
}
}
if ((lastbit = len - i)) {
mask = ((xfs_rtword_t)1 << lastbit) - 1;
if ((wdiff = (*b ^ want) & mask)) {
xfs_trans_brelse(tp, bp);
i += XFS_RTLOBIT(wdiff);
*rtblock = start + i - 1;
return 0;
} else
i = len;
}
xfs_trans_brelse(tp, bp);
*rtblock = start + i - 1;
return 0;
}
int
xfs_rtmodify_summary_int(
xfs_mount_t *mp,
xfs_trans_t *tp,
int log,
xfs_rtblock_t bbno,
int delta,
struct xfs_buf **rbpp,
xfs_fsblock_t *rsb,
xfs_suminfo_t *sum)
{
struct xfs_buf *bp;
int error;
xfs_fsblock_t sb;
int so;
xfs_suminfo_t *sp;
so = XFS_SUMOFFS(mp, log, bbno);
sb = XFS_SUMOFFSTOBLOCK(mp, so);
if (*rbpp && *rsb == sb)
bp = *rbpp;
else {
if (*rbpp)
xfs_trans_brelse(tp, *rbpp);
error = xfs_rtbuf_get(mp, tp, sb, 1, &bp);
if (error) {
return error;
}
*rbpp = bp;
*rsb = sb;
}
sp = XFS_SUMPTR(mp, bp, so);
if (delta) {
uint first = (uint)((char *)sp - (char *)bp->b_addr);
*sp += delta;
if (mp->m_rsum_cache) {
if (*sp == 0 && log == mp->m_rsum_cache[bbno])
mp->m_rsum_cache[bbno]++;
if (*sp != 0 && log < mp->m_rsum_cache[bbno])
mp->m_rsum_cache[bbno] = log;
}
xfs_trans_log_buf(tp, bp, first, first + sizeof(*sp) - 1);
}
if (sum)
*sum = *sp;
return 0;
}
int
xfs_rtmodify_summary(
xfs_mount_t *mp,
xfs_trans_t *tp,
int log,
xfs_rtblock_t bbno,
int delta,
struct xfs_buf **rbpp,
xfs_fsblock_t *rsb)
{
return xfs_rtmodify_summary_int(mp, tp, log, bbno,
delta, rbpp, rsb, NULL);
}
int
xfs_rtmodify_range(
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_rtblock_t start,
xfs_extlen_t len,
int val)
{
xfs_rtword_t *b;
int bit;
xfs_rtblock_t block;
struct xfs_buf *bp;
xfs_rtword_t *bufp;
int error;
xfs_rtword_t *first;
int i;
int lastbit;
xfs_rtword_t mask;
int word;
block = XFS_BITTOBLOCK(mp, start);
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
if (error) {
return error;
}
bufp = bp->b_addr;
word = XFS_BITTOWORD(mp, start);
first = b = &bufp[word];
bit = (int)(start & (XFS_NBWORD - 1));
val = -val;
if (bit) {
lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
if (val)
*b |= mask;
else
*b &= ~mask;
i = lastbit - bit;
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
xfs_trans_log_buf(tp, bp,
(uint)((char *)first - (char *)bufp),
(uint)((char *)b - (char *)bufp));
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
if (error) {
return error;
}
first = b = bufp = bp->b_addr;
word = 0;
} else {
b++;
}
} else {
i = 0;
}
while (len - i >= XFS_NBWORD) {
*b = val;
i += XFS_NBWORD;
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
xfs_trans_log_buf(tp, bp,
(uint)((char *)first - (char *)bufp),
(uint)((char *)b - (char *)bufp));
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
if (error) {
return error;
}
first = b = bufp = bp->b_addr;
word = 0;
} else {
b++;
}
}
if ((lastbit = len - i)) {
mask = ((xfs_rtword_t)1 << lastbit) - 1;
if (val)
*b |= mask;
else
*b &= ~mask;
b++;
}
if (b > first)
xfs_trans_log_buf(tp, bp, (uint)((char *)first - (char *)bufp),
(uint)((char *)b - (char *)bufp - 1));
return 0;
}
int
xfs_rtfree_range(
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_rtblock_t start,
xfs_extlen_t len,
struct xfs_buf **rbpp,
xfs_fsblock_t *rsb)
{
xfs_rtblock_t end;
int error;
xfs_rtblock_t postblock;
xfs_rtblock_t preblock;
end = start + len - 1;
error = xfs_rtmodify_range(mp, tp, start, len, 1);
if (error) {
return error;
}
error = xfs_rtfind_back(mp, tp, start, 0, &preblock);
if (error) {
return error;
}
error = xfs_rtfind_forw(mp, tp, end, mp->m_sb.sb_rextents - 1,
&postblock);
if (error)
return error;
if (preblock < start) {
error = xfs_rtmodify_summary(mp, tp,
XFS_RTBLOCKLOG(start - preblock),
XFS_BITTOBLOCK(mp, preblock), -1, rbpp, rsb);
if (error) {
return error;
}
}
if (postblock > end) {
error = xfs_rtmodify_summary(mp, tp,
XFS_RTBLOCKLOG(postblock - end),
XFS_BITTOBLOCK(mp, end + 1), -1, rbpp, rsb);
if (error) {
return error;
}
}
error = xfs_rtmodify_summary(mp, tp,
XFS_RTBLOCKLOG(postblock + 1 - preblock),
XFS_BITTOBLOCK(mp, preblock), 1, rbpp, rsb);
return error;
}
int
xfs_rtcheck_range(
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_rtblock_t start,
xfs_extlen_t len,
int val,
xfs_rtblock_t *new,
int *stat)
{
xfs_rtword_t *b;
int bit;
xfs_rtblock_t block;
struct xfs_buf *bp;
xfs_rtword_t *bufp;
int error;
xfs_rtblock_t i;
xfs_rtblock_t lastbit;
xfs_rtword_t mask;
xfs_rtword_t wdiff;
int word;
block = XFS_BITTOBLOCK(mp, start);
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
if (error) {
return error;
}
bufp = bp->b_addr;
word = XFS_BITTOWORD(mp, start);
b = &bufp[word];
bit = (int)(start & (XFS_NBWORD - 1));
val = -val;
if (bit) {
lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
if ((wdiff = (*b ^ val) & mask)) {
xfs_trans_brelse(tp, bp);
i = XFS_RTLOBIT(wdiff) - bit;
*new = start + i;
*stat = 0;
return 0;
}
i = lastbit - bit;
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
xfs_trans_brelse(tp, bp);
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
if (error) {
return error;
}
b = bufp = bp->b_addr;
word = 0;
} else {
b++;
}
} else {
i = 0;
}
while (len - i >= XFS_NBWORD) {
if ((wdiff = *b ^ val)) {
xfs_trans_brelse(tp, bp);
i += XFS_RTLOBIT(wdiff);
*new = start + i;
*stat = 0;
return 0;
}
i += XFS_NBWORD;
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
xfs_trans_brelse(tp, bp);
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
if (error) {
return error;
}
b = bufp = bp->b_addr;
word = 0;
} else {
b++;
}
}
if ((lastbit = len - i)) {
mask = ((xfs_rtword_t)1 << lastbit) - 1;
if ((wdiff = (*b ^ val) & mask)) {
xfs_trans_brelse(tp, bp);
i += XFS_RTLOBIT(wdiff);
*new = start + i;
*stat = 0;
return 0;
} else
i = len;
}
xfs_trans_brelse(tp, bp);
*new = start + i;
*stat = 1;
return 0;
}
#ifdef DEBUG
STATIC int
xfs_rtcheck_alloc_range(
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_rtblock_t bno,
xfs_extlen_t len)
{
xfs_rtblock_t new;
int stat;
int error;
error = xfs_rtcheck_range(mp, tp, bno, len, 0, &new, &stat);
if (error)
return error;
ASSERT(stat);
return 0;
}
#else
#define xfs_rtcheck_alloc_range(m,t,b,l) (0)
#endif
int
xfs_rtfree_extent(
xfs_trans_t *tp,
xfs_rtblock_t bno,
xfs_extlen_t len)
{
int error;
xfs_mount_t *mp;
xfs_fsblock_t sb;
struct xfs_buf *sumbp = NULL;
mp = tp->t_mountp;
ASSERT(mp->m_rbmip->i_itemp != NULL);
ASSERT(xfs_isilocked(mp->m_rbmip, XFS_ILOCK_EXCL));
error = xfs_rtcheck_alloc_range(mp, tp, bno, len);
if (error)
return error;
error = xfs_rtfree_range(mp, tp, bno, len, &sumbp, &sb);
if (error) {
return error;
}
xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS, (long)len);
if (tp->t_frextents_delta + mp->m_sb.sb_frextents ==
mp->m_sb.sb_rextents) {
if (!(mp->m_rbmip->i_diflags & XFS_DIFLAG_NEWRTBM))
mp->m_rbmip->i_diflags |= XFS_DIFLAG_NEWRTBM;
*(uint64_t *)&VFS_I(mp->m_rbmip)->i_atime = 0;
xfs_trans_log_inode(tp, mp->m_rbmip, XFS_ILOG_CORE);
}
return 0;
}
int
xfs_rtalloc_query_range(
struct xfs_mount *mp,
struct xfs_trans *tp,
const struct xfs_rtalloc_rec *low_rec,
const struct xfs_rtalloc_rec *high_rec,
xfs_rtalloc_query_range_fn fn,
void *priv)
{
struct xfs_rtalloc_rec rec;
xfs_rtblock_t rtstart;
xfs_rtblock_t rtend;
xfs_rtblock_t high_key;
int is_free;
int error = 0;
if (low_rec->ar_startext > high_rec->ar_startext)
return -EINVAL;
if (low_rec->ar_startext >= mp->m_sb.sb_rextents ||
low_rec->ar_startext == high_rec->ar_startext)
return 0;
high_key = min(high_rec->ar_startext, mp->m_sb.sb_rextents - 1);
rtstart = low_rec->ar_startext;
while (rtstart <= high_key) {
error = xfs_rtcheck_range(mp, tp, rtstart, 1, 1, &rtend,
&is_free);
if (error)
break;
error = xfs_rtfind_forw(mp, tp, rtstart, high_key, &rtend);
if (error)
break;
if (is_free) {
rec.ar_startext = rtstart;
rec.ar_extcount = rtend - rtstart + 1;
error = fn(mp, tp, &rec, priv);
if (error)
break;
}
rtstart = rtend + 1;
}
return error;
}
int
xfs_rtalloc_query_all(
struct xfs_mount *mp,
struct xfs_trans *tp,
xfs_rtalloc_query_range_fn fn,
void *priv)
{
struct xfs_rtalloc_rec keys[2];
keys[0].ar_startext = 0;
keys[1].ar_startext = mp->m_sb.sb_rextents - 1;
keys[0].ar_extcount = keys[1].ar_extcount = 0;
return xfs_rtalloc_query_range(mp, tp, &keys[0], &keys[1], fn, priv);
}
int
xfs_rtalloc_extent_is_free(
struct xfs_mount *mp,
struct xfs_trans *tp,
xfs_rtblock_t start,
xfs_extlen_t len,
bool *is_free)
{
xfs_rtblock_t end;
int matches;
int error;
error = xfs_rtcheck_range(mp, tp, start, len, 1, &end, &matches);
if (error)
return error;
*is_free = matches;
return 0;
}