2018-06-06 05:42:14 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-10-15 02:17:56 +04:00
/*
* 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_bmap_util.h"
# include "xfs_bmap_btree.h"
# include "xfs_alloc.h"
# include "xfs_error.h"
# include "xfs_trans.h"
# include "xfs_trans_space.h"
# include "xfs_trace.h"
# include "xfs_buf.h"
# include "xfs_icache.h"
2013-10-29 15:11:58 +04:00
# include "xfs_rtalloc.h"
2013-10-15 02:17:56 +04:00
/*
* Realtime allocator bitmap functions shared with userspace .
*/
2016-02-09 08:41:45 +03:00
/*
* 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 ,
} ;
2013-10-15 02:17:56 +04:00
/*
* Get a buffer for the bitmap or summary file block specified .
* The buffer is returned read and locked .
*/
2017-06-16 21:00:07 +03:00
int
2013-10-15 02:17:56 +04:00
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 */
xfs_buf_t * * bpp ) /* output: buffer for the block */
{
xfs_buf_t * 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 , XFS_DATA_FORK ) ;
if ( error )
return error ;
2018-05-31 19:07:20 +03:00
if ( nmap = = 0 | | ! xfs_bmap_is_real_extent ( & map ) )
return - EFSCORRUPTED ;
2013-10-15 02:17:56 +04:00
ASSERT ( map . br_startblock ! = NULLFSBLOCK ) ;
error = xfs_trans_read_buf ( mp , tp , mp - > m_ddev_targp ,
XFS_FSB_TO_DADDR ( mp , map . br_startblock ) ,
2016-02-09 08:41:45 +03:00
mp - > m_bsize , 0 , & bp , & xfs_rtbuf_ops ) ;
2013-10-15 02:17:56 +04:00
if ( error )
return error ;
2016-02-09 08:41:31 +03:00
xfs_trans_buf_set_type ( tp , bp , issum ? XFS_BLFT_RTSUMMARY_BUF
: XFS_BLFT_RTBITMAP_BUF ) ;
2013-10-15 02:17:56 +04:00
* 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 */
xfs_buf_t * 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 */
xfs_buf_t * 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 ;
}
/*
2014-09-09 05:58:42 +04:00
* Read and / or modify the summary information for a given extent size ,
2013-10-15 02:17:56 +04:00
* bitmap block combination .
* Keeps track of a current summary block , so we don ' t keep reading
* it from the buffer cache .
2014-09-09 05:58:42 +04:00
*
* Summary information is returned in * sum if specified .
* If no delta is specified , returns summary only .
2013-10-15 02:17:56 +04:00
*/
int
2014-09-09 05:58:42 +04:00
xfs_rtmodify_summary_int (
xfs_mount_t * mp , /* file system mount structure */
2013-10-15 02:17:56 +04:00
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 */
xfs_buf_t * * rbpp , /* in/out: summary block buffer */
2014-09-09 05:58:42 +04:00
xfs_fsblock_t * rsb , /* in/out: summary block number */
xfs_suminfo_t * sum ) /* out: summary info for this block */
2013-10-15 02:17:56 +04:00
{
xfs_buf_t * 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 .
*/
2014-09-09 05:59:12 +04:00
if ( * rbpp & & * rsb = = sb )
2013-10-15 02:17:56 +04:00
bp = * rbpp ;
/*
* Otherwise we have to get the buffer .
*/
else {
/*
* If there was an old one , get rid of it first .
*/
2014-09-09 05:59:12 +04:00
if ( * rbpp )
2013-10-15 02:17:56 +04:00
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 .
*/
2014-09-09 05:59:12 +04:00
* rbpp = bp ;
* rsb = sb ;
2013-10-15 02:17:56 +04:00
}
/*
2014-09-09 05:58:42 +04:00
* Point to the summary information , modify / log it , and / or copy it out .
2013-10-15 02:17:56 +04:00
*/
sp = XFS_SUMPTR ( mp , bp , so ) ;
2014-09-09 05:58:42 +04:00
if ( delta ) {
uint first = ( uint ) ( ( char * ) sp - ( char * ) bp - > b_addr ) ;
* sp + = delta ;
xfs_trans_log_buf ( tp , bp , first , first + sizeof ( * sp ) - 1 ) ;
}
2014-09-09 05:59:12 +04:00
if ( sum )
2014-09-09 05:58:42 +04:00
* sum = * sp ;
2013-10-15 02:17:56 +04:00
return 0 ;
}
2014-09-09 05:58:42 +04:00
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 */
xfs_buf_t * * 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 ) ;
}
2013-10-15 02:17:56 +04:00
/*
* 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 */
xfs_buf_t * 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 */
xfs_buf_t * * 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 */
xfs_buf_t * 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 */
xfs_buf_t * 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_d . di_flags & XFS_DIFLAG_NEWRTBM ) )
mp - > m_rbmip - > i_d . di_flags | = XFS_DIFLAG_NEWRTBM ;
2017-06-16 21:00:05 +03:00
* ( uint64_t * ) & VFS_I ( mp - > m_rbmip ) - > i_atime = 0 ;
2013-10-15 02:17:56 +04:00
xfs_trans_log_inode ( tp , mp - > m_rbmip , XFS_ILOG_CORE ) ;
}
return 0 ;
}
2017-03-29 00:56:36 +03:00
/* 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 ;
xfs_rtblock_t rem ;
int is_free ;
int error = 0 ;
2018-05-31 19:12:10 +03:00
if ( low_rec - > ar_startext > high_rec - > ar_startext )
2017-03-29 00:56:36 +03:00
return - EINVAL ;
2018-05-31 19:12:10 +03:00
if ( low_rec - > ar_startext > = mp - > m_sb . sb_rextents | |
low_rec - > ar_startext = = high_rec - > ar_startext )
2017-03-29 00:56:36 +03:00
return 0 ;
2018-05-31 19:12:10 +03:00
if ( high_rec - > ar_startext > = mp - > m_sb . sb_rextents )
high_rec - > ar_startext = mp - > m_sb . sb_rextents - 1 ;
2017-03-29 00:56:36 +03:00
/* Iterate the bitmap, looking for discrepancies. */
2018-05-31 19:12:10 +03:00
rtstart = low_rec - > ar_startext ;
rem = high_rec - > ar_startext - rtstart ;
2017-03-29 00:56:36 +03:00
while ( rem ) {
/* 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 ,
2018-05-31 19:12:10 +03:00
high_rec - > ar_startext - 1 , & rtend ) ;
2017-03-29 00:56:36 +03:00
if ( error )
break ;
if ( is_free ) {
2018-05-31 19:12:10 +03:00
rec . ar_startext = rtstart ;
rec . ar_extcount = rtend - rtstart + 1 ;
2017-03-29 00:56:36 +03:00
error = fn ( tp , & rec , priv ) ;
if ( error )
break ;
}
rem - = rtend - rtstart + 1 ;
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 ] ;
2018-05-31 19:12:10 +03:00
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 ;
2017-03-29 00:56:36 +03:00
return xfs_rtalloc_query_range ( tp , & keys [ 0 ] , & keys [ 1 ] , fn , priv ) ;
}
2017-10-18 07:37:32 +03:00
2018-01-17 05:53:10 +03:00
/* 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 ;
}