linux/fs/xfs/libxfs/xfs_rtbitmap.c

1098 lines
27 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#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"
/*
* Realtime allocator bitmap functions shared with userspace.
*/
/*
* Real time buffers need verifiers to avoid runtime warnings during IO.
* We don't have anything to verify, however, so these are just dummy
* operations.
*/
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,
};
/*
* Get a buffer for the bitmap or summary file block specified.
* The buffer is returned read and locked.
*/
int
xfs_rtbuf_get(
xfs_mount_t *mp, /* file system mount structure */
xfs_trans_t *tp, /* transaction pointer */
xfs_rtblock_t block, /* block number in bitmap or summary */
int issum, /* is summary not bitmap */
struct xfs_buf **bpp) /* output: buffer for the block */
{
struct xfs_buf *bp; /* block buffer, result */
xfs_inode_t *ip; /* bitmap or summary inode */
xfs_bmbt_irec_t map;
int nmap = 1;
int error; /* error value */
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;
}
/*
* Searching backward from start to limit, find the first block whose
* allocated/free state is different from start's.
*/
int
xfs_rtfind_back(
xfs_mount_t *mp, /* file system mount point */
xfs_trans_t *tp, /* transaction pointer */
xfs_rtblock_t start, /* starting block to look at */
xfs_rtblock_t limit, /* last block to look at */
xfs_rtblock_t *rtblock) /* out: start block found */
{
xfs_rtword_t *b; /* current word in buffer */
int bit; /* bit number in the word */
xfs_rtblock_t block; /* bitmap block number */
struct xfs_buf *bp; /* buf for the block */
xfs_rtword_t *bufp; /* starting word in buffer */
int error; /* error value */
xfs_rtblock_t firstbit; /* first useful bit in the word */
xfs_rtblock_t i; /* current bit number rel. to start */
xfs_rtblock_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 */
int word; /* word number in the buffer */
/*
* Compute and read in starting bitmap block for starting block.
*/
block = XFS_BITTOBLOCK(mp, start);
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
if (error) {
return error;
}
bufp = bp->b_addr;
/*
* Get the first word's index & point to it.
*/
word = XFS_BITTOWORD(mp, start);
b = &bufp[word];
bit = (int)(start & (XFS_NBWORD - 1));
len = start - limit + 1;
/*
* Compute match value, based on the bit at start: if 1 (free)
* then all-ones, else all-zeroes.
*/
want = (*b & ((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 = XFS_RTMAX((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 = (*b ^ want) & mask)) {
/*
* Different. Mark where we are and return.
*/
xfs_trans_brelse(tp, bp);
i = bit - XFS_RTHIBIT(wdiff);
*rtblock = 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.
*/
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 {
/*
* Go on to the previous word in the buffer.
*/
b--;
}
} 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.
*/
if ((wdiff = *b ^ want)) {
/*
* Different, mark where we are and return.
*/
xfs_trans_brelse(tp, bp);
i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
*rtblock = 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.
*/
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 {
/*
* Go on to the previous word in the buffer.
*/
b--;
}
}
/*
* 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.
*/
if ((wdiff = (*b ^ want) & mask)) {
/*
* Different, mark where we are and return.
*/
xfs_trans_brelse(tp, bp);
i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
*rtblock = start - i + 1;
return 0;
} else
i = len;
}
/*
* No match, return that we scanned the whole area.
*/
xfs_trans_brelse(tp, bp);
*rtblock = 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(
xfs_mount_t *mp, /* file system mount point */
xfs_trans_t *tp, /* transaction pointer */
xfs_rtblock_t start, /* starting block to look at */
xfs_rtblock_t limit, /* last block to look at */
xfs_rtblock_t *rtblock) /* out: start block found */
{
xfs_rtword_t *b; /* current word in buffer */
int bit; /* bit number in the word */
xfs_rtblock_t block; /* bitmap block number */
struct xfs_buf *bp; /* buf for the block */
xfs_rtword_t *bufp; /* starting word in buffer */
int error; /* error value */
xfs_rtblock_t i; /* current bit number rel. to start */
xfs_rtblock_t lastbit; /* last useful bit in the word */
xfs_rtblock_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 */
int word; /* word number in the buffer */
/*
* Compute and read in starting bitmap block for starting block.
*/
block = XFS_BITTOBLOCK(mp, start);
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
if (error) {
return error;
}
bufp = bp->b_addr;
/*
* Get the first word's index & point to it.
*/
word = XFS_BITTOWORD(mp, start);
b = &bufp[word];
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.
*/
want = (*b & ((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 = XFS_RTMIN(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 = (*b ^ want) & mask)) {
/*
* Different. Mark where we are and return.
*/
xfs_trans_brelse(tp, bp);
i = XFS_RTLOBIT(wdiff) - bit;
*rtblock = 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 == XFS_BLOCKWSIZE(mp) && i < len) {
/*
* If done with this block, get the previous one.
*/
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 {
/*
* Go on to the previous word in the buffer.
*/
b++;
}
} 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.
*/
if ((wdiff = *b ^ want)) {
/*
* Different, mark where we are and return.
*/
xfs_trans_brelse(tp, bp);
i += XFS_RTLOBIT(wdiff);
*rtblock = 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 == XFS_BLOCKWSIZE(mp) && i < len) {
/*
* If done with this block, get the next one.
*/
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 {
/*
* Go on to the next word in the buffer.
*/
b++;
}
}
/*
* 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.
*/
if ((wdiff = (*b ^ want) & mask)) {
/*
* Different, mark where we are and return.
*/
xfs_trans_brelse(tp, bp);
i += XFS_RTLOBIT(wdiff);
*rtblock = start + i - 1;
return 0;
} else
i = len;
}
/*
* No match, return that we scanned the whole area.
*/
xfs_trans_brelse(tp, bp);
*rtblock = start + i - 1;
return 0;
}
/*
* Read and/or modify the summary information for a given extent size,
* bitmap block combination.
* Keeps track of a current summary block, so we don't keep reading
* it from the buffer cache.
*
* Summary information is returned in *sum if specified.
* If no delta is specified, returns summary only.
*/
int
xfs_rtmodify_summary_int(
xfs_mount_t *mp, /* file system mount structure */
xfs_trans_t *tp, /* transaction pointer */
int log, /* log2 of extent size */
xfs_rtblock_t bbno, /* bitmap block number */
int delta, /* change to make to summary info */
struct xfs_buf **rbpp, /* in/out: summary block buffer */
xfs_fsblock_t *rsb, /* in/out: summary block number */
xfs_suminfo_t *sum) /* out: summary info for this block */
{
struct xfs_buf *bp; /* buffer for the summary block */
int error; /* error value */
xfs_fsblock_t sb; /* summary fsblock */
int so; /* index into the summary file */
xfs_suminfo_t *sp; /* pointer to returned data */
/*
* Compute entry number in the summary file.
*/
so = XFS_SUMOFFS(mp, log, bbno);
/*
* Compute the block number in the summary file.
*/
sb = XFS_SUMOFFSTOBLOCK(mp, so);
/*
* If we have an old buffer, and the block number matches, use that.
*/
if (*rbpp && *rsb == sb)
bp = *rbpp;
/*
* Otherwise we have to get the buffer.
*/
else {
/*
* If there was an old one, get rid of it first.
*/
if (*rbpp)
xfs_trans_brelse(tp, *rbpp);
error = xfs_rtbuf_get(mp, tp, sb, 1, &bp);
if (error) {
return error;
}
/*
* Remember this buffer and block for the next call.
*/
*rbpp = bp;
*rsb = sb;
}
/*
* Point to the summary information, modify/log it, and/or copy it out.
*/
sp = XFS_SUMPTR(mp, bp, so);
if (delta) {
uint first = (uint)((char *)sp - (char *)bp->b_addr);
*sp += delta;
xfs: cache minimum realtime summary level The realtime summary is a two-dimensional array on disk, effectively: u32 rsum[log2(number of realtime extents) + 1][number of blocks in the bitmap] rsum[log][bbno] is the number of extents of size 2**log which start in bitmap block bbno. xfs_rtallocate_extent_near() uses xfs_rtany_summary() to check whether rsum[log][bbno] != 0 for any log level. However, the summary array is stored in row-major order (i.e., like an array in C), so all of these entries are not adjacent, but rather spread across the entire summary file. In the worst case (a full bitmap block), xfs_rtany_summary() has to check every level. This means that on a moderately-used realtime device, an allocation will waste a lot of time finding, reading, and releasing buffers for the realtime summary. In particular, one of our storage services (which runs on servers with 8 very slow CPUs and 15 8 TB XFS realtime filesystems) spends almost 5% of its CPU cycles in xfs_rtbuf_get() and xfs_trans_brelse() called from xfs_rtany_summary(). One solution would be to also store the summary with the dimensions swapped. However, this would require a disk format change to a very old component of XFS. Instead, we can cache the minimum size which contains any extents. We do so lazily; rather than guaranteeing that the cache contains the precise minimum, it always contains a loose lower bound which we tighten when we read or update a summary block. This only uses a few kilobytes of memory and is already serialized via the realtime bitmap and summary inode locks, so the cost is minimal. With this change, the same workload only spends 0.2% of its CPU cycles in the realtime allocator. Signed-off-by: Omar Sandoval <osandov@fb.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2018-12-12 16:46:32 +00:00
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, /* file system mount structure */
xfs_trans_t *tp, /* transaction pointer */
int log, /* log2 of extent size */
xfs_rtblock_t bbno, /* bitmap block number */
int delta, /* change to make to summary info */
struct xfs_buf **rbpp, /* in/out: summary block buffer */
xfs_fsblock_t *rsb) /* in/out: summary block number */
{
return xfs_rtmodify_summary_int(mp, tp, log, bbno,
delta, rbpp, rsb, NULL);
}
/*
* Set the given range of bitmap bits to the given value.
* Do whatever I/O and logging is required.
*/
int
xfs_rtmodify_range(
xfs_mount_t *mp, /* file system mount point */
xfs_trans_t *tp, /* transaction pointer */
xfs_rtblock_t start, /* starting block to modify */
xfs_extlen_t len, /* length of extent to modify */
int val) /* 1 for free, 0 for allocated */
{
xfs_rtword_t *b; /* current word in buffer */
int bit; /* bit number in the word */
xfs_rtblock_t block; /* bitmap block number */
struct xfs_buf *bp; /* buf for the block */
xfs_rtword_t *bufp; /* starting word in buffer */
int error; /* error value */
xfs_rtword_t *first; /* first used word in the buffer */
int i; /* current bit number rel. to start */
int lastbit; /* last useful bit in word */
xfs_rtword_t mask; /* mask o frelevant bits for value */
int word; /* word number in the buffer */
/*
* Compute starting bitmap block number.
*/
block = XFS_BITTOBLOCK(mp, start);
/*
* Read the bitmap block, and point to its data.
*/
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
if (error) {
return error;
}
bufp = bp->b_addr;
/*
* Compute the starting word's address, and starting bit.
*/
word = XFS_BITTOWORD(mp, start);
first = b = &bufp[word];
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 = XFS_RTMIN(bit + len, XFS_NBWORD);
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
/*
* Set/clear the active bits.
*/
if (val)
*b |= mask;
else
*b &= ~mask;
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 == XFS_BLOCKWSIZE(mp) && i < len) {
/*
* Log the changed part of this block.
* Get the next one.
*/
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 {
/*
* Go on to the next word in the buffer
*/
b++;
}
} 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.
*/
*b = 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 == XFS_BLOCKWSIZE(mp) && i < len) {
/*
* Log the changed part of this block.
* Get the next one.
*/
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 {
/*
* Go on to the next word in the buffer
*/
b++;
}
}
/*
* 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.
*/
if (val)
*b |= mask;
else
*b &= ~mask;
b++;
}
/*
* Log any remaining changed bytes.
*/
if (b > first)
xfs_trans_log_buf(tp, bp, (uint)((char *)first - (char *)bufp),
(uint)((char *)b - (char *)bufp - 1));
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(
xfs_mount_t *mp, /* file system mount point */
xfs_trans_t *tp, /* transaction pointer */
xfs_rtblock_t start, /* starting block to free */
xfs_extlen_t len, /* length to free */
struct xfs_buf **rbpp, /* in/out: summary block buffer */
xfs_fsblock_t *rsb) /* in/out: summary block number */
{
xfs_rtblock_t end; /* end of the freed extent */
int error; /* error value */
xfs_rtblock_t postblock; /* first block freed > end */
xfs_rtblock_t preblock; /* first block freed < start */
end = start + len - 1;
/*
* Modify the bitmap to mark this extent freed.
*/
error = xfs_rtmodify_range(mp, tp, 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(mp, tp, start, 0, &preblock);
if (error) {
return error;
}
/*
* Find the next allocated block (end of allocated extent).
*/
error = xfs_rtfind_forw(mp, tp, end, mp->m_sb.sb_rextents - 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(mp, tp,
XFS_RTBLOCKLOG(start - preblock),
XFS_BITTOBLOCK(mp, preblock), -1, rbpp, rsb);
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(mp, tp,
XFS_RTBLOCKLOG(postblock - end),
XFS_BITTOBLOCK(mp, end + 1), -1, rbpp, rsb);
if (error) {
return error;
}
}
/*
* Increment the summary information corresponding to the entire
* (new) free extent.
*/
error = xfs_rtmodify_summary(mp, tp,
XFS_RTBLOCKLOG(postblock + 1 - preblock),
XFS_BITTOBLOCK(mp, preblock), 1, rbpp, rsb);
return error;
}
/*
* Check that the given range is either all allocated (val = 0) or
* all free (val = 1).
*/
int
xfs_rtcheck_range(
xfs_mount_t *mp, /* file system mount point */
xfs_trans_t *tp, /* transaction pointer */
xfs_rtblock_t start, /* starting block number of extent */
xfs_extlen_t len, /* length of extent */
int val, /* 1 for free, 0 for allocated */
xfs_rtblock_t *new, /* out: first block not matching */
int *stat) /* out: 1 for matches, 0 for not */
{
xfs_rtword_t *b; /* current word in buffer */
int bit; /* bit number in the word */
xfs_rtblock_t block; /* bitmap block number */
struct xfs_buf *bp; /* buf for the block */
xfs_rtword_t *bufp; /* starting word in buffer */
int error; /* error value */
xfs_rtblock_t i; /* current bit number rel. to start */
xfs_rtblock_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 */
int word; /* word number in the buffer */
/*
* Compute starting bitmap block number
*/
block = XFS_BITTOBLOCK(mp, start);
/*
* Read the bitmap block.
*/
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
if (error) {
return error;
}
bufp = bp->b_addr;
/*
* Compute the starting word's address, and starting bit.
*/
word = XFS_BITTOWORD(mp, start);
b = &bufp[word];
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 = XFS_RTMIN(bit + len, XFS_NBWORD);
/*
* Mask of relevant bits.
*/
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
/*
* Compute difference between actual and desired value.
*/
if ((wdiff = (*b ^ val) & mask)) {
/*
* Different, compute first wrong bit and return.
*/
xfs_trans_brelse(tp, bp);
i = XFS_RTLOBIT(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 == XFS_BLOCKWSIZE(mp) && i < len) {
/*
* If done with this block, get the next one.
*/
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 {
/*
* Go on to the next word in the buffer.
*/
b++;
}
} 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.
*/
if ((wdiff = *b ^ val)) {
/*
* Different, compute first wrong bit and return.
*/
xfs_trans_brelse(tp, bp);
i += XFS_RTLOBIT(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 == XFS_BLOCKWSIZE(mp) && i < len) {
/*
* If done with this block, get the next one.
*/
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 {
/*
* Go on to the next word in the buffer.
*/
b++;
}
}
/*
* 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.
*/
if ((wdiff = (*b ^ val) & mask)) {
/*
* Different, compute first wrong bit and return.
*/
xfs_trans_brelse(tp, bp);
i += XFS_RTLOBIT(wdiff);
*new = start + i;
*stat = 0;
return 0;
} else
i = len;
}
/*
* Successful, return.
*/
xfs_trans_brelse(tp, bp);
*new = start + i;
*stat = 1;
return 0;
}
#ifdef DEBUG
/*
* Check that the given extent (block range) is allocated already.
*/
STATIC int /* error */
xfs_rtcheck_alloc_range(
xfs_mount_t *mp, /* file system mount point */
xfs_trans_t *tp, /* transaction pointer */
xfs_rtblock_t bno, /* starting block number of extent */
xfs_extlen_t len) /* length of extent */
{
xfs_rtblock_t new; /* dummy for xfs_rtcheck_range */
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
/*
* Free an extent in the realtime subvolume. Length is expressed in
* realtime extents, as is the block number.
*/
int /* error */
xfs_rtfree_extent(
xfs_trans_t *tp, /* transaction pointer */
xfs_rtblock_t bno, /* starting block number to free */
xfs_extlen_t len) /* length of extent freed */
{
int error; /* error value */
xfs_mount_t *mp; /* file system mount structure */
xfs_fsblock_t sb; /* summary file block number */
struct xfs_buf *sumbp = NULL; /* summary file block buffer */
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;
/*
* Free the range of realtime blocks.
*/
error = xfs_rtfree_range(mp, tp, bno, len, &sumbp, &sb);
if (error) {
return error;
}
/*
* 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.
*/
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;
}
/* Find all the free records within a given range. */
int
xfs_rtalloc_query_range(
struct xfs_trans *tp,
struct xfs_rtalloc_rec *low_rec,
struct xfs_rtalloc_rec *high_rec,
xfs_rtalloc_query_range_fn fn,
void *priv)
{
struct xfs_rtalloc_rec rec;
struct xfs_mount *mp = tp->t_mountp;
xfs_rtblock_t rtstart;
xfs_rtblock_t rtend;
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;
xfs: fix high key handling in the rt allocator's query_range function Fix some off-by-one errors in xfs_rtalloc_query_range. The highest key in the realtime bitmap is always one less than the number of rt extents, which means that the key clamp at the start of the function is wrong. The 4th argument to xfs_rtfind_forw is the highest rt extent that we want to probe, which means that passing 1 less than the high key is wrong. Finally, drop the rem variable that controls the loop because we can compare the iteration point (rtstart) against the high key directly. The sordid history of this function is that the original commit (fb3c3) incorrectly passed (high_rec->ar_startblock - 1) as the 'limit' parameter to xfs_rtfind_forw. This was wrong because the "high key" is supposed to be the largest key for which the caller wants result rows, not the key for the first row that could possibly be outside the range that the caller wants to see. A subsequent attempt (8ad56) to strengthen the parameter checking added incorrect clamping of the parameters to the number of rt blocks in the system (despite the bitmap functions all taking units of rt extents) to avoid querying ranges past the end of rt bitmap file but failed to fix the incorrect _rtfind_forw parameter. The original _rtfind_forw parameter error then survived the conversion of the startblock and blockcount fields to rt extents (a0e5c), and the most recent off-by-one fix (a3a37) thought it was patching a problem when the end of the rt volume is not in use, but none of these fixes actually solved the original problem that the author was confused about the "limit" argument to xfs_rtfind_forw. Sadly, all four of these patches were written by this author and even his own usage of this function and rt testing were inadequate to get this fixed quickly. Original-problem: fb3c3de2f65c ("xfs: add a couple of queries to iterate free extents in the rtbitmap") Not-fixed-by: 8ad560d2565e ("xfs: strengthen rtalloc query range checks") Not-fixed-by: a0e5c435babd ("xfs: fix xfs_rtalloc_rec units") Fixes: a3a374bf1889 ("xfs: fix off-by-one error in xfs_rtalloc_query_range") Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
2020-10-13 15:46:27 +00:00
high_rec->ar_startext = min(high_rec->ar_startext,
mp->m_sb.sb_rextents - 1);
/* Iterate the bitmap, looking for discrepancies. */
rtstart = low_rec->ar_startext;
xfs: fix high key handling in the rt allocator's query_range function Fix some off-by-one errors in xfs_rtalloc_query_range. The highest key in the realtime bitmap is always one less than the number of rt extents, which means that the key clamp at the start of the function is wrong. The 4th argument to xfs_rtfind_forw is the highest rt extent that we want to probe, which means that passing 1 less than the high key is wrong. Finally, drop the rem variable that controls the loop because we can compare the iteration point (rtstart) against the high key directly. The sordid history of this function is that the original commit (fb3c3) incorrectly passed (high_rec->ar_startblock - 1) as the 'limit' parameter to xfs_rtfind_forw. This was wrong because the "high key" is supposed to be the largest key for which the caller wants result rows, not the key for the first row that could possibly be outside the range that the caller wants to see. A subsequent attempt (8ad56) to strengthen the parameter checking added incorrect clamping of the parameters to the number of rt blocks in the system (despite the bitmap functions all taking units of rt extents) to avoid querying ranges past the end of rt bitmap file but failed to fix the incorrect _rtfind_forw parameter. The original _rtfind_forw parameter error then survived the conversion of the startblock and blockcount fields to rt extents (a0e5c), and the most recent off-by-one fix (a3a37) thought it was patching a problem when the end of the rt volume is not in use, but none of these fixes actually solved the original problem that the author was confused about the "limit" argument to xfs_rtfind_forw. Sadly, all four of these patches were written by this author and even his own usage of this function and rt testing were inadequate to get this fixed quickly. Original-problem: fb3c3de2f65c ("xfs: add a couple of queries to iterate free extents in the rtbitmap") Not-fixed-by: 8ad560d2565e ("xfs: strengthen rtalloc query range checks") Not-fixed-by: a0e5c435babd ("xfs: fix xfs_rtalloc_rec units") Fixes: a3a374bf1889 ("xfs: fix off-by-one error in xfs_rtalloc_query_range") Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
2020-10-13 15:46:27 +00:00
while (rtstart <= high_rec->ar_startext) {
/* Is the first block free? */
error = xfs_rtcheck_range(mp, tp, rtstart, 1, 1, &rtend,
&is_free);
if (error)
break;
/* How long does the extent go for? */
error = xfs_rtfind_forw(mp, tp, rtstart,
xfs: fix high key handling in the rt allocator's query_range function Fix some off-by-one errors in xfs_rtalloc_query_range. The highest key in the realtime bitmap is always one less than the number of rt extents, which means that the key clamp at the start of the function is wrong. The 4th argument to xfs_rtfind_forw is the highest rt extent that we want to probe, which means that passing 1 less than the high key is wrong. Finally, drop the rem variable that controls the loop because we can compare the iteration point (rtstart) against the high key directly. The sordid history of this function is that the original commit (fb3c3) incorrectly passed (high_rec->ar_startblock - 1) as the 'limit' parameter to xfs_rtfind_forw. This was wrong because the "high key" is supposed to be the largest key for which the caller wants result rows, not the key for the first row that could possibly be outside the range that the caller wants to see. A subsequent attempt (8ad56) to strengthen the parameter checking added incorrect clamping of the parameters to the number of rt blocks in the system (despite the bitmap functions all taking units of rt extents) to avoid querying ranges past the end of rt bitmap file but failed to fix the incorrect _rtfind_forw parameter. The original _rtfind_forw parameter error then survived the conversion of the startblock and blockcount fields to rt extents (a0e5c), and the most recent off-by-one fix (a3a37) thought it was patching a problem when the end of the rt volume is not in use, but none of these fixes actually solved the original problem that the author was confused about the "limit" argument to xfs_rtfind_forw. Sadly, all four of these patches were written by this author and even his own usage of this function and rt testing were inadequate to get this fixed quickly. Original-problem: fb3c3de2f65c ("xfs: add a couple of queries to iterate free extents in the rtbitmap") Not-fixed-by: 8ad560d2565e ("xfs: strengthen rtalloc query range checks") Not-fixed-by: a0e5c435babd ("xfs: fix xfs_rtalloc_rec units") Fixes: a3a374bf1889 ("xfs: fix off-by-one error in xfs_rtalloc_query_range") Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
2020-10-13 15:46:27 +00:00
high_rec->ar_startext, &rtend);
if (error)
break;
if (is_free) {
rec.ar_startext = rtstart;
rec.ar_extcount = rtend - rtstart + 1;
error = fn(tp, &rec, priv);
if (error)
break;
}
rtstart = rtend + 1;
}
return error;
}
/* Find all the free records. */
int
xfs_rtalloc_query_all(
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 = tp->t_mountp->m_sb.sb_rextents - 1;
keys[0].ar_extcount = keys[1].ar_extcount = 0;
return xfs_rtalloc_query_range(tp, &keys[0], &keys[1], fn, priv);
}
/* Is the given extent all free? */
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;
}