2018-06-06 05:42:14 +03:00
// SPDX-License-Identifier: GPL-2.0+
2017-03-29 00:56:37 +03:00
/*
* Copyright ( C ) 2017 Oracle . All Rights Reserved .
* Author : Darrick J . Wong < darrick . wong @ oracle . com >
*/
# 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_mount.h"
# include "xfs_inode.h"
# include "xfs_trans.h"
# include "xfs_btree.h"
# include "xfs_rmap_btree.h"
# include "xfs_trace.h"
# include "xfs_rmap.h"
# include "xfs_alloc.h"
# include "xfs_bit.h"
# include <linux/fsmap.h>
# include "xfs_fsmap.h"
# include "xfs_refcount.h"
# include "xfs_refcount_btree.h"
2017-03-29 00:56:37 +03:00
# include "xfs_alloc_btree.h"
2017-03-29 00:56:38 +03:00
# include "xfs_rtalloc.h"
2021-06-02 03:48:24 +03:00
# include "xfs_ag.h"
2017-03-29 00:56:37 +03:00
/* Convert an xfs_fsmap to an fsmap. */
2020-10-01 20:56:07 +03:00
static void
2017-03-29 00:56:37 +03:00
xfs_fsmap_from_internal (
struct fsmap * dest ,
struct xfs_fsmap * src )
{
dest - > fmr_device = src - > fmr_device ;
dest - > fmr_flags = src - > fmr_flags ;
dest - > fmr_physical = BBTOB ( src - > fmr_physical ) ;
dest - > fmr_owner = src - > fmr_owner ;
dest - > fmr_offset = BBTOB ( src - > fmr_offset ) ;
dest - > fmr_length = BBTOB ( src - > fmr_length ) ;
dest - > fmr_reserved [ 0 ] = 0 ;
dest - > fmr_reserved [ 1 ] = 0 ;
dest - > fmr_reserved [ 2 ] = 0 ;
}
/* Convert an fsmap to an xfs_fsmap. */
void
xfs_fsmap_to_internal (
struct xfs_fsmap * dest ,
struct fsmap * src )
{
dest - > fmr_device = src - > fmr_device ;
dest - > fmr_flags = src - > fmr_flags ;
dest - > fmr_physical = BTOBBT ( src - > fmr_physical ) ;
dest - > fmr_owner = src - > fmr_owner ;
dest - > fmr_offset = BTOBBT ( src - > fmr_offset ) ;
dest - > fmr_length = BTOBBT ( src - > fmr_length ) ;
}
/* Convert an fsmap owner into an rmapbt owner. */
static int
xfs_fsmap_owner_to_rmap (
struct xfs_rmap_irec * dest ,
2021-08-11 03:00:31 +03:00
const struct xfs_fsmap * src )
2017-03-29 00:56:37 +03:00
{
if ( ! ( src - > fmr_flags & FMR_OF_SPECIAL_OWNER ) ) {
dest - > rm_owner = src - > fmr_owner ;
return 0 ;
}
switch ( src - > fmr_owner ) {
case 0 : /* "lowest owner id possible" */
case - 1ULL : /* "highest owner id possible" */
dest - > rm_owner = 0 ;
break ;
case XFS_FMR_OWN_FREE :
dest - > rm_owner = XFS_RMAP_OWN_NULL ;
break ;
case XFS_FMR_OWN_UNKNOWN :
dest - > rm_owner = XFS_RMAP_OWN_UNKNOWN ;
break ;
case XFS_FMR_OWN_FS :
dest - > rm_owner = XFS_RMAP_OWN_FS ;
break ;
case XFS_FMR_OWN_LOG :
dest - > rm_owner = XFS_RMAP_OWN_LOG ;
break ;
case XFS_FMR_OWN_AG :
dest - > rm_owner = XFS_RMAP_OWN_AG ;
break ;
case XFS_FMR_OWN_INOBT :
dest - > rm_owner = XFS_RMAP_OWN_INOBT ;
break ;
case XFS_FMR_OWN_INODES :
dest - > rm_owner = XFS_RMAP_OWN_INODES ;
break ;
case XFS_FMR_OWN_REFC :
dest - > rm_owner = XFS_RMAP_OWN_REFC ;
break ;
case XFS_FMR_OWN_COW :
dest - > rm_owner = XFS_RMAP_OWN_COW ;
break ;
case XFS_FMR_OWN_DEFECTIVE : /* not implemented */
/* fall through */
default :
return - EINVAL ;
}
return 0 ;
}
/* Convert an rmapbt owner into an fsmap owner. */
static int
xfs_fsmap_owner_from_rmap (
2021-08-11 03:02:16 +03:00
struct xfs_fsmap * dest ,
const struct xfs_rmap_irec * src )
2017-03-29 00:56:37 +03:00
{
dest - > fmr_flags = 0 ;
if ( ! XFS_RMAP_NON_INODE_OWNER ( src - > rm_owner ) ) {
dest - > fmr_owner = src - > rm_owner ;
return 0 ;
}
dest - > fmr_flags | = FMR_OF_SPECIAL_OWNER ;
switch ( src - > rm_owner ) {
case XFS_RMAP_OWN_FS :
dest - > fmr_owner = XFS_FMR_OWN_FS ;
break ;
case XFS_RMAP_OWN_LOG :
dest - > fmr_owner = XFS_FMR_OWN_LOG ;
break ;
case XFS_RMAP_OWN_AG :
dest - > fmr_owner = XFS_FMR_OWN_AG ;
break ;
case XFS_RMAP_OWN_INOBT :
dest - > fmr_owner = XFS_FMR_OWN_INOBT ;
break ;
case XFS_RMAP_OWN_INODES :
dest - > fmr_owner = XFS_FMR_OWN_INODES ;
break ;
case XFS_RMAP_OWN_REFC :
dest - > fmr_owner = XFS_FMR_OWN_REFC ;
break ;
case XFS_RMAP_OWN_COW :
dest - > fmr_owner = XFS_FMR_OWN_COW ;
break ;
2017-03-29 00:56:37 +03:00
case XFS_RMAP_OWN_NULL : /* "free" */
dest - > fmr_owner = XFS_FMR_OWN_FREE ;
break ;
2017-03-29 00:56:37 +03:00
default :
2019-11-02 19:41:18 +03:00
ASSERT ( 0 ) ;
2017-03-29 00:56:37 +03:00
return - EFSCORRUPTED ;
}
return 0 ;
}
/* getfsmap query state */
struct xfs_getfsmap_info {
struct xfs_fsmap_head * head ;
2020-10-01 20:56:07 +03:00
struct fsmap * fsmap_recs ; /* mapping records */
2017-03-29 00:56:37 +03:00
struct xfs_buf * agf_bp ; /* AGF, for refcount queries */
2021-06-02 03:48:24 +03:00
struct xfs_perag * pag ; /* AG info, if applicable */
2017-03-29 00:56:37 +03:00
xfs_daddr_t next_daddr ; /* next daddr we expect */
u64 missing_owner ; /* owner of holes */
u32 dev ; /* device id */
struct xfs_rmap_irec low ; /* low rmap key */
struct xfs_rmap_irec high ; /* high rmap key */
bool last ; /* last extent? */
} ;
/* Associate a device with a getfsmap handler. */
struct xfs_getfsmap_dev {
u32 dev ;
int ( * fn ) ( struct xfs_trans * tp ,
2021-08-11 03:00:31 +03:00
const struct xfs_fsmap * keys ,
2017-03-29 00:56:37 +03:00
struct xfs_getfsmap_info * info ) ;
} ;
/* Compare two getfsmap device handlers. */
static int
xfs_getfsmap_dev_compare (
const void * p1 ,
const void * p2 )
{
const struct xfs_getfsmap_dev * d1 = p1 ;
const struct xfs_getfsmap_dev * d2 = p2 ;
return d1 - > dev - d2 - > dev ;
}
/* Decide if this mapping is shared. */
STATIC int
xfs_getfsmap_is_shared (
struct xfs_trans * tp ,
struct xfs_getfsmap_info * info ,
2021-08-11 03:02:16 +03:00
const struct xfs_rmap_irec * rec ,
2017-03-29 00:56:37 +03:00
bool * stat )
{
struct xfs_mount * mp = tp - > t_mountp ;
struct xfs_btree_cur * cur ;
xfs_agblock_t fbno ;
xfs_extlen_t flen ;
int error ;
* stat = false ;
2021-08-19 04:46:37 +03:00
if ( ! xfs_has_reflink ( mp ) )
2017-03-29 00:56:37 +03:00
return 0 ;
2021-06-02 03:48:24 +03:00
/* rt files will have no perag structure */
if ( ! info - > pag )
2017-03-29 00:56:37 +03:00
return 0 ;
/* Are there any shared blocks here? */
flen = 0 ;
2021-06-02 03:48:24 +03:00
cur = xfs_refcountbt_init_cursor ( mp , tp , info - > agf_bp , info - > pag ) ;
2017-03-29 00:56:37 +03:00
error = xfs_refcount_find_shared ( cur , rec - > rm_startblock ,
rec - > rm_blockcount , & fbno , & flen , false ) ;
2018-07-19 22:26:31 +03:00
xfs_btree_del_cursor ( cur , error ) ;
2017-03-29 00:56:37 +03:00
if ( error )
return error ;
* stat = flen > 0 ;
return 0 ;
}
2020-10-01 20:56:07 +03:00
static inline void
xfs_getfsmap_format (
struct xfs_mount * mp ,
struct xfs_fsmap * xfm ,
struct xfs_getfsmap_info * info )
{
struct fsmap * rec ;
trace_xfs_getfsmap_mapping ( mp , xfm ) ;
rec = & info - > fsmap_recs [ info - > head - > fmh_entries + + ] ;
xfs_fsmap_from_internal ( rec , xfm ) ;
}
2017-03-29 00:56:37 +03:00
/*
* Format a reverse mapping for getfsmap , having translated rm_startblock
* into the appropriate daddr units .
*/
STATIC int
xfs_getfsmap_helper (
struct xfs_trans * tp ,
struct xfs_getfsmap_info * info ,
2021-08-11 03:02:16 +03:00
const struct xfs_rmap_irec * rec ,
2017-03-29 00:56:37 +03:00
xfs_daddr_t rec_daddr )
{
struct xfs_fsmap fmr ;
struct xfs_mount * mp = tp - > t_mountp ;
bool shared ;
int error ;
if ( fatal_signal_pending ( current ) )
return - EINTR ;
/*
* Filter out records that start before our startpoint , if the
* caller requested that .
*/
if ( xfs_rmap_compare ( rec , & info - > low ) < 0 ) {
rec_daddr + = XFS_FSB_TO_BB ( mp , rec - > rm_blockcount ) ;
if ( info - > next_daddr < rec_daddr )
info - > next_daddr = rec_daddr ;
2019-08-29 00:39:46 +03:00
return 0 ;
2017-03-29 00:56:37 +03:00
}
/* Are we just counting mappings? */
if ( info - > head - > fmh_count = = 0 ) {
2020-10-01 20:56:07 +03:00
if ( info - > head - > fmh_entries = = UINT_MAX )
return - ECANCELED ;
2017-03-29 00:56:37 +03:00
if ( rec_daddr > info - > next_daddr )
info - > head - > fmh_entries + + ;
if ( info - > last )
2019-08-29 00:39:46 +03:00
return 0 ;
2017-03-29 00:56:37 +03:00
info - > head - > fmh_entries + + ;
rec_daddr + = XFS_FSB_TO_BB ( mp , rec - > rm_blockcount ) ;
if ( info - > next_daddr < rec_daddr )
info - > next_daddr = rec_daddr ;
2019-08-29 00:39:46 +03:00
return 0 ;
2017-03-29 00:56:37 +03:00
}
/*
* If the record starts past the last physical block we saw ,
* then we ' ve found a gap . Report the gap as being owned by
* whatever the caller specified is the missing owner .
*/
if ( rec_daddr > info - > next_daddr ) {
if ( info - > head - > fmh_entries > = info - > head - > fmh_count )
2019-08-29 00:37:57 +03:00
return - ECANCELED ;
2017-03-29 00:56:37 +03:00
fmr . fmr_device = info - > dev ;
fmr . fmr_physical = info - > next_daddr ;
fmr . fmr_owner = info - > missing_owner ;
fmr . fmr_offset = 0 ;
fmr . fmr_length = rec_daddr - info - > next_daddr ;
fmr . fmr_flags = FMR_OF_SPECIAL_OWNER ;
2020-10-01 20:56:07 +03:00
xfs_getfsmap_format ( mp , & fmr , info ) ;
2017-03-29 00:56:37 +03:00
}
if ( info - > last )
goto out ;
/* Fill out the extent we found */
if ( info - > head - > fmh_entries > = info - > head - > fmh_count )
2019-08-29 00:37:57 +03:00
return - ECANCELED ;
2017-03-29 00:56:37 +03:00
2021-06-02 03:48:24 +03:00
trace_xfs_fsmap_mapping ( mp , info - > dev ,
info - > pag ? info - > pag - > pag_agno : NULLAGNUMBER , rec ) ;
2017-03-29 00:56:37 +03:00
fmr . fmr_device = info - > dev ;
fmr . fmr_physical = rec_daddr ;
error = xfs_fsmap_owner_from_rmap ( & fmr , rec ) ;
if ( error )
return error ;
fmr . fmr_offset = XFS_FSB_TO_BB ( mp , rec - > rm_offset ) ;
fmr . fmr_length = XFS_FSB_TO_BB ( mp , rec - > rm_blockcount ) ;
if ( rec - > rm_flags & XFS_RMAP_UNWRITTEN )
fmr . fmr_flags | = FMR_OF_PREALLOC ;
if ( rec - > rm_flags & XFS_RMAP_ATTR_FORK )
fmr . fmr_flags | = FMR_OF_ATTR_FORK ;
if ( rec - > rm_flags & XFS_RMAP_BMBT_BLOCK )
fmr . fmr_flags | = FMR_OF_EXTENT_MAP ;
if ( fmr . fmr_flags = = 0 ) {
error = xfs_getfsmap_is_shared ( tp , info , rec , & shared ) ;
if ( error )
return error ;
if ( shared )
fmr . fmr_flags | = FMR_OF_SHARED ;
}
2020-10-01 20:56:07 +03:00
xfs_getfsmap_format ( mp , & fmr , info ) ;
2017-03-29 00:56:37 +03:00
out :
rec_daddr + = XFS_FSB_TO_BB ( mp , rec - > rm_blockcount ) ;
if ( info - > next_daddr < rec_daddr )
info - > next_daddr = rec_daddr ;
2019-08-29 00:39:46 +03:00
return 0 ;
2017-03-29 00:56:37 +03:00
}
/* Transform a rmapbt irec into a fsmap */
STATIC int
xfs_getfsmap_datadev_helper (
struct xfs_btree_cur * cur ,
2021-08-11 03:02:16 +03:00
const struct xfs_rmap_irec * rec ,
2017-03-29 00:56:37 +03:00
void * priv )
{
struct xfs_mount * mp = cur - > bc_mp ;
struct xfs_getfsmap_info * info = priv ;
xfs_fsblock_t fsb ;
xfs_daddr_t rec_daddr ;
2021-06-02 03:48:24 +03:00
fsb = XFS_AGB_TO_FSB ( mp , cur - > bc_ag . pag - > pag_agno , rec - > rm_startblock ) ;
2017-03-29 00:56:37 +03:00
rec_daddr = XFS_FSB_TO_DADDR ( mp , fsb ) ;
return xfs_getfsmap_helper ( cur - > bc_tp , info , rec , rec_daddr ) ;
}
2017-03-29 00:56:37 +03:00
/* Transform a bnobt irec into a fsmap */
STATIC int
xfs_getfsmap_datadev_bnobt_helper (
struct xfs_btree_cur * cur ,
2021-08-11 03:02:16 +03:00
const struct xfs_alloc_rec_incore * rec ,
2017-03-29 00:56:37 +03:00
void * priv )
{
struct xfs_mount * mp = cur - > bc_mp ;
struct xfs_getfsmap_info * info = priv ;
struct xfs_rmap_irec irec ;
xfs_daddr_t rec_daddr ;
2021-06-02 03:48:24 +03:00
rec_daddr = XFS_AGB_TO_DADDR ( mp , cur - > bc_ag . pag - > pag_agno ,
2017-03-29 00:56:37 +03:00
rec - > ar_startblock ) ;
irec . rm_startblock = rec - > ar_startblock ;
irec . rm_blockcount = rec - > ar_blockcount ;
irec . rm_owner = XFS_RMAP_OWN_NULL ; /* "free" */
irec . rm_offset = 0 ;
irec . rm_flags = 0 ;
return xfs_getfsmap_helper ( cur - > bc_tp , info , & irec , rec_daddr ) ;
}
2017-03-29 00:56:37 +03:00
/* Set rmap flags based on the getfsmap flags */
static void
xfs_getfsmap_set_irec_flags (
struct xfs_rmap_irec * irec ,
2021-08-11 03:00:31 +03:00
const struct xfs_fsmap * fmr )
2017-03-29 00:56:37 +03:00
{
irec - > rm_flags = 0 ;
if ( fmr - > fmr_flags & FMR_OF_ATTR_FORK )
irec - > rm_flags | = XFS_RMAP_ATTR_FORK ;
if ( fmr - > fmr_flags & FMR_OF_EXTENT_MAP )
irec - > rm_flags | = XFS_RMAP_BMBT_BLOCK ;
if ( fmr - > fmr_flags & FMR_OF_PREALLOC )
irec - > rm_flags | = XFS_RMAP_UNWRITTEN ;
}
/* Execute a getfsmap query against the log device. */
STATIC int
xfs_getfsmap_logdev (
struct xfs_trans * tp ,
2021-08-11 03:00:31 +03:00
const struct xfs_fsmap * keys ,
2017-03-29 00:56:37 +03:00
struct xfs_getfsmap_info * info )
{
struct xfs_mount * mp = tp - > t_mountp ;
struct xfs_rmap_irec rmap ;
int error ;
/* Set up search keys */
info - > low . rm_startblock = XFS_BB_TO_FSBT ( mp , keys [ 0 ] . fmr_physical ) ;
info - > low . rm_offset = XFS_BB_TO_FSBT ( mp , keys [ 0 ] . fmr_offset ) ;
error = xfs_fsmap_owner_to_rmap ( & info - > low , keys ) ;
if ( error )
return error ;
info - > low . rm_blockcount = 0 ;
xfs_getfsmap_set_irec_flags ( & info - > low , & keys [ 0 ] ) ;
error = xfs_fsmap_owner_to_rmap ( & info - > high , keys + 1 ) ;
if ( error )
return error ;
info - > high . rm_startblock = - 1U ;
info - > high . rm_owner = ULLONG_MAX ;
info - > high . rm_offset = ULLONG_MAX ;
info - > high . rm_blockcount = 0 ;
info - > high . rm_flags = XFS_RMAP_KEY_FLAGS | XFS_RMAP_REC_FLAGS ;
info - > missing_owner = XFS_FMR_OWN_FREE ;
2021-06-02 03:48:24 +03:00
trace_xfs_fsmap_low_key ( mp , info - > dev , NULLAGNUMBER , & info - > low ) ;
trace_xfs_fsmap_high_key ( mp , info - > dev , NULLAGNUMBER , & info - > high ) ;
2017-03-29 00:56:37 +03:00
if ( keys [ 0 ] . fmr_physical > 0 )
return 0 ;
/* Fabricate an rmap entry for the external log device. */
rmap . rm_startblock = 0 ;
rmap . rm_blockcount = mp - > m_sb . sb_logblocks ;
rmap . rm_owner = XFS_RMAP_OWN_LOG ;
rmap . rm_offset = 0 ;
rmap . rm_flags = 0 ;
return xfs_getfsmap_helper ( tp , info , & rmap , 0 ) ;
}
2017-10-13 19:47:47 +03:00
# ifdef CONFIG_XFS_RT
/* Transform a rtbitmap "record" into a fsmap */
STATIC int
xfs_getfsmap_rtdev_rtbitmap_helper (
struct xfs_trans * tp ,
2021-08-11 03:02:16 +03:00
const struct xfs_rtalloc_rec * rec ,
2017-10-13 19:47:47 +03:00
void * priv )
{
struct xfs_mount * mp = tp - > t_mountp ;
struct xfs_getfsmap_info * info = priv ;
struct xfs_rmap_irec irec ;
xfs_daddr_t rec_daddr ;
2018-05-31 19:12:10 +03:00
irec . rm_startblock = rec - > ar_startext * mp - > m_sb . sb_rextsize ;
rec_daddr = XFS_FSB_TO_BB ( mp , irec . rm_startblock ) ;
irec . rm_blockcount = rec - > ar_extcount * mp - > m_sb . sb_rextsize ;
2017-10-13 19:47:47 +03:00
irec . rm_owner = XFS_RMAP_OWN_NULL ; /* "free" */
irec . rm_offset = 0 ;
irec . rm_flags = 0 ;
return xfs_getfsmap_helper ( tp , info , & irec , rec_daddr ) ;
}
2017-03-29 00:56:38 +03:00
/* Execute a getfsmap query against the realtime device. */
STATIC int
__xfs_getfsmap_rtdev (
struct xfs_trans * tp ,
2021-08-11 03:00:31 +03:00
const struct xfs_fsmap * keys ,
2017-03-29 00:56:38 +03:00
int ( * query_fn ) ( struct xfs_trans * ,
struct xfs_getfsmap_info * ) ,
struct xfs_getfsmap_info * info )
{
struct xfs_mount * mp = tp - > t_mountp ;
xfs_fsblock_t start_fsb ;
xfs_fsblock_t end_fsb ;
2021-08-11 03:00:31 +03:00
uint64_t eofs ;
2017-03-29 00:56:38 +03:00
int error = 0 ;
eofs = XFS_FSB_TO_BB ( mp , mp - > m_sb . sb_rblocks ) ;
if ( keys [ 0 ] . fmr_physical > = eofs )
return 0 ;
start_fsb = XFS_BB_TO_FSBT ( mp , keys [ 0 ] . fmr_physical ) ;
2021-08-11 03:00:31 +03:00
end_fsb = XFS_BB_TO_FSB ( mp , min ( eofs - 1 , keys [ 1 ] . fmr_physical ) ) ;
2017-03-29 00:56:38 +03:00
/* Set up search keys */
info - > low . rm_startblock = start_fsb ;
error = xfs_fsmap_owner_to_rmap ( & info - > low , & keys [ 0 ] ) ;
if ( error )
return error ;
info - > low . rm_offset = XFS_BB_TO_FSBT ( mp , keys [ 0 ] . fmr_offset ) ;
info - > low . rm_blockcount = 0 ;
xfs_getfsmap_set_irec_flags ( & info - > low , & keys [ 0 ] ) ;
info - > high . rm_startblock = end_fsb ;
error = xfs_fsmap_owner_to_rmap ( & info - > high , & keys [ 1 ] ) ;
if ( error )
return error ;
info - > high . rm_offset = XFS_BB_TO_FSBT ( mp , keys [ 1 ] . fmr_offset ) ;
info - > high . rm_blockcount = 0 ;
xfs_getfsmap_set_irec_flags ( & info - > high , & keys [ 1 ] ) ;
2021-06-02 03:48:24 +03:00
trace_xfs_fsmap_low_key ( mp , info - > dev , NULLAGNUMBER , & info - > low ) ;
trace_xfs_fsmap_high_key ( mp , info - > dev , NULLAGNUMBER , & info - > high ) ;
2017-03-29 00:56:38 +03:00
return query_fn ( tp , info ) ;
}
/* Actually query the realtime bitmap. */
STATIC int
xfs_getfsmap_rtdev_rtbitmap_query (
struct xfs_trans * tp ,
struct xfs_getfsmap_info * info )
{
2018-06-22 09:26:57 +03:00
struct xfs_rtalloc_rec alow = { 0 } ;
struct xfs_rtalloc_rec ahigh = { 0 } ;
2021-08-11 03:00:31 +03:00
struct xfs_mount * mp = tp - > t_mountp ;
2017-03-29 00:56:38 +03:00
int error ;
2021-08-11 03:00:31 +03:00
xfs_ilock ( mp - > m_rbmip , XFS_ILOCK_SHARED ) ;
2017-03-29 00:56:38 +03:00
2021-08-11 03:00:31 +03:00
/*
* Set up query parameters to return free rtextents covering the range
* we want .
*/
2018-05-31 19:12:10 +03:00
alow . ar_startext = info - > low . rm_startblock ;
ahigh . ar_startext = info - > high . rm_startblock ;
2021-08-11 03:00:31 +03:00
do_div ( alow . ar_startext , mp - > m_sb . sb_rextsize ) ;
if ( do_div ( ahigh . ar_startext , mp - > m_sb . sb_rextsize ) )
2018-05-31 19:12:10 +03:00
ahigh . ar_startext + + ;
2017-03-29 00:56:38 +03:00
error = xfs_rtalloc_query_range ( tp , & alow , & ahigh ,
xfs_getfsmap_rtdev_rtbitmap_helper , info ) ;
if ( error )
goto err ;
2021-08-11 03:00:31 +03:00
/*
* Report any gaps at the end of the rtbitmap by simulating a null
* rmap starting at the block after the end of the query range .
*/
2017-03-29 00:56:38 +03:00
info - > last = true ;
2021-08-11 03:00:31 +03:00
ahigh . ar_startext = min ( mp - > m_sb . sb_rextents , ahigh . ar_startext ) ;
2017-03-29 00:56:38 +03:00
error = xfs_getfsmap_rtdev_rtbitmap_helper ( tp , & ahigh , info ) ;
if ( error )
goto err ;
err :
2021-08-11 03:00:31 +03:00
xfs_iunlock ( mp - > m_rbmip , XFS_ILOCK_SHARED ) ;
2017-03-29 00:56:38 +03:00
return error ;
}
/* Execute a getfsmap query against the realtime device rtbitmap. */
STATIC int
xfs_getfsmap_rtdev_rtbitmap (
struct xfs_trans * tp ,
2021-08-11 03:00:31 +03:00
const struct xfs_fsmap * keys ,
2017-03-29 00:56:38 +03:00
struct xfs_getfsmap_info * info )
{
info - > missing_owner = XFS_FMR_OWN_UNKNOWN ;
return __xfs_getfsmap_rtdev ( tp , keys , xfs_getfsmap_rtdev_rtbitmap_query ,
info ) ;
}
2017-10-09 21:37:22 +03:00
# endif /* CONFIG_XFS_RT */
2017-03-29 00:56:38 +03:00
2017-03-29 00:56:37 +03:00
/* Execute a getfsmap query against the regular data device. */
STATIC int
__xfs_getfsmap_datadev (
struct xfs_trans * tp ,
2021-08-11 03:00:31 +03:00
const struct xfs_fsmap * keys ,
2017-03-29 00:56:37 +03:00
struct xfs_getfsmap_info * info ,
int ( * query_fn ) ( struct xfs_trans * ,
struct xfs_getfsmap_info * ,
struct xfs_btree_cur * * ,
void * ) ,
void * priv )
{
struct xfs_mount * mp = tp - > t_mountp ;
2021-06-02 03:48:24 +03:00
struct xfs_perag * pag ;
2017-03-29 00:56:37 +03:00
struct xfs_btree_cur * bt_cur = NULL ;
xfs_fsblock_t start_fsb ;
xfs_fsblock_t end_fsb ;
xfs_agnumber_t start_ag ;
xfs_agnumber_t end_ag ;
2021-08-11 03:00:31 +03:00
uint64_t eofs ;
2017-03-29 00:56:37 +03:00
int error = 0 ;
eofs = XFS_FSB_TO_BB ( mp , mp - > m_sb . sb_dblocks ) ;
if ( keys [ 0 ] . fmr_physical > = eofs )
return 0 ;
start_fsb = XFS_DADDR_TO_FSB ( mp , keys [ 0 ] . fmr_physical ) ;
2021-08-11 03:00:31 +03:00
end_fsb = XFS_DADDR_TO_FSB ( mp , min ( eofs - 1 , keys [ 1 ] . fmr_physical ) ) ;
2017-03-29 00:56:37 +03:00
/*
* Convert the fsmap low / high keys to AG based keys . Initialize
* low to the fsmap low key and max out the high key to the end
* of the AG .
*/
info - > low . rm_startblock = XFS_FSB_TO_AGBNO ( mp , start_fsb ) ;
info - > low . rm_offset = XFS_BB_TO_FSBT ( mp , keys [ 0 ] . fmr_offset ) ;
error = xfs_fsmap_owner_to_rmap ( & info - > low , & keys [ 0 ] ) ;
if ( error )
return error ;
info - > low . rm_blockcount = 0 ;
xfs_getfsmap_set_irec_flags ( & info - > low , & keys [ 0 ] ) ;
info - > high . rm_startblock = - 1U ;
info - > high . rm_owner = ULLONG_MAX ;
info - > high . rm_offset = ULLONG_MAX ;
info - > high . rm_blockcount = 0 ;
info - > high . rm_flags = XFS_RMAP_KEY_FLAGS | XFS_RMAP_REC_FLAGS ;
start_ag = XFS_FSB_TO_AGNO ( mp , start_fsb ) ;
end_ag = XFS_FSB_TO_AGNO ( mp , end_fsb ) ;
2021-06-02 03:48:24 +03:00
for_each_perag_range ( mp , start_ag , end_ag , pag ) {
2017-03-29 00:56:37 +03:00
/*
* Set the AG high key from the fsmap high key if this
* is the last AG that we ' re querying .
*/
2021-06-02 03:48:24 +03:00
info - > pag = pag ;
if ( pag - > pag_agno = = end_ag ) {
2017-03-29 00:56:37 +03:00
info - > high . rm_startblock = XFS_FSB_TO_AGBNO ( mp ,
end_fsb ) ;
info - > high . rm_offset = XFS_BB_TO_FSBT ( mp ,
keys [ 1 ] . fmr_offset ) ;
error = xfs_fsmap_owner_to_rmap ( & info - > high , & keys [ 1 ] ) ;
if ( error )
2021-06-02 03:48:24 +03:00
break ;
2017-03-29 00:56:37 +03:00
xfs_getfsmap_set_irec_flags ( & info - > high , & keys [ 1 ] ) ;
}
if ( bt_cur ) {
xfs_btree_del_cursor ( bt_cur , XFS_BTREE_NOERROR ) ;
bt_cur = NULL ;
xfs_trans_brelse ( tp , info - > agf_bp ) ;
info - > agf_bp = NULL ;
}
2021-06-02 03:48:24 +03:00
error = xfs_alloc_read_agf ( mp , tp , pag - > pag_agno , 0 ,
2017-03-29 00:56:37 +03:00
& info - > agf_bp ) ;
if ( error )
2021-06-02 03:48:24 +03:00
break ;
2017-03-29 00:56:37 +03:00
2021-06-02 03:48:24 +03:00
trace_xfs_fsmap_low_key ( mp , info - > dev , pag - > pag_agno ,
& info - > low ) ;
trace_xfs_fsmap_high_key ( mp , info - > dev , pag - > pag_agno ,
2017-03-29 00:56:37 +03:00
& info - > high ) ;
error = query_fn ( tp , info , & bt_cur , priv ) ;
if ( error )
2021-06-02 03:48:24 +03:00
break ;
2017-03-29 00:56:37 +03:00
/*
* Set the AG low key to the start of the AG prior to
* moving on to the next AG .
*/
2021-06-02 03:48:24 +03:00
if ( pag - > pag_agno = = start_ag ) {
2017-03-29 00:56:37 +03:00
info - > low . rm_startblock = 0 ;
info - > low . rm_owner = 0 ;
info - > low . rm_offset = 0 ;
info - > low . rm_flags = 0 ;
}
2021-06-02 03:48:24 +03:00
/*
* If this is the last AG , report any gap at the end of it
* before we drop the reference to the perag when the loop
* terminates .
*/
if ( pag - > pag_agno = = end_ag ) {
info - > last = true ;
error = query_fn ( tp , info , & bt_cur , priv ) ;
if ( error )
break ;
}
info - > pag = NULL ;
}
2017-03-29 00:56:37 +03:00
if ( bt_cur )
xfs_btree_del_cursor ( bt_cur , error < 0 ? XFS_BTREE_ERROR :
XFS_BTREE_NOERROR ) ;
if ( info - > agf_bp ) {
xfs_trans_brelse ( tp , info - > agf_bp ) ;
info - > agf_bp = NULL ;
}
2021-06-02 03:48:24 +03:00
if ( info - > pag ) {
xfs_perag_put ( info - > pag ) ;
info - > pag = NULL ;
} else if ( pag ) {
/* loop termination case */
xfs_perag_put ( pag ) ;
}
2017-03-29 00:56:37 +03:00
return error ;
}
/* Actually query the rmap btree. */
STATIC int
xfs_getfsmap_datadev_rmapbt_query (
struct xfs_trans * tp ,
struct xfs_getfsmap_info * info ,
struct xfs_btree_cur * * curpp ,
void * priv )
{
/* Report any gap at the end of the last AG. */
if ( info - > last )
return xfs_getfsmap_datadev_helper ( * curpp , & info - > high , info ) ;
/* Allocate cursor for this AG and query_range it. */
* curpp = xfs_rmapbt_init_cursor ( tp - > t_mountp , tp , info - > agf_bp ,
2021-06-02 03:48:24 +03:00
info - > pag ) ;
2017-03-29 00:56:37 +03:00
return xfs_rmap_query_range ( * curpp , & info - > low , & info - > high ,
xfs_getfsmap_datadev_helper , info ) ;
}
/* Execute a getfsmap query against the regular data device rmapbt. */
STATIC int
xfs_getfsmap_datadev_rmapbt (
struct xfs_trans * tp ,
2021-08-11 03:00:31 +03:00
const struct xfs_fsmap * keys ,
2017-03-29 00:56:37 +03:00
struct xfs_getfsmap_info * info )
{
info - > missing_owner = XFS_FMR_OWN_FREE ;
return __xfs_getfsmap_datadev ( tp , keys , info ,
xfs_getfsmap_datadev_rmapbt_query , NULL ) ;
}
2017-03-29 00:56:37 +03:00
/* Actually query the bno btree. */
STATIC int
xfs_getfsmap_datadev_bnobt_query (
struct xfs_trans * tp ,
struct xfs_getfsmap_info * info ,
struct xfs_btree_cur * * curpp ,
void * priv )
{
struct xfs_alloc_rec_incore * key = priv ;
/* Report any gap at the end of the last AG. */
if ( info - > last )
return xfs_getfsmap_datadev_bnobt_helper ( * curpp , & key [ 1 ] , info ) ;
/* Allocate cursor for this AG and query_range it. */
* curpp = xfs_allocbt_init_cursor ( tp - > t_mountp , tp , info - > agf_bp ,
2021-06-02 03:48:24 +03:00
info - > pag , XFS_BTNUM_BNO ) ;
2017-03-29 00:56:37 +03:00
key - > ar_startblock = info - > low . rm_startblock ;
key [ 1 ] . ar_startblock = info - > high . rm_startblock ;
return xfs_alloc_query_range ( * curpp , key , & key [ 1 ] ,
xfs_getfsmap_datadev_bnobt_helper , info ) ;
}
/* Execute a getfsmap query against the regular data device's bnobt. */
STATIC int
xfs_getfsmap_datadev_bnobt (
struct xfs_trans * tp ,
2021-08-11 03:00:31 +03:00
const struct xfs_fsmap * keys ,
2017-03-29 00:56:37 +03:00
struct xfs_getfsmap_info * info )
{
struct xfs_alloc_rec_incore akeys [ 2 ] ;
info - > missing_owner = XFS_FMR_OWN_UNKNOWN ;
return __xfs_getfsmap_datadev ( tp , keys , info ,
xfs_getfsmap_datadev_bnobt_query , & akeys [ 0 ] ) ;
}
2017-03-29 00:56:37 +03:00
/* Do we recognize the device? */
STATIC bool
xfs_getfsmap_is_valid_device (
struct xfs_mount * mp ,
struct xfs_fsmap * fm )
{
if ( fm - > fmr_device = = 0 | | fm - > fmr_device = = UINT_MAX | |
fm - > fmr_device = = new_encode_dev ( mp - > m_ddev_targp - > bt_dev ) )
return true ;
if ( mp - > m_logdev_targp & &
fm - > fmr_device = = new_encode_dev ( mp - > m_logdev_targp - > bt_dev ) )
return true ;
2017-03-29 00:56:38 +03:00
if ( mp - > m_rtdev_targp & &
fm - > fmr_device = = new_encode_dev ( mp - > m_rtdev_targp - > bt_dev ) )
return true ;
2017-03-29 00:56:37 +03:00
return false ;
}
/* Ensure that the low key is less than the high key. */
STATIC bool
xfs_getfsmap_check_keys (
struct xfs_fsmap * low_key ,
struct xfs_fsmap * high_key )
{
if ( low_key - > fmr_device > high_key - > fmr_device )
return false ;
if ( low_key - > fmr_device < high_key - > fmr_device )
return true ;
if ( low_key - > fmr_physical > high_key - > fmr_physical )
return false ;
if ( low_key - > fmr_physical < high_key - > fmr_physical )
return true ;
if ( low_key - > fmr_owner > high_key - > fmr_owner )
return false ;
if ( low_key - > fmr_owner < high_key - > fmr_owner )
return true ;
if ( low_key - > fmr_offset > high_key - > fmr_offset )
return false ;
if ( low_key - > fmr_offset < high_key - > fmr_offset )
return true ;
return false ;
}
2017-10-09 21:37:22 +03:00
/*
* There are only two devices if we didn ' t configure RT devices at build time .
*/
# ifdef CONFIG_XFS_RT
2017-03-29 00:56:38 +03:00
# define XFS_GETFSMAP_DEVS 3
2017-10-09 21:37:22 +03:00
# else
# define XFS_GETFSMAP_DEVS 2
# endif /* CONFIG_XFS_RT */
2017-03-29 00:56:37 +03:00
/*
2020-10-01 20:56:07 +03:00
* Get filesystem ' s extents as described in head , and format for output . Fills
* in the supplied records array until there are no more reverse mappings to
* return or head . fmh_entries = = head . fmh_count . In the second case , this
* function returns - ECANCELED to indicate that more records would have been
* returned .
2017-03-29 00:56:37 +03:00
*
* Key to Confusion
* - - - - - - - - - - - - - - - -
* There are multiple levels of keys and counters at work here :
* xfs_fsmap_head . fmh_keys - - low and high fsmap keys passed in ;
* these reflect fs - wide sector addrs .
* dkeys - - fmh_keys used to query each device ;
* these are fmh_keys but w / the low key
* bumped up by fmr_length .
* xfs_getfsmap_info . next_daddr - - next disk addr we expect to see ; this
* is how we detect gaps in the fsmap
records and report them .
* xfs_getfsmap_info . low / high - - per - AG low / high keys computed from
* dkeys ; used to query the metadata .
*/
int
xfs_getfsmap (
struct xfs_mount * mp ,
struct xfs_fsmap_head * head ,
2020-10-01 20:56:07 +03:00
struct fsmap * fsmap_recs )
2017-03-29 00:56:37 +03:00
{
struct xfs_trans * tp = NULL ;
struct xfs_fsmap dkeys [ 2 ] ; /* per-dev keys */
struct xfs_getfsmap_dev handlers [ XFS_GETFSMAP_DEVS ] ;
2017-04-21 21:24:39 +03:00
struct xfs_getfsmap_info info = { NULL } ;
2017-05-12 20:44:10 +03:00
bool use_rmap ;
2017-03-29 00:56:37 +03:00
int i ;
int error = 0 ;
if ( head - > fmh_iflags & ~ FMH_IF_VALID )
return - EINVAL ;
if ( ! xfs_getfsmap_is_valid_device ( mp , & head - > fmh_keys [ 0 ] ) | |
! xfs_getfsmap_is_valid_device ( mp , & head - > fmh_keys [ 1 ] ) )
return - EINVAL ;
2017-05-12 20:44:10 +03:00
use_rmap = capable ( CAP_SYS_ADMIN ) & &
2021-08-19 04:46:37 +03:00
xfs_has_rmapbt ( mp ) ;
2017-03-29 00:56:37 +03:00
head - > fmh_entries = 0 ;
/* Set up our device handlers. */
memset ( handlers , 0 , sizeof ( handlers ) ) ;
handlers [ 0 ] . dev = new_encode_dev ( mp - > m_ddev_targp - > bt_dev ) ;
2017-05-12 20:44:10 +03:00
if ( use_rmap )
2017-03-29 00:56:37 +03:00
handlers [ 0 ] . fn = xfs_getfsmap_datadev_rmapbt ;
else
handlers [ 0 ] . fn = xfs_getfsmap_datadev_bnobt ;
2017-03-29 00:56:37 +03:00
if ( mp - > m_logdev_targp ! = mp - > m_ddev_targp ) {
handlers [ 1 ] . dev = new_encode_dev ( mp - > m_logdev_targp - > bt_dev ) ;
handlers [ 1 ] . fn = xfs_getfsmap_logdev ;
}
2017-10-09 21:37:22 +03:00
# ifdef CONFIG_XFS_RT
2017-03-29 00:56:38 +03:00
if ( mp - > m_rtdev_targp ) {
handlers [ 2 ] . dev = new_encode_dev ( mp - > m_rtdev_targp - > bt_dev ) ;
handlers [ 2 ] . fn = xfs_getfsmap_rtdev_rtbitmap ;
}
2017-10-09 21:37:22 +03:00
# endif /* CONFIG_XFS_RT */
2017-03-29 00:56:37 +03:00
xfs_sort ( handlers , XFS_GETFSMAP_DEVS , sizeof ( struct xfs_getfsmap_dev ) ,
xfs_getfsmap_dev_compare ) ;
/*
* To continue where we left off , we allow userspace to use the
* last mapping from a previous call as the low key of the next .
* This is identified by a non - zero length in the low key . We
* have to increment the low key in this scenario to ensure we
* don ' t return the same mapping again , and instead return the
* very next mapping .
*
* If the low key mapping refers to file data , the same physical
* blocks could be mapped to several other files / offsets .
* According to rmapbt record ordering , the minimal next
* possible record for the block range is the next starting
* offset in the same inode . Therefore , bump the file offset to
* continue the search appropriately . For all other low key
* mapping types ( attr blocks , metadata ) , bump the physical
* offset as there can be no other mapping for the same physical
* block range .
*/
dkeys [ 0 ] = head - > fmh_keys [ 0 ] ;
if ( dkeys [ 0 ] . fmr_flags & ( FMR_OF_SPECIAL_OWNER | FMR_OF_EXTENT_MAP ) ) {
dkeys [ 0 ] . fmr_physical + = dkeys [ 0 ] . fmr_length ;
dkeys [ 0 ] . fmr_owner = 0 ;
if ( dkeys [ 0 ] . fmr_offset )
return - EINVAL ;
} else
dkeys [ 0 ] . fmr_offset + = dkeys [ 0 ] . fmr_length ;
dkeys [ 0 ] . fmr_length = 0 ;
memset ( & dkeys [ 1 ] , 0xFF , sizeof ( struct xfs_fsmap ) ) ;
if ( ! xfs_getfsmap_check_keys ( dkeys , & head - > fmh_keys [ 1 ] ) )
return - EINVAL ;
info . next_daddr = head - > fmh_keys [ 0 ] . fmr_physical +
head - > fmh_keys [ 0 ] . fmr_length ;
2020-10-01 20:56:07 +03:00
info . fsmap_recs = fsmap_recs ;
2017-03-29 00:56:37 +03:00
info . head = head ;
/* For each device we support... */
for ( i = 0 ; i < XFS_GETFSMAP_DEVS ; i + + ) {
/* Is this device within the range the user asked for? */
if ( ! handlers [ i ] . fn )
continue ;
if ( head - > fmh_keys [ 0 ] . fmr_device > handlers [ i ] . dev )
continue ;
if ( head - > fmh_keys [ 1 ] . fmr_device < handlers [ i ] . dev )
break ;
/*
* If this device number matches the high key , we have
* to pass the high key to the handler to limit the
* query results . If the device number exceeds the
* low key , zero out the low key so that we get
* everything from the beginning .
*/
if ( handlers [ i ] . dev = = head - > fmh_keys [ 1 ] . fmr_device )
dkeys [ 1 ] = head - > fmh_keys [ 1 ] ;
if ( handlers [ i ] . dev > head - > fmh_keys [ 0 ] . fmr_device )
memset ( & dkeys [ 0 ] , 0 , sizeof ( struct xfs_fsmap ) ) ;
2021-03-22 19:51:50 +03:00
/*
* Grab an empty transaction so that we can use its recursive
* buffer locking abilities to detect cycles in the rmapbt
* without deadlocking .
*/
2017-03-29 00:56:37 +03:00
error = xfs_trans_alloc_empty ( mp , & tp ) ;
if ( error )
break ;
info . dev = handlers [ i ] . dev ;
info . last = false ;
2021-06-02 03:48:24 +03:00
info . pag = NULL ;
2017-03-29 00:56:37 +03:00
error = handlers [ i ] . fn ( tp , dkeys , & info ) ;
if ( error )
break ;
xfs_trans_cancel ( tp ) ;
tp = NULL ;
info . next_daddr = 0 ;
}
if ( tp )
xfs_trans_cancel ( tp ) ;
head - > fmh_oflags = FMH_OF_DEV_T ;
return error ;
}