root/fs/xfs/libxfs/xfs_rtbitmap.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
 */
#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"

/*
 * Realtime allocator bitmap functions shared with userspace.
 */

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,
};

/* Release cached rt bitmap and summary buffers. */
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;
        }
}

/*
 * Get a buffer for the bitmap or summary file block specified.
 * The buffer is returned read and locked.
 */
static int
xfs_rtbuf_get(
        struct xfs_rtalloc_args *args,
        xfs_fileoff_t           block,  /* block number in bitmap or summary */
        enum xfs_rtg_inodes     type)
{
        struct xfs_inode        *ip = args->rtg->rtg_inodes[type];
        struct xfs_mount        *mp = args->mp;
        struct xfs_buf          **cbpp; /* cached block buffer */
        xfs_fileoff_t           *coffp; /* cached block number */
        struct xfs_buf          *bp;    /* block buffer, result */
        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 we have a cached buffer, and the block number matches, use that.
         */
        if (*cbpp && *coffp == block)
                return 0;

        /*
         * Otherwise we have to have to get the buffer.  If there was an old
         * one, get rid of it first.
         */
        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);
}

/*
 * 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            *rtx)   /* out: start rtext found */
{
        struct xfs_mount        *mp = args->mp;
        int                     bit;    /* bit number in the word */
        xfs_fileoff_t           block;  /* bitmap block number */
        int                     error;  /* error value */
        xfs_rtxnum_t            firstbit; /* first useful bit in the word */
        xfs_rtxnum_t            i;      /* current bit number rel. to start */
        xfs_rtxnum_t            len;    /* length of inspected area */
        xfs_rtword_t            mask;   /* mask of relevant bits for value */
        xfs_rtword_t            want;   /* mask for "good" values */
        xfs_rtword_t            wdiff;  /* difference from wanted value */
        xfs_rtword_t            incore;
        unsigned int            word;   /* word number in the buffer */

        /*
         * Compute and read in starting bitmap block for starting block.
         */
        block = xfs_rtx_to_rbmblock(mp, start);
        error = xfs_rtbitmap_read_buf(args, block);
        if (error)
                return error;

        /*
         * Get the first word's index & point to it.
         */
        word = xfs_rtx_to_rbmword(mp, start);
        bit = (int)(start & (XFS_NBWORD - 1));
        len = start + 1;
        /*
         * Compute match value, based on the bit at start: if 1 (free)
         * then all-ones, else all-zeroes.
         */
        incore = xfs_rtbitmap_getword(args, word);
        want = (incore & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
        /*
         * If the starting position is not word-aligned, deal with the
         * partial word.
         */
        if (bit < XFS_NBWORD - 1) {
                /*
                 * Calculate first (leftmost) bit number to look at,
                 * and mask for all the relevant bits in this word.
                 */
                firstbit = max_t(xfs_srtblock_t, bit - len + 1, 0);
                mask = (((xfs_rtword_t)1 << (bit - firstbit + 1)) - 1) <<
                        firstbit;
                /*
                 * Calculate the difference between the value there
                 * and what we're looking for.
                 */
                if ((wdiff = (incore ^ want) & mask)) {
                        /*
                         * Different.  Mark where we are and return.
                         */
                        i = bit - xfs_highbit32(wdiff);
                        *rtx = start - i + 1;
                        return 0;
                }
                i = bit - firstbit + 1;
                /*
                 * Go on to previous block if that's where the previous word is
                 * and we need the previous word.
                 */
                if (--word == -1 && i < len) {
                        /*
                         * If done with this block, get the previous one.
                         */
                        error = xfs_rtbitmap_read_buf(args, --block);
                        if (error)
                                return error;

                        word = mp->m_blockwsize - 1;
                }
        } else {
                /*
                 * Starting on a word boundary, no partial word.
                 */
                i = 0;
        }
        /*
         * Loop over whole words in buffers.  When we use up one buffer
         * we move on to the previous one.
         */
        while (len - i >= XFS_NBWORD) {
                /*
                 * Compute difference between actual and desired value.
                 */
                incore = xfs_rtbitmap_getword(args, word);
                if ((wdiff = incore ^ want)) {
                        /*
                         * Different, mark where we are and return.
                         */
                        i += XFS_NBWORD - 1 - xfs_highbit32(wdiff);
                        *rtx = start - i + 1;
                        return 0;
                }
                i += XFS_NBWORD;
                /*
                 * Go on to previous block if that's where the previous word is
                 * and we need the previous word.
                 */
                if (--word == -1 && i < len) {
                        /*
                         * If done with this block, get the previous one.
                         */
                        error = xfs_rtbitmap_read_buf(args, --block);
                        if (error)
                                return error;

                        word = mp->m_blockwsize - 1;
                }
        }
        /*
         * If not ending on a word boundary, deal with the last
         * (partial) word.
         */
        if (len - i) {
                /*
                 * Calculate first (leftmost) bit number to look at,
                 * and mask for all the relevant bits in this word.
                 */
                firstbit = XFS_NBWORD - (len - i);
                mask = (((xfs_rtword_t)1 << (len - i)) - 1) << firstbit;
                /*
                 * Compute difference between actual and desired value.
                 */
                incore = xfs_rtbitmap_getword(args, word);
                if ((wdiff = (incore ^ want) & mask)) {
                        /*
                         * Different, mark where we are and return.
                         */
                        i += XFS_NBWORD - 1 - xfs_highbit32(wdiff);
                        *rtx = start - i + 1;
                        return 0;
                } else
                        i = len;
        }
        /*
         * No match, return that we scanned the whole area.
         */
        *rtx = start - i + 1;
        return 0;
}

/*
 * Searching forward from start to limit, find the first block whose
 * allocated/free state is different from start's.
 */
int
xfs_rtfind_forw(
        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;
        int                     bit;    /* bit number in the word */
        xfs_fileoff_t           block;  /* bitmap block number */
        int                     error;
        xfs_rtxnum_t            i;      /* current bit number rel. to start */
        xfs_rtxnum_t            lastbit;/* last useful bit in the word */
        xfs_rtxnum_t            len;    /* length of inspected area */
        xfs_rtword_t            mask;   /* mask of relevant bits for value */
        xfs_rtword_t            want;   /* mask for "good" values */
        xfs_rtword_t            wdiff;  /* difference from wanted value */
        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.
         */
        block = xfs_rtx_to_rbmblock(mp, start);
        error = xfs_rtbitmap_read_buf(args, block);
        if (error)
                return error;

        /*
         * Get the first word's index & point to it.
         */
        word = xfs_rtx_to_rbmword(mp, start);
        bit = (int)(start & (XFS_NBWORD - 1));
        len = limit - start + 1;
        /*
         * Compute match value, based on the bit at start: if 1 (free)
         * then all-ones, else all-zeroes.
         */
        incore = xfs_rtbitmap_getword(args, word);
        want = (incore & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
        /*
         * If the starting position is not word-aligned, deal with the
         * partial word.
         */
        if (bit) {
                /*
                 * Calculate last (rightmost) bit number to look at,
                 * and mask for all the relevant bits in this word.
                 */
                lastbit = min(bit + len, XFS_NBWORD);
                mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
                /*
                 * Calculate the difference between the value there
                 * and what we're looking for.
                 */
                if ((wdiff = (incore ^ want) & mask)) {
                        /*
                         * Different.  Mark where we are and return.
                         */
                        i = xfs_lowbit32(wdiff) - bit;
                        *rtx = start + i - 1;
                        return 0;
                }
                i = lastbit - bit;
                /*
                 * Go on to next block if that's where the next word is
                 * and we need the next word.
                 */
                if (++word == mp->m_blockwsize && i < len) {
                        /*
                         * If done with this block, get the previous one.
                         */
                        error = xfs_rtbitmap_read_buf(args, ++block);
                        if (error)
                                return error;

                        word = 0;
                }
        } else {
                /*
                 * Starting on a word boundary, no partial word.
                 */
                i = 0;
        }
        /*
         * Loop over whole words in buffers.  When we use up one buffer
         * we move on to the next one.
         */
        while (len - i >= XFS_NBWORD) {
                /*
                 * Compute difference between actual and desired value.
                 */
                incore = xfs_rtbitmap_getword(args, word);
                if ((wdiff = incore ^ want)) {
                        /*
                         * Different, mark where we are and return.
                         */
                        i += xfs_lowbit32(wdiff);
                        *rtx = start + i - 1;
                        return 0;
                }
                i += XFS_NBWORD;
                /*
                 * Go on to next block if that's where the next word is
                 * and we need the next word.
                 */
                if (++word == mp->m_blockwsize && i < len) {
                        /*
                         * If done with this block, get the next one.
                         */
                        error = xfs_rtbitmap_read_buf(args, ++block);
                        if (error)
                                return error;

                        word = 0;
                }
        }
        /*
         * If not ending on a word boundary, deal with the last
         * (partial) word.
         */
        if ((lastbit = len - i)) {
                /*
                 * Calculate mask for all the relevant bits in this word.
                 */
                mask = ((xfs_rtword_t)1 << lastbit) - 1;
                /*
                 * Compute difference between actual and desired value.
                 */
                incore = xfs_rtbitmap_getword(args, word);
                if ((wdiff = (incore ^ want) & mask)) {
                        /*
                         * Different, mark where we are and return.
                         */
                        i += xfs_lowbit32(wdiff);
                        *rtx = start + i - 1;
                        return 0;
                } else
                        i = len;
        }
        /*
         * No match, return that we scanned the whole area.
         */
        *rtx = start + i - 1;
        return 0;
}

/* Log rtsummary counter at @infoword. */
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);
}

/*
 * Modify the summary information for a given extent size, bitmap block
 * combination.
 */
int
xfs_rtmodify_summary(
        struct xfs_rtalloc_args *args,
        int                     log,    /* log2 of extent size */
        xfs_fileoff_t           bbno,   /* bitmap block number */
        int                     delta)  /* in/out: summary block number */
{
        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;
}

/*
 * Read and return the summary information for a given extent size, bitmap block
 * combination.
 */
int
xfs_rtget_summary(
        struct xfs_rtalloc_args *args,
        int                     log,    /* log2 of extent size */
        xfs_fileoff_t           bbno,   /* bitmap block number */
        xfs_suminfo_t           *sum)   /* out: summary info for this block */
{
        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;
}

/* Log rtbitmap block from the word @from to the byte before @next. */
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);
}

/*
 * Set the given range of bitmap bits to the given value.
 * Do whatever I/O and logging is required.
 */
int
xfs_rtmodify_range(
        struct xfs_rtalloc_args *args,
        xfs_rtxnum_t            start,  /* starting rtext to modify */
        xfs_rtxlen_t            len,    /* length of extent to modify */
        int                     val)    /* 1 for free, 0 for allocated */
{
        struct xfs_mount        *mp = args->mp;
        int                     bit;    /* bit number in the word */
        xfs_fileoff_t           block;  /* bitmap block number */
        int                     error;
        int                     i;      /* current bit number rel. to start */
        int                     lastbit; /* last useful bit in word */
        xfs_rtword_t            mask;    /* mask of relevant bits for value */
        xfs_rtword_t            incore;
        unsigned int            firstword; /* first word used in the buffer */
        unsigned int            word;   /* word number in the buffer */

        /*
         * Compute starting bitmap block number.
         */
        block = xfs_rtx_to_rbmblock(mp, start);
        /*
         * Read the bitmap block, and point to its data.
         */
        error = xfs_rtbitmap_read_buf(args, block);
        if (error)
                return error;

        /*
         * Compute the starting word's address, and starting bit.
         */
        firstword = word = xfs_rtx_to_rbmword(mp, start);
        bit = (int)(start & (XFS_NBWORD - 1));
        /*
         * 0 (allocated) => all zeroes; 1 (free) => all ones.
         */
        val = -val;
        /*
         * If not starting on a word boundary, deal with the first
         * (partial) word.
         */
        if (bit) {
                /*
                 * Compute first bit not changed and mask of relevant bits.
                 */
                lastbit = min(bit + len, XFS_NBWORD);
                mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
                /*
                 * Set/clear the active bits.
                 */
                incore = xfs_rtbitmap_getword(args, word);
                if (val)
                        incore |= mask;
                else
                        incore &= ~mask;
                xfs_rtbitmap_setword(args, word, incore);
                i = lastbit - bit;
                /*
                 * Go on to the next block if that's where the next word is
                 * and we need the next word.
                 */
                if (++word == mp->m_blockwsize && i < len) {
                        /*
                         * Log the changed part of this block.
                         * Get the next one.
                         */
                        xfs_trans_log_rtbitmap(args, firstword, word);
                        error = xfs_rtbitmap_read_buf(args, ++block);
                        if (error)
                                return error;

                        firstword = word = 0;
                }
        } else {
                /*
                 * Starting on a word boundary, no partial word.
                 */
                i = 0;
        }
        /*
         * Loop over whole words in buffers.  When we use up one buffer
         * we move on to the next one.
         */
        while (len - i >= XFS_NBWORD) {
                /*
                 * Set the word value correctly.
                 */
                xfs_rtbitmap_setword(args, word, val);
                i += XFS_NBWORD;
                /*
                 * Go on to the next block if that's where the next word is
                 * and we need the next word.
                 */
                if (++word == mp->m_blockwsize && i < len) {
                        /*
                         * Log the changed part of this block.
                         * Get the next one.
                         */
                        xfs_trans_log_rtbitmap(args, firstword, word);
                        error = xfs_rtbitmap_read_buf(args, ++block);
                        if (error)
                                return error;

                        firstword = word = 0;
                }
        }
        /*
         * If not ending on a word boundary, deal with the last
         * (partial) word.
         */
        if ((lastbit = len - i)) {
                /*
                 * Compute a mask of relevant bits.
                 */
                mask = ((xfs_rtword_t)1 << lastbit) - 1;
                /*
                 * Set/clear the active bits.
                 */
                incore = xfs_rtbitmap_getword(args, word);
                if (val)
                        incore |= mask;
                else
                        incore &= ~mask;
                xfs_rtbitmap_setword(args, word, incore);
                word++;
        }
        /*
         * Log any remaining changed bytes.
         */
        if (word > firstword)
                xfs_trans_log_rtbitmap(args, firstword, word);
        return 0;
}

/*
 * Mark an extent specified by start and len freed.
 * Updates all the summary information as well as the bitmap.
 */
int
xfs_rtfree_range(
        struct xfs_rtalloc_args *args,
        xfs_rtxnum_t            start,  /* starting rtext to free */
        xfs_rtxlen_t            len)    /* in/out: summary block number */
{
        struct xfs_mount        *mp = args->mp;
        xfs_rtxnum_t            end;    /* end of the freed extent */
        int                     error;  /* error value */
        xfs_rtxnum_t            postblock; /* first rtext freed > end */
        xfs_rtxnum_t            preblock;  /* first rtext freed < start */

        end = start + len - 1;
        /*
         * Modify the bitmap to mark this extent freed.
         */
        error = xfs_rtmodify_range(args, start, len, 1);
        if (error) {
                return error;
        }
        /*
         * Assume we're freeing out of the middle of an allocated extent.
         * We need to find the beginning and end of the extent so we can
         * properly update the summary.
         */
        error = xfs_rtfind_back(args, start, &preblock);
        if (error) {
                return error;
        }
        /*
         * Find the next allocated block (end of allocated extent).
         */
        error = xfs_rtfind_forw(args, end, args->rtg->rtg_extents - 1,
                        &postblock);
        if (error)
                return error;
        /*
         * If there are blocks not being freed at the front of the
         * old extent, add summary data for them to be allocated.
         */
        if (preblock < start) {
                error = xfs_rtmodify_summary(args,
                                xfs_highbit64(start - preblock),
                                xfs_rtx_to_rbmblock(mp, preblock), -1);
                if (error) {
                        return error;
                }
        }
        /*
         * If there are blocks not being freed at the end of the
         * old extent, add summary data for them to be allocated.
         */
        if (postblock > end) {
                error = xfs_rtmodify_summary(args,
                                xfs_highbit64(postblock - end),
                                xfs_rtx_to_rbmblock(mp, end + 1), -1);
                if (error) {
                        return error;
                }
        }
        /*
         * Increment the summary information corresponding to the entire
         * (new) free extent.
         */
        return xfs_rtmodify_summary(args,
                        xfs_highbit64(postblock + 1 - preblock),
                        xfs_rtx_to_rbmblock(mp, preblock), 1);
}

/*
 * Check that the given range is either all allocated (val = 0) or
 * all free (val = 1).
 */
int
xfs_rtcheck_range(
        struct xfs_rtalloc_args *args,
        xfs_rtxnum_t            start,  /* starting rtext number of extent */
        xfs_rtxlen_t            len,    /* length of extent */
        int                     val,    /* 1 for free, 0 for allocated */
        xfs_rtxnum_t            *new,   /* out: first rtext not matching */
        int                     *stat)  /* out: 1 for matches, 0 for not */
{
        struct xfs_mount        *mp = args->mp;
        int                     bit;    /* bit number in the word */
        xfs_fileoff_t           block;  /* bitmap block number */
        int                     error;
        xfs_rtxnum_t            i;      /* current bit number rel. to start */
        xfs_rtxnum_t            lastbit; /* last useful bit in word */
        xfs_rtword_t            mask;   /* mask of relevant bits for value */
        xfs_rtword_t            wdiff;  /* difference from wanted value */
        xfs_rtword_t            incore;
        unsigned int            word;   /* word number in the buffer */

        /*
         * Compute starting bitmap block number
         */
        block = xfs_rtx_to_rbmblock(mp, start);
        /*
         * Read the bitmap block.
         */
        error = xfs_rtbitmap_read_buf(args, block);
        if (error)
                return error;

        /*
         * Compute the starting word's address, and starting bit.
         */
        word = xfs_rtx_to_rbmword(mp, start);
        bit = (int)(start & (XFS_NBWORD - 1));
        /*
         * 0 (allocated) => all zero's; 1 (free) => all one's.
         */
        val = -val;
        /*
         * If not starting on a word boundary, deal with the first
         * (partial) word.
         */
        if (bit) {
                /*
                 * Compute first bit not examined.
                 */
                lastbit = min(bit + len, XFS_NBWORD);
                /*
                 * Mask of relevant bits.
                 */
                mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
                /*
                 * Compute difference between actual and desired value.
                 */
                incore = xfs_rtbitmap_getword(args, word);
                if ((wdiff = (incore ^ val) & mask)) {
                        /*
                         * Different, compute first wrong bit and return.
                         */
                        i = xfs_lowbit32(wdiff) - bit;
                        *new = start + i;
                        *stat = 0;
                        return 0;
                }
                i = lastbit - bit;
                /*
                 * Go on to next block if that's where the next word is
                 * and we need the next word.
                 */
                if (++word == mp->m_blockwsize && i < len) {
                        /*
                         * If done with this block, get the next one.
                         */
                        error = xfs_rtbitmap_read_buf(args, ++block);
                        if (error)
                                return error;

                        word = 0;
                }
        } else {
                /*
                 * Starting on a word boundary, no partial word.
                 */
                i = 0;
        }
        /*
         * Loop over whole words in buffers.  When we use up one buffer
         * we move on to the next one.
         */
        while (len - i >= XFS_NBWORD) {
                /*
                 * Compute difference between actual and desired value.
                 */
                incore = xfs_rtbitmap_getword(args, word);
                if ((wdiff = incore ^ val)) {
                        /*
                         * Different, compute first wrong bit and return.
                         */
                        i += xfs_lowbit32(wdiff);
                        *new = start + i;
                        *stat = 0;
                        return 0;
                }
                i += XFS_NBWORD;
                /*
                 * Go on to next block if that's where the next word is
                 * and we need the next word.
                 */
                if (++word == mp->m_blockwsize && i < len) {
                        /*
                         * If done with this block, get the next one.
                         */
                        error = xfs_rtbitmap_read_buf(args, ++block);
                        if (error)
                                return error;

                        word = 0;
                }
        }
        /*
         * If not ending on a word boundary, deal with the last
         * (partial) word.
         */
        if ((lastbit = len - i)) {
                /*
                 * Mask of relevant bits.
                 */
                mask = ((xfs_rtword_t)1 << lastbit) - 1;
                /*
                 * Compute difference between actual and desired value.
                 */
                incore = xfs_rtbitmap_getword(args, word);
                if ((wdiff = (incore ^ val) & mask)) {
                        /*
                         * Different, compute first wrong bit and return.
                         */
                        i += xfs_lowbit32(wdiff);
                        *new = start + i;
                        *stat = 0;
                        return 0;
                } else
                        i = len;
        }
        /*
         * Successful, return.
         */
        *new = start + i;
        *stat = 1;
        return 0;
}

#ifdef DEBUG
/*
 * Check that the given extent (block range) is allocated already.
 */
STATIC int
xfs_rtcheck_alloc_range(
        struct xfs_rtalloc_args *args,
        xfs_rtxnum_t            start,  /* starting rtext number of extent */
        xfs_rtxlen_t            len)    /* length of extent */
{
        xfs_rtxnum_t            new;    /* dummy for xfs_rtcheck_range */
        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
/*
 * Free an extent in the realtime subvolume.  Length is expressed in
 * realtime extents, as is the block number.
 */
int
xfs_rtfree_extent(
        struct xfs_trans        *tp,    /* transaction pointer */
        struct xfs_rtgroup      *rtg,
        xfs_rtxnum_t            start,  /* starting rtext number to free */
        xfs_rtxlen_t            len)    /* length of extent freed */
{
        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;

        /*
         * Free the range of realtime blocks.
         */
        error = xfs_rtfree_range(&args, start, len);
        if (error)
                goto out;

        /*
         * Mark more blocks free in the superblock.
         */
        xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS, (long)len);

        /*
         * If we've now freed all the blocks, reset the file sequence
         * number to 0 for pre-RTG file systems.
         */
        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;
}

/*
 * Free some blocks in the realtime subvolume.  rtbno and rtlen are in units of
 * rt blocks, not rt extents; must be aligned to the rt extent size; and rtlen
 * cannot exceed XFS_MAX_BMBT_EXTLEN.
 */
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;
}

/* Find all the free records within a given range. */
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;

        /* Iterate the bitmap, looking for discrepancies. */
        while (start <= end) {
                struct xfs_rtalloc_rec  rec;
                int                     is_free;
                xfs_rtxnum_t            rtend;

                /* Is the first block free? */
                error = xfs_rtcheck_range(&args, start, 1, 1, &rtend,
                                &is_free);
                if (error)
                        break;

                /* How long does the extent go for? */
                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;
}

/* Find all the free records. */
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);
}

/* Is the given extent all free? */
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;
}

/* Compute the number of rt extents tracked by a single bitmap block. */
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;
}

/*
 * Compute the number of rtbitmap blocks needed to track the given number of rt
 * extents.
 */
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));
}

/* How many rt extents does each rtbitmap file track? */
static inline xfs_rtbxlen_t
xfs_rtbitmap_bitcount(
        struct xfs_mount        *mp)
{
        if (!mp->m_sb.sb_rextents)
                return 0;

        /* rtgroup size can be nonzero even if rextents is zero */
        if (xfs_has_rtgroups(mp))
                return mp->m_sb.sb_rgextents;

        return mp->m_sb.sb_rextents;
}

/*
 * Compute the number of rtbitmap blocks used for a given file system.
 */
xfs_filblks_t
xfs_rtbitmap_blockcount(
        struct xfs_mount        *mp)
{
        return xfs_rtbitmap_blockcount_len(mp, xfs_rtbitmap_bitcount(mp));
}

/*
 * Compute the geometry of the rtsummary file needed to track the given rt
 * space.
 */
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;
}

/* Get a buffer for the block. */
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);
}

/*
 * 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_rtgroup      *rtg,
        enum xfs_rtg_inodes     type,
        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 = 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;

                /*
                 * 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(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;
}