root/fs/xfs/libxfs/xfs_rtbitmap.h
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
 * All Rights Reserved.
 */
#ifndef __XFS_RTBITMAP_H__
#define __XFS_RTBITMAP_H__

#include "xfs_rtgroup.h"

struct xfs_rtalloc_args {
        struct xfs_rtgroup      *rtg;
        struct xfs_mount        *mp;
        struct xfs_trans        *tp;

        struct xfs_buf          *rbmbp; /* bitmap block buffer */
        struct xfs_buf          *sumbp; /* summary block buffer */

        xfs_fileoff_t           rbmoff; /* bitmap block number */
        xfs_fileoff_t           sumoff; /* summary block number */
};

static inline xfs_rtblock_t
xfs_rtx_to_rtb(
        struct xfs_rtgroup      *rtg,
        xfs_rtxnum_t            rtx)
{
        struct xfs_mount        *mp = rtg_mount(rtg);
        xfs_rtblock_t           start = xfs_group_start_fsb(rtg_group(rtg));

        if (mp->m_rtxblklog >= 0)
                return start + (rtx << mp->m_rtxblklog);
        return start + (rtx * mp->m_sb.sb_rextsize);
}

/* Convert an rgbno into an rt extent number. */
static inline xfs_rtxnum_t
xfs_rgbno_to_rtx(
        struct xfs_mount        *mp,
        xfs_rgblock_t           rgbno)
{
        if (likely(mp->m_rtxblklog >= 0))
                return rgbno >> mp->m_rtxblklog;
        return rgbno / mp->m_sb.sb_rextsize;
}

static inline uint64_t
xfs_rtbxlen_to_blen(
        struct xfs_mount        *mp,
        xfs_rtbxlen_t           rtbxlen)
{
        if (mp->m_rtxblklog >= 0)
                return rtbxlen << mp->m_rtxblklog;

        return rtbxlen * mp->m_sb.sb_rextsize;
}

static inline xfs_extlen_t
xfs_rtxlen_to_extlen(
        struct xfs_mount        *mp,
        xfs_rtxlen_t            rtxlen)
{
        if (mp->m_rtxblklog >= 0)
                return rtxlen << mp->m_rtxblklog;

        return rtxlen * mp->m_sb.sb_rextsize;
}

/* Compute the misalignment between an extent length and a realtime extent .*/
static inline unsigned int
xfs_extlen_to_rtxmod(
        struct xfs_mount        *mp,
        xfs_extlen_t            len)
{
        if (mp->m_rtxblklog >= 0)
                return len & mp->m_rtxblkmask;

        return len % mp->m_sb.sb_rextsize;
}

static inline xfs_rtxlen_t
xfs_extlen_to_rtxlen(
        struct xfs_mount        *mp,
        xfs_extlen_t            len)
{
        if (mp->m_rtxblklog >= 0)
                return len >> mp->m_rtxblklog;

        return len / mp->m_sb.sb_rextsize;
}

/* Convert an rt block count into an rt extent count. */
static inline xfs_rtbxlen_t
xfs_blen_to_rtbxlen(
        struct xfs_mount        *mp,
        uint64_t                blen)
{
        if (likely(mp->m_rtxblklog >= 0))
                return blen >> mp->m_rtxblklog;

        return div_u64(blen, mp->m_sb.sb_rextsize);
}

/* Return the offset of a file block length within an rt extent. */
static inline xfs_extlen_t
xfs_blen_to_rtxoff(
        struct xfs_mount        *mp,
        xfs_filblks_t           blen)
{
        if (likely(mp->m_rtxblklog >= 0))
                return blen & mp->m_rtxblkmask;

        return do_div(blen, mp->m_sb.sb_rextsize);
}

/* Round this block count up to the nearest rt extent size. */
static inline xfs_filblks_t
xfs_blen_roundup_rtx(
        struct xfs_mount        *mp,
        xfs_filblks_t           blen)
{
        return roundup_64(blen, mp->m_sb.sb_rextsize);
}

/* Convert an rt block number into an rt extent number. */
static inline xfs_rtxnum_t
xfs_rtb_to_rtx(
        struct xfs_mount        *mp,
        xfs_rtblock_t           rtbno)
{
        /* open-coded 64-bit masking operation */
        rtbno &= mp->m_groups[XG_TYPE_RTG].blkmask;
        if (likely(mp->m_rtxblklog >= 0))
                return rtbno >> mp->m_rtxblklog;
        return div_u64(rtbno, mp->m_sb.sb_rextsize);
}

/* Return the offset of a rtgroup block number within an rt extent. */
static inline xfs_extlen_t
xfs_rgbno_to_rtxoff(
        struct xfs_mount        *mp,
        xfs_rgblock_t           rgbno)
{
        return rgbno % mp->m_sb.sb_rextsize;
}

/* Return the offset of an rt block number within an rt extent. */
static inline xfs_extlen_t
xfs_rtb_to_rtxoff(
        struct xfs_mount        *mp,
        xfs_rtblock_t           rtbno)
{
        /* open-coded 64-bit masking operation */
        rtbno &= mp->m_groups[XG_TYPE_RTG].blkmask;
        if (likely(mp->m_rtxblklog >= 0))
                return rtbno & mp->m_rtxblkmask;
        return do_div(rtbno, mp->m_sb.sb_rextsize);
}

/* Round this file block offset up to the nearest rt extent size. */
static inline xfs_rtblock_t
xfs_fileoff_roundup_rtx(
        struct xfs_mount        *mp,
        xfs_fileoff_t           off)
{
        return roundup_64(off, mp->m_sb.sb_rextsize);
}

/* Round this file block offset down to the nearest rt extent size. */
static inline xfs_rtblock_t
xfs_fileoff_rounddown_rtx(
        struct xfs_mount        *mp,
        xfs_fileoff_t           off)
{
        return rounddown_64(off, mp->m_sb.sb_rextsize);
}

/* Convert an rt extent number to a file block offset in the rt bitmap file. */
static inline xfs_fileoff_t
xfs_rtx_to_rbmblock(
        struct xfs_mount        *mp,
        xfs_rtxnum_t            rtx)
{
        if (xfs_has_rtgroups(mp))
                return div_u64(rtx, mp->m_rtx_per_rbmblock);

        return rtx >> mp->m_blkbit_log;
}

/* Convert an rt extent number to a word offset within an rt bitmap block. */
static inline unsigned int
xfs_rtx_to_rbmword(
        struct xfs_mount        *mp,
        xfs_rtxnum_t            rtx)
{
        if (xfs_has_rtgroups(mp)) {
                unsigned int    mod;

                div_u64_rem(rtx >> XFS_NBWORDLOG, mp->m_blockwsize, &mod);
                return mod;
        }

        return (rtx >> XFS_NBWORDLOG) & (mp->m_blockwsize - 1);
}

/* Convert a file block offset in the rt bitmap file to an rt extent number. */
static inline xfs_rtxnum_t
xfs_rbmblock_to_rtx(
        struct xfs_mount        *mp,
        xfs_fileoff_t           rbmoff)
{
        if (xfs_has_rtgroups(mp))
                return rbmoff * mp->m_rtx_per_rbmblock;

        return rbmoff << mp->m_blkbit_log;
}

/* Return a pointer to a bitmap word within a rt bitmap block. */
static inline union xfs_rtword_raw *
xfs_rbmblock_wordptr(
        struct xfs_rtalloc_args *args,
        unsigned int            index)
{
        struct xfs_mount        *mp = args->mp;
        union xfs_rtword_raw    *words;
        struct xfs_rtbuf_blkinfo *hdr = args->rbmbp->b_addr;

        if (xfs_has_rtgroups(mp))
                words = (union xfs_rtword_raw *)(hdr + 1);
        else
                words = args->rbmbp->b_addr;

        return words + index;
}

/* Convert an ondisk bitmap word to its incore representation. */
static inline xfs_rtword_t
xfs_rtbitmap_getword(
        struct xfs_rtalloc_args *args,
        unsigned int            index)
{
        union xfs_rtword_raw    *word = xfs_rbmblock_wordptr(args, index);

        if (xfs_has_rtgroups(args->mp))
                return be32_to_cpu(word->rtg);
        return word->old;
}

/* Set an ondisk bitmap word from an incore representation. */
static inline void
xfs_rtbitmap_setword(
        struct xfs_rtalloc_args *args,
        unsigned int            index,
        xfs_rtword_t            value)
{
        union xfs_rtword_raw    *word = xfs_rbmblock_wordptr(args, index);

        if (xfs_has_rtgroups(args->mp))
                word->rtg = cpu_to_be32(value);
        else
                word->old = value;
}

/*
 * Convert a rt extent length and rt bitmap block number to a xfs_suminfo_t
 * offset within the rt summary file.
 */
static inline xfs_rtsumoff_t
xfs_rtsumoffs(
        struct xfs_mount        *mp,
        int                     log2_len,
        xfs_fileoff_t           rbmoff)
{
        return log2_len * mp->m_sb.sb_rbmblocks + rbmoff;
}

/*
 * Convert an xfs_suminfo_t offset to a file block offset within the rt summary
 * file.
 */
static inline xfs_fileoff_t
xfs_rtsumoffs_to_block(
        struct xfs_mount        *mp,
        xfs_rtsumoff_t          rsumoff)
{
        if (xfs_has_rtgroups(mp))
                return rsumoff / mp->m_blockwsize;

        return XFS_B_TO_FSBT(mp, rsumoff * sizeof(xfs_suminfo_t));
}

/*
 * Convert an xfs_suminfo_t offset to an info word offset within an rt summary
 * block.
 */
static inline unsigned int
xfs_rtsumoffs_to_infoword(
        struct xfs_mount        *mp,
        xfs_rtsumoff_t          rsumoff)
{
        unsigned int            mask = mp->m_blockmask >> XFS_SUMINFOLOG;

        if (xfs_has_rtgroups(mp))
                return rsumoff % mp->m_blockwsize;

        return rsumoff & mask;
}

/* Return a pointer to a summary info word within a rt summary block. */
static inline union xfs_suminfo_raw *
xfs_rsumblock_infoptr(
        struct xfs_rtalloc_args *args,
        unsigned int            index)
{
        union xfs_suminfo_raw   *info;
        struct xfs_rtbuf_blkinfo *hdr = args->sumbp->b_addr;

        if (xfs_has_rtgroups(args->mp))
                info = (union xfs_suminfo_raw *)(hdr + 1);
        else
                info = args->sumbp->b_addr;

        return info + index;
}

/* Get the current value of a summary counter. */
static inline xfs_suminfo_t
xfs_suminfo_get(
        struct xfs_rtalloc_args *args,
        unsigned int            index)
{
        union xfs_suminfo_raw   *info = xfs_rsumblock_infoptr(args, index);

        if (xfs_has_rtgroups(args->mp))
                return be32_to_cpu(info->rtg);
        return info->old;
}

/* Add to the current value of a summary counter and return the new value. */
static inline xfs_suminfo_t
xfs_suminfo_add(
        struct xfs_rtalloc_args *args,
        unsigned int            index,
        int                     delta)
{
        union xfs_suminfo_raw   *info = xfs_rsumblock_infoptr(args, index);

        if (xfs_has_rtgroups(args->mp)) {
                be32_add_cpu(&info->rtg, delta);
                return be32_to_cpu(info->rtg);
        }

        info->old += delta;
        return info->old;
}

static inline const struct xfs_buf_ops *
xfs_rtblock_ops(
        struct xfs_mount        *mp,
        enum xfs_rtg_inodes     type)
{
        if (xfs_has_rtgroups(mp)) {
                if (type == XFS_RTGI_SUMMARY)
                        return &xfs_rtsummary_buf_ops;
                return &xfs_rtbitmap_buf_ops;
        }
        return &xfs_rtbuf_ops;
}

/*
 * Functions for walking free space rtextents in the realtime bitmap.
 */
struct xfs_rtalloc_rec {
        xfs_rtxnum_t            ar_startext;
        xfs_rtbxlen_t           ar_extcount;
};

typedef int (*xfs_rtalloc_query_range_fn)(
        struct xfs_rtgroup              *rtg,
        struct xfs_trans                *tp,
        const struct xfs_rtalloc_rec    *rec,
        void                            *priv);

#ifdef CONFIG_XFS_RT
void xfs_rtbuf_cache_relse(struct xfs_rtalloc_args *args);
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 *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,
                xfs_rtxlen_t len, int val);
int xfs_rtget_summary(struct xfs_rtalloc_args *args, int log,
                xfs_fileoff_t bbno, xfs_suminfo_t *sum);
int xfs_rtmodify_summary(struct xfs_rtalloc_args *args, int log,
                xfs_fileoff_t bbno, int delta);
int xfs_rtfree_range(struct xfs_rtalloc_args *args, xfs_rtxnum_t start,
                xfs_rtxlen_t len);
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);
int xfs_rtalloc_query_all(struct xfs_rtgroup *rtg, struct xfs_trans *tp,
                xfs_rtalloc_query_range_fn fn, void *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);
int xfs_rtfree_extent(struct xfs_trans *tp, struct xfs_rtgroup *rtg,
                xfs_rtxnum_t start, xfs_rtxlen_t len);
/* Same as above, but in units of rt blocks. */
int xfs_rtfree_blocks(struct xfs_trans *tp, struct xfs_rtgroup *rtg,
                xfs_fsblock_t rtbno, xfs_filblks_t rtlen);

xfs_rtxnum_t xfs_rtbitmap_rtx_per_rbmblock(struct xfs_mount *mp);
xfs_filblks_t xfs_rtbitmap_blockcount(struct xfs_mount *mp);
xfs_filblks_t xfs_rtbitmap_blockcount_len(struct xfs_mount *mp,
                xfs_rtbxlen_t rtextents);
xfs_filblks_t xfs_rtsummary_blockcount(struct xfs_mount *mp,
                unsigned int *rsumlevels);

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);
int xfs_rtbitmap_create(struct xfs_rtgroup *rtg, struct xfs_inode *ip,
                struct xfs_trans *tp, bool init);
int xfs_rtsummary_create(struct xfs_rtgroup *rtg, struct xfs_inode *ip,
                struct xfs_trans *tp, bool init);

#else /* CONFIG_XFS_RT */
# define xfs_rtfree_extent(t,b,l)                       (-ENOSYS)

static inline int xfs_rtfree_blocks(struct xfs_trans *tp,
                struct xfs_rtgroup *rtg, xfs_fsblock_t rtbno,
                xfs_filblks_t rtlen)
{
        return -ENOSYS;
}
# define xfs_rtalloc_query_range(m,t,l,h,f,p)           (-ENOSYS)
# define xfs_rtalloc_query_all(m,t,f,p)                 (-ENOSYS)
# define xfs_rtbitmap_read_buf(a,b)                     (-ENOSYS)
# define xfs_rtsummary_read_buf(a,b)                    (-ENOSYS)
# define xfs_rtbuf_cache_relse(a)                       (0)
# define xfs_rtalloc_extent_is_free(m,t,s,l,i)          (-ENOSYS)
static inline xfs_filblks_t
xfs_rtbitmap_blockcount_len(struct xfs_mount *mp, xfs_rtbxlen_t rtextents)
{
        /* shut up gcc */
        return 0;
}
#endif /* CONFIG_XFS_RT */

#endif /* __XFS_RTBITMAP_H__ */