// 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_alloc.h"
#include "xfs_bmap.h"
#include "xfs_bmap_btree.h"
#include "xfs_bmap_util.h"
#include "xfs_trans.h"
#include "xfs_trans_space.h"
#include "xfs_icache.h"
#include "xfs_rtalloc.h"
#include "xfs_sb.h"
#include "xfs_rtbitmap.h"
#include "xfs_quota.h"
#include "xfs_log_priv.h"
#include "xfs_health.h"
/*
* Return whether there are any free extents in the size range given
* by low and high, for the bitmap block bbno.
*/
STATIC int
xfs_rtany_summary(
struct xfs_rtalloc_args *args,
int low, /* low log2 extent size */
int high, /* high log2 extent size */
xfs_fileoff_t bbno, /* bitmap block number */
int *maxlog) /* out: max log2 extent size free */
{
struct xfs_mount *mp = args->mp;
int error;
int log; /* loop counter, log2 of ext. size */
xfs_suminfo_t sum; /* summary data */
/* There are no extents at levels >= m_rsum_cache[bbno]. */
if (mp->m_rsum_cache) {
high = min(high, mp->m_rsum_cache[bbno] - 1);
if (low > high) {
*maxlog = -1;
return 0;
}
}
/*
* Loop over logs of extent sizes.
*/
for (log = high; log >= low; log--) {
/*
* Get one summary datum.
*/
error = xfs_rtget_summary(args, log, bbno, &sum);
if (error) {
return error;
}
/*
* If there are any, return success.
*/
if (sum) {
*maxlog = log;
goto out;
}
}
/*
* Found nothing, return failure.
*/
*maxlog = -1;
out:
/* There were no extents at levels > log. */
if (mp->m_rsum_cache && log + 1 < mp->m_rsum_cache[bbno])
mp->m_rsum_cache[bbno] = log + 1;
return 0;
}
/*
* Copy and transform the summary file, given the old and new
* parameters in the mount structures.
*/
STATIC int
xfs_rtcopy_summary(
struct xfs_rtalloc_args *oargs,
struct xfs_rtalloc_args *nargs)
{
xfs_fileoff_t bbno; /* bitmap block number */
int error;
int log; /* summary level number (log length) */
xfs_suminfo_t sum; /* summary data */
for (log = oargs->mp->m_rsumlevels - 1; log >= 0; log--) {
for (bbno = oargs->mp->m_sb.sb_rbmblocks - 1;
(xfs_srtblock_t)bbno >= 0;
bbno--) {
error = xfs_rtget_summary(oargs, log, bbno, &sum);
if (error)
goto out;
if (sum == 0)
continue;
error = xfs_rtmodify_summary(oargs, log, bbno, -sum);
if (error)
goto out;
error = xfs_rtmodify_summary(nargs, log, bbno, sum);
if (error)
goto out;
ASSERT(sum > 0);
}
}
error = 0;
out:
xfs_rtbuf_cache_relse(oargs);
return 0;
}
/*
* Mark an extent specified by start and len allocated.
* Updates all the summary information as well as the bitmap.
*/
STATIC int
xfs_rtallocate_range(
struct xfs_rtalloc_args *args,
xfs_rtxnum_t start, /* start rtext to allocate */
xfs_rtxlen_t len) /* in/out: summary block number */
{
struct xfs_mount *mp = args->mp;
xfs_rtxnum_t end; /* end of the allocated rtext */
int error;
xfs_rtxnum_t postblock = 0; /* first rtext allocated > end */
xfs_rtxnum_t preblock = 0; /* first rtext allocated < start */
end = start + len - 1;
/*
* Assume we're allocating out of the middle of a free 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 free extent).
*/
error = xfs_rtfind_forw(args, end, mp->m_sb.sb_rextents - 1,
&postblock);
if (error)
return error;
/*
* Decrement the summary information corresponding to the entire
* (old) free extent.
*/
error = xfs_rtmodify_summary(args,
xfs_highbit64(postblock + 1 - preblock),
xfs_rtx_to_rbmblock(mp, preblock), -1);
if (error)
return error;
/*
* If there are blocks not being allocated at the front of the
* old extent, add summary data for them to be free.
*/
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 allocated at the end of the
* old extent, add summary data for them to be free.
*/
if (postblock > end) {
error = xfs_rtmodify_summary(args,
xfs_highbit64(postblock - end),
xfs_rtx_to_rbmblock(mp, end + 1), 1);
if (error)
return error;
}
/*
* Modify the bitmap to mark this extent allocated.
*/
return xfs_rtmodify_range(args, start, len, 0);
}
/*
* Make sure we don't run off the end of the rt volume. Be careful that
* adjusting maxlen downwards doesn't cause us to fail the alignment checks.
*/
static inline xfs_rtxlen_t
xfs_rtallocate_clamp_len(
struct xfs_mount *mp,
xfs_rtxnum_t startrtx,
xfs_rtxlen_t rtxlen,
xfs_rtxlen_t prod)
{
xfs_rtxlen_t ret;
ret = min(mp->m_sb.sb_rextents, startrtx + rtxlen) - startrtx;
return rounddown(ret, prod);
}
/*
* Attempt to allocate an extent minlen<=len<=maxlen starting from
* bitmap block bbno. If we don't get maxlen then use prod to trim
* the length, if given. Returns error; returns starting block in *rtx.
* The lengths are all in rtextents.
*/
STATIC int
xfs_rtallocate_extent_block(
struct xfs_rtalloc_args *args,
xfs_fileoff_t bbno, /* bitmap block number */
xfs_rtxlen_t minlen, /* minimum length to allocate */
xfs_rtxlen_t maxlen, /* maximum length to allocate */
xfs_rtxlen_t *len, /* out: actual length allocated */
xfs_rtxnum_t *nextp, /* out: next rtext to try */
xfs_rtxlen_t prod, /* extent product factor */
xfs_rtxnum_t *rtx) /* out: start rtext allocated */
{
struct xfs_mount *mp = args->mp;
xfs_rtxnum_t besti; /* best rtext found so far */
xfs_rtxnum_t bestlen;/* best length found so far */
xfs_rtxnum_t end; /* last rtext in chunk */
int error;
xfs_rtxnum_t i; /* current rtext trying */
xfs_rtxnum_t next; /* next rtext to try */
int stat; /* status from internal calls */
/*
* Loop over all the extents starting in this bitmap block,
* looking for one that's long enough.
*/
for (i = xfs_rbmblock_to_rtx(mp, bbno), besti = -1, bestlen = 0,
end = xfs_rbmblock_to_rtx(mp, bbno + 1) - 1;
i <= end;
i++) {
/* Make sure we don't scan off the end of the rt volume. */
maxlen = xfs_rtallocate_clamp_len(mp, i, maxlen, prod);
/*
* See if there's a free extent of maxlen starting at i.
* If it's not so then next will contain the first non-free.
*/
error = xfs_rtcheck_range(args, i, maxlen, 1, &next, &stat);
if (error)
return error;
if (stat) {
/*
* i for maxlen is all free, allocate and return that.
*/
*len = maxlen;
*rtx = i;
return 0;
}
/*
* In the case where we have a variable-sized allocation
* request, figure out how big this free piece is,
* and if it's big enough for the minimum, and the best
* so far, remember it.
*/
if (minlen < maxlen) {
xfs_rtxnum_t thislen; /* this extent size */
thislen = next - i;
if (thislen >= minlen && thislen > bestlen) {
besti = i;
bestlen = thislen;
}
}
/*
* If not done yet, find the start of the next free space.
*/
if (next >= end)
break;
error = xfs_rtfind_forw(args, next, end, &i);
if (error)
return error;
}
/*
* Searched the whole thing & didn't find a maxlen free extent.
*/
if (minlen > maxlen || besti == -1) {
/*
* Allocation failed. Set *nextp to the next block to try.
*/
*nextp = next;
return -ENOSPC;
}
/*
* If size should be a multiple of prod, make that so.
*/
if (prod > 1) {
xfs_rtxlen_t p; /* amount to trim length by */
div_u64_rem(bestlen, prod, &p);
if (p)
bestlen -= p;
}
|