162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Oracle. All Rights Reserved. 462306a36Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_log_format.h" 1162306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1262306a36Sopenharmony_ci#include "xfs_mount.h" 1362306a36Sopenharmony_ci#include "xfs_inode.h" 1462306a36Sopenharmony_ci#include "xfs_trans.h" 1562306a36Sopenharmony_ci#include "xfs_btree.h" 1662306a36Sopenharmony_ci#include "xfs_rmap_btree.h" 1762306a36Sopenharmony_ci#include "xfs_trace.h" 1862306a36Sopenharmony_ci#include "xfs_rmap.h" 1962306a36Sopenharmony_ci#include "xfs_alloc.h" 2062306a36Sopenharmony_ci#include "xfs_bit.h" 2162306a36Sopenharmony_ci#include <linux/fsmap.h> 2262306a36Sopenharmony_ci#include "xfs_fsmap.h" 2362306a36Sopenharmony_ci#include "xfs_refcount.h" 2462306a36Sopenharmony_ci#include "xfs_refcount_btree.h" 2562306a36Sopenharmony_ci#include "xfs_alloc_btree.h" 2662306a36Sopenharmony_ci#include "xfs_rtalloc.h" 2762306a36Sopenharmony_ci#include "xfs_ag.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Convert an xfs_fsmap to an fsmap. */ 3062306a36Sopenharmony_cistatic void 3162306a36Sopenharmony_cixfs_fsmap_from_internal( 3262306a36Sopenharmony_ci struct fsmap *dest, 3362306a36Sopenharmony_ci struct xfs_fsmap *src) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci dest->fmr_device = src->fmr_device; 3662306a36Sopenharmony_ci dest->fmr_flags = src->fmr_flags; 3762306a36Sopenharmony_ci dest->fmr_physical = BBTOB(src->fmr_physical); 3862306a36Sopenharmony_ci dest->fmr_owner = src->fmr_owner; 3962306a36Sopenharmony_ci dest->fmr_offset = BBTOB(src->fmr_offset); 4062306a36Sopenharmony_ci dest->fmr_length = BBTOB(src->fmr_length); 4162306a36Sopenharmony_ci dest->fmr_reserved[0] = 0; 4262306a36Sopenharmony_ci dest->fmr_reserved[1] = 0; 4362306a36Sopenharmony_ci dest->fmr_reserved[2] = 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Convert an fsmap to an xfs_fsmap. */ 4762306a36Sopenharmony_civoid 4862306a36Sopenharmony_cixfs_fsmap_to_internal( 4962306a36Sopenharmony_ci struct xfs_fsmap *dest, 5062306a36Sopenharmony_ci struct fsmap *src) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci dest->fmr_device = src->fmr_device; 5362306a36Sopenharmony_ci dest->fmr_flags = src->fmr_flags; 5462306a36Sopenharmony_ci dest->fmr_physical = BTOBBT(src->fmr_physical); 5562306a36Sopenharmony_ci dest->fmr_owner = src->fmr_owner; 5662306a36Sopenharmony_ci dest->fmr_offset = BTOBBT(src->fmr_offset); 5762306a36Sopenharmony_ci dest->fmr_length = BTOBBT(src->fmr_length); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* Convert an fsmap owner into an rmapbt owner. */ 6162306a36Sopenharmony_cistatic int 6262306a36Sopenharmony_cixfs_fsmap_owner_to_rmap( 6362306a36Sopenharmony_ci struct xfs_rmap_irec *dest, 6462306a36Sopenharmony_ci const struct xfs_fsmap *src) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci if (!(src->fmr_flags & FMR_OF_SPECIAL_OWNER)) { 6762306a36Sopenharmony_ci dest->rm_owner = src->fmr_owner; 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci switch (src->fmr_owner) { 7262306a36Sopenharmony_ci case 0: /* "lowest owner id possible" */ 7362306a36Sopenharmony_ci case -1ULL: /* "highest owner id possible" */ 7462306a36Sopenharmony_ci dest->rm_owner = 0; 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci case XFS_FMR_OWN_FREE: 7762306a36Sopenharmony_ci dest->rm_owner = XFS_RMAP_OWN_NULL; 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci case XFS_FMR_OWN_UNKNOWN: 8062306a36Sopenharmony_ci dest->rm_owner = XFS_RMAP_OWN_UNKNOWN; 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci case XFS_FMR_OWN_FS: 8362306a36Sopenharmony_ci dest->rm_owner = XFS_RMAP_OWN_FS; 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci case XFS_FMR_OWN_LOG: 8662306a36Sopenharmony_ci dest->rm_owner = XFS_RMAP_OWN_LOG; 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci case XFS_FMR_OWN_AG: 8962306a36Sopenharmony_ci dest->rm_owner = XFS_RMAP_OWN_AG; 9062306a36Sopenharmony_ci break; 9162306a36Sopenharmony_ci case XFS_FMR_OWN_INOBT: 9262306a36Sopenharmony_ci dest->rm_owner = XFS_RMAP_OWN_INOBT; 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci case XFS_FMR_OWN_INODES: 9562306a36Sopenharmony_ci dest->rm_owner = XFS_RMAP_OWN_INODES; 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci case XFS_FMR_OWN_REFC: 9862306a36Sopenharmony_ci dest->rm_owner = XFS_RMAP_OWN_REFC; 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci case XFS_FMR_OWN_COW: 10162306a36Sopenharmony_ci dest->rm_owner = XFS_RMAP_OWN_COW; 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci case XFS_FMR_OWN_DEFECTIVE: /* not implemented */ 10462306a36Sopenharmony_ci /* fall through */ 10562306a36Sopenharmony_ci default: 10662306a36Sopenharmony_ci return -EINVAL; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* Convert an rmapbt owner into an fsmap owner. */ 11262306a36Sopenharmony_cistatic int 11362306a36Sopenharmony_cixfs_fsmap_owner_from_rmap( 11462306a36Sopenharmony_ci struct xfs_fsmap *dest, 11562306a36Sopenharmony_ci const struct xfs_rmap_irec *src) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci dest->fmr_flags = 0; 11862306a36Sopenharmony_ci if (!XFS_RMAP_NON_INODE_OWNER(src->rm_owner)) { 11962306a36Sopenharmony_ci dest->fmr_owner = src->rm_owner; 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci dest->fmr_flags |= FMR_OF_SPECIAL_OWNER; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci switch (src->rm_owner) { 12562306a36Sopenharmony_ci case XFS_RMAP_OWN_FS: 12662306a36Sopenharmony_ci dest->fmr_owner = XFS_FMR_OWN_FS; 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci case XFS_RMAP_OWN_LOG: 12962306a36Sopenharmony_ci dest->fmr_owner = XFS_FMR_OWN_LOG; 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci case XFS_RMAP_OWN_AG: 13262306a36Sopenharmony_ci dest->fmr_owner = XFS_FMR_OWN_AG; 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci case XFS_RMAP_OWN_INOBT: 13562306a36Sopenharmony_ci dest->fmr_owner = XFS_FMR_OWN_INOBT; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci case XFS_RMAP_OWN_INODES: 13862306a36Sopenharmony_ci dest->fmr_owner = XFS_FMR_OWN_INODES; 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci case XFS_RMAP_OWN_REFC: 14162306a36Sopenharmony_ci dest->fmr_owner = XFS_FMR_OWN_REFC; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case XFS_RMAP_OWN_COW: 14462306a36Sopenharmony_ci dest->fmr_owner = XFS_FMR_OWN_COW; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci case XFS_RMAP_OWN_NULL: /* "free" */ 14762306a36Sopenharmony_ci dest->fmr_owner = XFS_FMR_OWN_FREE; 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci default: 15062306a36Sopenharmony_ci ASSERT(0); 15162306a36Sopenharmony_ci return -EFSCORRUPTED; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* getfsmap query state */ 15762306a36Sopenharmony_cistruct xfs_getfsmap_info { 15862306a36Sopenharmony_ci struct xfs_fsmap_head *head; 15962306a36Sopenharmony_ci struct fsmap *fsmap_recs; /* mapping records */ 16062306a36Sopenharmony_ci struct xfs_buf *agf_bp; /* AGF, for refcount queries */ 16162306a36Sopenharmony_ci struct xfs_perag *pag; /* AG info, if applicable */ 16262306a36Sopenharmony_ci xfs_daddr_t next_daddr; /* next daddr we expect */ 16362306a36Sopenharmony_ci /* daddr of low fsmap key when we're using the rtbitmap */ 16462306a36Sopenharmony_ci xfs_daddr_t low_daddr; 16562306a36Sopenharmony_ci u64 missing_owner; /* owner of holes */ 16662306a36Sopenharmony_ci u32 dev; /* device id */ 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * Low rmap key for the query. If low.rm_blockcount is nonzero, this 16962306a36Sopenharmony_ci * is the second (or later) call to retrieve the recordset in pieces. 17062306a36Sopenharmony_ci * xfs_getfsmap_rec_before_start will compare all records retrieved 17162306a36Sopenharmony_ci * by the rmapbt query to filter out any records that start before 17262306a36Sopenharmony_ci * the last record. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci struct xfs_rmap_irec low; 17562306a36Sopenharmony_ci struct xfs_rmap_irec high; /* high rmap key */ 17662306a36Sopenharmony_ci bool last; /* last extent? */ 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* Associate a device with a getfsmap handler. */ 18062306a36Sopenharmony_cistruct xfs_getfsmap_dev { 18162306a36Sopenharmony_ci u32 dev; 18262306a36Sopenharmony_ci int (*fn)(struct xfs_trans *tp, 18362306a36Sopenharmony_ci const struct xfs_fsmap *keys, 18462306a36Sopenharmony_ci struct xfs_getfsmap_info *info); 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* Compare two getfsmap device handlers. */ 18862306a36Sopenharmony_cistatic int 18962306a36Sopenharmony_cixfs_getfsmap_dev_compare( 19062306a36Sopenharmony_ci const void *p1, 19162306a36Sopenharmony_ci const void *p2) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci const struct xfs_getfsmap_dev *d1 = p1; 19462306a36Sopenharmony_ci const struct xfs_getfsmap_dev *d2 = p2; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return d1->dev - d2->dev; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* Decide if this mapping is shared. */ 20062306a36Sopenharmony_ciSTATIC int 20162306a36Sopenharmony_cixfs_getfsmap_is_shared( 20262306a36Sopenharmony_ci struct xfs_trans *tp, 20362306a36Sopenharmony_ci struct xfs_getfsmap_info *info, 20462306a36Sopenharmony_ci const struct xfs_rmap_irec *rec, 20562306a36Sopenharmony_ci bool *stat) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct xfs_mount *mp = tp->t_mountp; 20862306a36Sopenharmony_ci struct xfs_btree_cur *cur; 20962306a36Sopenharmony_ci xfs_agblock_t fbno; 21062306a36Sopenharmony_ci xfs_extlen_t flen; 21162306a36Sopenharmony_ci int error; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci *stat = false; 21462306a36Sopenharmony_ci if (!xfs_has_reflink(mp)) 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci /* rt files will have no perag structure */ 21762306a36Sopenharmony_ci if (!info->pag) 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Are there any shared blocks here? */ 22162306a36Sopenharmony_ci flen = 0; 22262306a36Sopenharmony_ci cur = xfs_refcountbt_init_cursor(mp, tp, info->agf_bp, info->pag); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci error = xfs_refcount_find_shared(cur, rec->rm_startblock, 22562306a36Sopenharmony_ci rec->rm_blockcount, &fbno, &flen, false); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci xfs_btree_del_cursor(cur, error); 22862306a36Sopenharmony_ci if (error) 22962306a36Sopenharmony_ci return error; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci *stat = flen > 0; 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic inline void 23662306a36Sopenharmony_cixfs_getfsmap_format( 23762306a36Sopenharmony_ci struct xfs_mount *mp, 23862306a36Sopenharmony_ci struct xfs_fsmap *xfm, 23962306a36Sopenharmony_ci struct xfs_getfsmap_info *info) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct fsmap *rec; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci trace_xfs_getfsmap_mapping(mp, xfm); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci rec = &info->fsmap_recs[info->head->fmh_entries++]; 24662306a36Sopenharmony_ci xfs_fsmap_from_internal(rec, xfm); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic inline bool 25062306a36Sopenharmony_cixfs_getfsmap_rec_before_start( 25162306a36Sopenharmony_ci struct xfs_getfsmap_info *info, 25262306a36Sopenharmony_ci const struct xfs_rmap_irec *rec, 25362306a36Sopenharmony_ci xfs_daddr_t rec_daddr) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci if (info->low_daddr != -1ULL) 25662306a36Sopenharmony_ci return rec_daddr < info->low_daddr; 25762306a36Sopenharmony_ci if (info->low.rm_blockcount) 25862306a36Sopenharmony_ci return xfs_rmap_compare(rec, &info->low) < 0; 25962306a36Sopenharmony_ci return false; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * Format a reverse mapping for getfsmap, having translated rm_startblock 26462306a36Sopenharmony_ci * into the appropriate daddr units. Pass in a nonzero @len_daddr if the 26562306a36Sopenharmony_ci * length could be larger than rm_blockcount in struct xfs_rmap_irec. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ciSTATIC int 26862306a36Sopenharmony_cixfs_getfsmap_helper( 26962306a36Sopenharmony_ci struct xfs_trans *tp, 27062306a36Sopenharmony_ci struct xfs_getfsmap_info *info, 27162306a36Sopenharmony_ci const struct xfs_rmap_irec *rec, 27262306a36Sopenharmony_ci xfs_daddr_t rec_daddr, 27362306a36Sopenharmony_ci xfs_daddr_t len_daddr) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct xfs_fsmap fmr; 27662306a36Sopenharmony_ci struct xfs_mount *mp = tp->t_mountp; 27762306a36Sopenharmony_ci bool shared; 27862306a36Sopenharmony_ci int error; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (fatal_signal_pending(current)) 28162306a36Sopenharmony_ci return -EINTR; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (len_daddr == 0) 28462306a36Sopenharmony_ci len_daddr = XFS_FSB_TO_BB(mp, rec->rm_blockcount); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * Filter out records that start before our startpoint, if the 28862306a36Sopenharmony_ci * caller requested that. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci if (xfs_getfsmap_rec_before_start(info, rec, rec_daddr)) { 29162306a36Sopenharmony_ci rec_daddr += len_daddr; 29262306a36Sopenharmony_ci if (info->next_daddr < rec_daddr) 29362306a36Sopenharmony_ci info->next_daddr = rec_daddr; 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Are we just counting mappings? */ 29862306a36Sopenharmony_ci if (info->head->fmh_count == 0) { 29962306a36Sopenharmony_ci if (info->head->fmh_entries == UINT_MAX) 30062306a36Sopenharmony_ci return -ECANCELED; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (rec_daddr > info->next_daddr) 30362306a36Sopenharmony_ci info->head->fmh_entries++; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (info->last) 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci info->head->fmh_entries++; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci rec_daddr += len_daddr; 31162306a36Sopenharmony_ci if (info->next_daddr < rec_daddr) 31262306a36Sopenharmony_ci info->next_daddr = rec_daddr; 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* 31762306a36Sopenharmony_ci * If the record starts past the last physical block we saw, 31862306a36Sopenharmony_ci * then we've found a gap. Report the gap as being owned by 31962306a36Sopenharmony_ci * whatever the caller specified is the missing owner. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci if (rec_daddr > info->next_daddr) { 32262306a36Sopenharmony_ci if (info->head->fmh_entries >= info->head->fmh_count) 32362306a36Sopenharmony_ci return -ECANCELED; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci fmr.fmr_device = info->dev; 32662306a36Sopenharmony_ci fmr.fmr_physical = info->next_daddr; 32762306a36Sopenharmony_ci fmr.fmr_owner = info->missing_owner; 32862306a36Sopenharmony_ci fmr.fmr_offset = 0; 32962306a36Sopenharmony_ci fmr.fmr_length = rec_daddr - info->next_daddr; 33062306a36Sopenharmony_ci fmr.fmr_flags = FMR_OF_SPECIAL_OWNER; 33162306a36Sopenharmony_ci xfs_getfsmap_format(mp, &fmr, info); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (info->last) 33562306a36Sopenharmony_ci goto out; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Fill out the extent we found */ 33862306a36Sopenharmony_ci if (info->head->fmh_entries >= info->head->fmh_count) 33962306a36Sopenharmony_ci return -ECANCELED; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci trace_xfs_fsmap_mapping(mp, info->dev, 34262306a36Sopenharmony_ci info->pag ? info->pag->pag_agno : NULLAGNUMBER, rec); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci fmr.fmr_device = info->dev; 34562306a36Sopenharmony_ci fmr.fmr_physical = rec_daddr; 34662306a36Sopenharmony_ci error = xfs_fsmap_owner_from_rmap(&fmr, rec); 34762306a36Sopenharmony_ci if (error) 34862306a36Sopenharmony_ci return error; 34962306a36Sopenharmony_ci fmr.fmr_offset = XFS_FSB_TO_BB(mp, rec->rm_offset); 35062306a36Sopenharmony_ci fmr.fmr_length = len_daddr; 35162306a36Sopenharmony_ci if (rec->rm_flags & XFS_RMAP_UNWRITTEN) 35262306a36Sopenharmony_ci fmr.fmr_flags |= FMR_OF_PREALLOC; 35362306a36Sopenharmony_ci if (rec->rm_flags & XFS_RMAP_ATTR_FORK) 35462306a36Sopenharmony_ci fmr.fmr_flags |= FMR_OF_ATTR_FORK; 35562306a36Sopenharmony_ci if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK) 35662306a36Sopenharmony_ci fmr.fmr_flags |= FMR_OF_EXTENT_MAP; 35762306a36Sopenharmony_ci if (fmr.fmr_flags == 0) { 35862306a36Sopenharmony_ci error = xfs_getfsmap_is_shared(tp, info, rec, &shared); 35962306a36Sopenharmony_ci if (error) 36062306a36Sopenharmony_ci return error; 36162306a36Sopenharmony_ci if (shared) 36262306a36Sopenharmony_ci fmr.fmr_flags |= FMR_OF_SHARED; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci xfs_getfsmap_format(mp, &fmr, info); 36662306a36Sopenharmony_ciout: 36762306a36Sopenharmony_ci rec_daddr += len_daddr; 36862306a36Sopenharmony_ci if (info->next_daddr < rec_daddr) 36962306a36Sopenharmony_ci info->next_daddr = rec_daddr; 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci/* Transform a rmapbt irec into a fsmap */ 37462306a36Sopenharmony_ciSTATIC int 37562306a36Sopenharmony_cixfs_getfsmap_datadev_helper( 37662306a36Sopenharmony_ci struct xfs_btree_cur *cur, 37762306a36Sopenharmony_ci const struct xfs_rmap_irec *rec, 37862306a36Sopenharmony_ci void *priv) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct xfs_mount *mp = cur->bc_mp; 38162306a36Sopenharmony_ci struct xfs_getfsmap_info *info = priv; 38262306a36Sopenharmony_ci xfs_fsblock_t fsb; 38362306a36Sopenharmony_ci xfs_daddr_t rec_daddr; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci fsb = XFS_AGB_TO_FSB(mp, cur->bc_ag.pag->pag_agno, rec->rm_startblock); 38662306a36Sopenharmony_ci rec_daddr = XFS_FSB_TO_DADDR(mp, fsb); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return xfs_getfsmap_helper(cur->bc_tp, info, rec, rec_daddr, 0); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* Transform a bnobt irec into a fsmap */ 39262306a36Sopenharmony_ciSTATIC int 39362306a36Sopenharmony_cixfs_getfsmap_datadev_bnobt_helper( 39462306a36Sopenharmony_ci struct xfs_btree_cur *cur, 39562306a36Sopenharmony_ci const struct xfs_alloc_rec_incore *rec, 39662306a36Sopenharmony_ci void *priv) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct xfs_mount *mp = cur->bc_mp; 39962306a36Sopenharmony_ci struct xfs_getfsmap_info *info = priv; 40062306a36Sopenharmony_ci struct xfs_rmap_irec irec; 40162306a36Sopenharmony_ci xfs_daddr_t rec_daddr; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci rec_daddr = XFS_AGB_TO_DADDR(mp, cur->bc_ag.pag->pag_agno, 40462306a36Sopenharmony_ci rec->ar_startblock); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci irec.rm_startblock = rec->ar_startblock; 40762306a36Sopenharmony_ci irec.rm_blockcount = rec->ar_blockcount; 40862306a36Sopenharmony_ci irec.rm_owner = XFS_RMAP_OWN_NULL; /* "free" */ 40962306a36Sopenharmony_ci irec.rm_offset = 0; 41062306a36Sopenharmony_ci irec.rm_flags = 0; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return xfs_getfsmap_helper(cur->bc_tp, info, &irec, rec_daddr, 0); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci/* Set rmap flags based on the getfsmap flags */ 41662306a36Sopenharmony_cistatic void 41762306a36Sopenharmony_cixfs_getfsmap_set_irec_flags( 41862306a36Sopenharmony_ci struct xfs_rmap_irec *irec, 41962306a36Sopenharmony_ci const struct xfs_fsmap *fmr) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci irec->rm_flags = 0; 42262306a36Sopenharmony_ci if (fmr->fmr_flags & FMR_OF_ATTR_FORK) 42362306a36Sopenharmony_ci irec->rm_flags |= XFS_RMAP_ATTR_FORK; 42462306a36Sopenharmony_ci if (fmr->fmr_flags & FMR_OF_EXTENT_MAP) 42562306a36Sopenharmony_ci irec->rm_flags |= XFS_RMAP_BMBT_BLOCK; 42662306a36Sopenharmony_ci if (fmr->fmr_flags & FMR_OF_PREALLOC) 42762306a36Sopenharmony_ci irec->rm_flags |= XFS_RMAP_UNWRITTEN; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci/* Execute a getfsmap query against the log device. */ 43162306a36Sopenharmony_ciSTATIC int 43262306a36Sopenharmony_cixfs_getfsmap_logdev( 43362306a36Sopenharmony_ci struct xfs_trans *tp, 43462306a36Sopenharmony_ci const struct xfs_fsmap *keys, 43562306a36Sopenharmony_ci struct xfs_getfsmap_info *info) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct xfs_mount *mp = tp->t_mountp; 43862306a36Sopenharmony_ci struct xfs_rmap_irec rmap; 43962306a36Sopenharmony_ci xfs_daddr_t rec_daddr, len_daddr; 44062306a36Sopenharmony_ci xfs_fsblock_t start_fsb, end_fsb; 44162306a36Sopenharmony_ci uint64_t eofs; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks); 44462306a36Sopenharmony_ci if (keys[0].fmr_physical >= eofs) 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci start_fsb = XFS_BB_TO_FSBT(mp, 44762306a36Sopenharmony_ci keys[0].fmr_physical + keys[0].fmr_length); 44862306a36Sopenharmony_ci end_fsb = XFS_BB_TO_FSB(mp, min(eofs - 1, keys[1].fmr_physical)); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Adjust the low key if we are continuing from where we left off. */ 45162306a36Sopenharmony_ci if (keys[0].fmr_length > 0) 45262306a36Sopenharmony_ci info->low_daddr = XFS_FSB_TO_BB(mp, start_fsb); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci trace_xfs_fsmap_low_key_linear(mp, info->dev, start_fsb); 45562306a36Sopenharmony_ci trace_xfs_fsmap_high_key_linear(mp, info->dev, end_fsb); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (start_fsb > 0) 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Fabricate an rmap entry for the external log device. */ 46162306a36Sopenharmony_ci rmap.rm_startblock = 0; 46262306a36Sopenharmony_ci rmap.rm_blockcount = mp->m_sb.sb_logblocks; 46362306a36Sopenharmony_ci rmap.rm_owner = XFS_RMAP_OWN_LOG; 46462306a36Sopenharmony_ci rmap.rm_offset = 0; 46562306a36Sopenharmony_ci rmap.rm_flags = 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci rec_daddr = XFS_FSB_TO_BB(mp, rmap.rm_startblock); 46862306a36Sopenharmony_ci len_daddr = XFS_FSB_TO_BB(mp, rmap.rm_blockcount); 46962306a36Sopenharmony_ci return xfs_getfsmap_helper(tp, info, &rmap, rec_daddr, len_daddr); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci#ifdef CONFIG_XFS_RT 47362306a36Sopenharmony_ci/* Transform a rtbitmap "record" into a fsmap */ 47462306a36Sopenharmony_ciSTATIC int 47562306a36Sopenharmony_cixfs_getfsmap_rtdev_rtbitmap_helper( 47662306a36Sopenharmony_ci struct xfs_mount *mp, 47762306a36Sopenharmony_ci struct xfs_trans *tp, 47862306a36Sopenharmony_ci const struct xfs_rtalloc_rec *rec, 47962306a36Sopenharmony_ci void *priv) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct xfs_getfsmap_info *info = priv; 48262306a36Sopenharmony_ci struct xfs_rmap_irec irec; 48362306a36Sopenharmony_ci xfs_rtblock_t rtbno; 48462306a36Sopenharmony_ci xfs_daddr_t rec_daddr, len_daddr; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci rtbno = rec->ar_startext * mp->m_sb.sb_rextsize; 48762306a36Sopenharmony_ci rec_daddr = XFS_FSB_TO_BB(mp, rtbno); 48862306a36Sopenharmony_ci irec.rm_startblock = rtbno; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci rtbno = rec->ar_extcount * mp->m_sb.sb_rextsize; 49162306a36Sopenharmony_ci len_daddr = XFS_FSB_TO_BB(mp, rtbno); 49262306a36Sopenharmony_ci irec.rm_blockcount = rtbno; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci irec.rm_owner = XFS_RMAP_OWN_NULL; /* "free" */ 49562306a36Sopenharmony_ci irec.rm_offset = 0; 49662306a36Sopenharmony_ci irec.rm_flags = 0; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return xfs_getfsmap_helper(tp, info, &irec, rec_daddr, len_daddr); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci/* Execute a getfsmap query against the realtime device rtbitmap. */ 50262306a36Sopenharmony_ciSTATIC int 50362306a36Sopenharmony_cixfs_getfsmap_rtdev_rtbitmap( 50462306a36Sopenharmony_ci struct xfs_trans *tp, 50562306a36Sopenharmony_ci const struct xfs_fsmap *keys, 50662306a36Sopenharmony_ci struct xfs_getfsmap_info *info) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci struct xfs_rtalloc_rec alow = { 0 }; 51062306a36Sopenharmony_ci struct xfs_rtalloc_rec ahigh = { 0 }; 51162306a36Sopenharmony_ci struct xfs_mount *mp = tp->t_mountp; 51262306a36Sopenharmony_ci xfs_rtblock_t start_rtb; 51362306a36Sopenharmony_ci xfs_rtblock_t end_rtb; 51462306a36Sopenharmony_ci uint64_t eofs; 51562306a36Sopenharmony_ci int error; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rextents * mp->m_sb.sb_rextsize); 51862306a36Sopenharmony_ci if (keys[0].fmr_physical >= eofs) 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci start_rtb = XFS_BB_TO_FSBT(mp, 52162306a36Sopenharmony_ci keys[0].fmr_physical + keys[0].fmr_length); 52262306a36Sopenharmony_ci end_rtb = XFS_BB_TO_FSB(mp, min(eofs - 1, keys[1].fmr_physical)); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci info->missing_owner = XFS_FMR_OWN_UNKNOWN; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* Adjust the low key if we are continuing from where we left off. */ 52762306a36Sopenharmony_ci if (keys[0].fmr_length > 0) { 52862306a36Sopenharmony_ci info->low_daddr = XFS_FSB_TO_BB(mp, start_rtb); 52962306a36Sopenharmony_ci if (info->low_daddr >= eofs) 53062306a36Sopenharmony_ci return 0; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci trace_xfs_fsmap_low_key_linear(mp, info->dev, start_rtb); 53462306a36Sopenharmony_ci trace_xfs_fsmap_high_key_linear(mp, info->dev, end_rtb); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci xfs_ilock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* 53962306a36Sopenharmony_ci * Set up query parameters to return free rtextents covering the range 54062306a36Sopenharmony_ci * we want. 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci alow.ar_startext = start_rtb; 54362306a36Sopenharmony_ci ahigh.ar_startext = end_rtb; 54462306a36Sopenharmony_ci do_div(alow.ar_startext, mp->m_sb.sb_rextsize); 54562306a36Sopenharmony_ci if (do_div(ahigh.ar_startext, mp->m_sb.sb_rextsize)) 54662306a36Sopenharmony_ci ahigh.ar_startext++; 54762306a36Sopenharmony_ci error = xfs_rtalloc_query_range(mp, tp, &alow, &ahigh, 54862306a36Sopenharmony_ci xfs_getfsmap_rtdev_rtbitmap_helper, info); 54962306a36Sopenharmony_ci if (error) 55062306a36Sopenharmony_ci goto err; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* 55362306a36Sopenharmony_ci * Report any gaps at the end of the rtbitmap by simulating a null 55462306a36Sopenharmony_ci * rmap starting at the block after the end of the query range. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci info->last = true; 55762306a36Sopenharmony_ci ahigh.ar_startext = min(mp->m_sb.sb_rextents, ahigh.ar_startext); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci error = xfs_getfsmap_rtdev_rtbitmap_helper(mp, tp, &ahigh, info); 56062306a36Sopenharmony_ci if (error) 56162306a36Sopenharmony_ci goto err; 56262306a36Sopenharmony_cierr: 56362306a36Sopenharmony_ci xfs_iunlock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP); 56462306a36Sopenharmony_ci return error; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci#endif /* CONFIG_XFS_RT */ 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic inline bool 56962306a36Sopenharmony_cirmap_not_shareable(struct xfs_mount *mp, const struct xfs_rmap_irec *r) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci if (!xfs_has_reflink(mp)) 57262306a36Sopenharmony_ci return true; 57362306a36Sopenharmony_ci if (XFS_RMAP_NON_INODE_OWNER(r->rm_owner)) 57462306a36Sopenharmony_ci return true; 57562306a36Sopenharmony_ci if (r->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK | 57662306a36Sopenharmony_ci XFS_RMAP_UNWRITTEN)) 57762306a36Sopenharmony_ci return true; 57862306a36Sopenharmony_ci return false; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci/* Execute a getfsmap query against the regular data device. */ 58262306a36Sopenharmony_ciSTATIC int 58362306a36Sopenharmony_ci__xfs_getfsmap_datadev( 58462306a36Sopenharmony_ci struct xfs_trans *tp, 58562306a36Sopenharmony_ci const struct xfs_fsmap *keys, 58662306a36Sopenharmony_ci struct xfs_getfsmap_info *info, 58762306a36Sopenharmony_ci int (*query_fn)(struct xfs_trans *, 58862306a36Sopenharmony_ci struct xfs_getfsmap_info *, 58962306a36Sopenharmony_ci struct xfs_btree_cur **, 59062306a36Sopenharmony_ci void *), 59162306a36Sopenharmony_ci void *priv) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct xfs_mount *mp = tp->t_mountp; 59462306a36Sopenharmony_ci struct xfs_perag *pag; 59562306a36Sopenharmony_ci struct xfs_btree_cur *bt_cur = NULL; 59662306a36Sopenharmony_ci xfs_fsblock_t start_fsb; 59762306a36Sopenharmony_ci xfs_fsblock_t end_fsb; 59862306a36Sopenharmony_ci xfs_agnumber_t start_ag; 59962306a36Sopenharmony_ci xfs_agnumber_t end_ag; 60062306a36Sopenharmony_ci uint64_t eofs; 60162306a36Sopenharmony_ci int error = 0; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks); 60462306a36Sopenharmony_ci if (keys[0].fmr_physical >= eofs) 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_ci start_fsb = XFS_DADDR_TO_FSB(mp, keys[0].fmr_physical); 60762306a36Sopenharmony_ci end_fsb = XFS_DADDR_TO_FSB(mp, min(eofs - 1, keys[1].fmr_physical)); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* 61062306a36Sopenharmony_ci * Convert the fsmap low/high keys to AG based keys. Initialize 61162306a36Sopenharmony_ci * low to the fsmap low key and max out the high key to the end 61262306a36Sopenharmony_ci * of the AG. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci info->low.rm_offset = XFS_BB_TO_FSBT(mp, keys[0].fmr_offset); 61562306a36Sopenharmony_ci error = xfs_fsmap_owner_to_rmap(&info->low, &keys[0]); 61662306a36Sopenharmony_ci if (error) 61762306a36Sopenharmony_ci return error; 61862306a36Sopenharmony_ci info->low.rm_blockcount = XFS_BB_TO_FSBT(mp, keys[0].fmr_length); 61962306a36Sopenharmony_ci xfs_getfsmap_set_irec_flags(&info->low, &keys[0]); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* Adjust the low key if we are continuing from where we left off. */ 62262306a36Sopenharmony_ci if (info->low.rm_blockcount == 0) { 62362306a36Sopenharmony_ci /* No previous record from which to continue */ 62462306a36Sopenharmony_ci } else if (rmap_not_shareable(mp, &info->low)) { 62562306a36Sopenharmony_ci /* Last record seen was an unshareable extent */ 62662306a36Sopenharmony_ci info->low.rm_owner = 0; 62762306a36Sopenharmony_ci info->low.rm_offset = 0; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci start_fsb += info->low.rm_blockcount; 63062306a36Sopenharmony_ci if (XFS_FSB_TO_DADDR(mp, start_fsb) >= eofs) 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci } else { 63362306a36Sopenharmony_ci /* Last record seen was a shareable file data extent */ 63462306a36Sopenharmony_ci info->low.rm_offset += info->low.rm_blockcount; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci info->low.rm_startblock = XFS_FSB_TO_AGBNO(mp, start_fsb); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci info->high.rm_startblock = -1U; 63962306a36Sopenharmony_ci info->high.rm_owner = ULLONG_MAX; 64062306a36Sopenharmony_ci info->high.rm_offset = ULLONG_MAX; 64162306a36Sopenharmony_ci info->high.rm_blockcount = 0; 64262306a36Sopenharmony_ci info->high.rm_flags = XFS_RMAP_KEY_FLAGS | XFS_RMAP_REC_FLAGS; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci start_ag = XFS_FSB_TO_AGNO(mp, start_fsb); 64562306a36Sopenharmony_ci end_ag = XFS_FSB_TO_AGNO(mp, end_fsb); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci for_each_perag_range(mp, start_ag, end_ag, pag) { 64862306a36Sopenharmony_ci /* 64962306a36Sopenharmony_ci * Set the AG high key from the fsmap high key if this 65062306a36Sopenharmony_ci * is the last AG that we're querying. 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci info->pag = pag; 65362306a36Sopenharmony_ci if (pag->pag_agno == end_ag) { 65462306a36Sopenharmony_ci info->high.rm_startblock = XFS_FSB_TO_AGBNO(mp, 65562306a36Sopenharmony_ci end_fsb); 65662306a36Sopenharmony_ci info->high.rm_offset = XFS_BB_TO_FSBT(mp, 65762306a36Sopenharmony_ci keys[1].fmr_offset); 65862306a36Sopenharmony_ci error = xfs_fsmap_owner_to_rmap(&info->high, &keys[1]); 65962306a36Sopenharmony_ci if (error) 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci xfs_getfsmap_set_irec_flags(&info->high, &keys[1]); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (bt_cur) { 66562306a36Sopenharmony_ci xfs_btree_del_cursor(bt_cur, XFS_BTREE_NOERROR); 66662306a36Sopenharmony_ci bt_cur = NULL; 66762306a36Sopenharmony_ci xfs_trans_brelse(tp, info->agf_bp); 66862306a36Sopenharmony_ci info->agf_bp = NULL; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci error = xfs_alloc_read_agf(pag, tp, 0, &info->agf_bp); 67262306a36Sopenharmony_ci if (error) 67362306a36Sopenharmony_ci break; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci trace_xfs_fsmap_low_key(mp, info->dev, pag->pag_agno, 67662306a36Sopenharmony_ci &info->low); 67762306a36Sopenharmony_ci trace_xfs_fsmap_high_key(mp, info->dev, pag->pag_agno, 67862306a36Sopenharmony_ci &info->high); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci error = query_fn(tp, info, &bt_cur, priv); 68162306a36Sopenharmony_ci if (error) 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* 68562306a36Sopenharmony_ci * Set the AG low key to the start of the AG prior to 68662306a36Sopenharmony_ci * moving on to the next AG. 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_ci if (pag->pag_agno == start_ag) 68962306a36Sopenharmony_ci memset(&info->low, 0, sizeof(info->low)); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* 69262306a36Sopenharmony_ci * If this is the last AG, report any gap at the end of it 69362306a36Sopenharmony_ci * before we drop the reference to the perag when the loop 69462306a36Sopenharmony_ci * terminates. 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_ci if (pag->pag_agno == end_ag) { 69762306a36Sopenharmony_ci info->last = true; 69862306a36Sopenharmony_ci error = query_fn(tp, info, &bt_cur, priv); 69962306a36Sopenharmony_ci if (error) 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci info->pag = NULL; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (bt_cur) 70662306a36Sopenharmony_ci xfs_btree_del_cursor(bt_cur, error < 0 ? XFS_BTREE_ERROR : 70762306a36Sopenharmony_ci XFS_BTREE_NOERROR); 70862306a36Sopenharmony_ci if (info->agf_bp) { 70962306a36Sopenharmony_ci xfs_trans_brelse(tp, info->agf_bp); 71062306a36Sopenharmony_ci info->agf_bp = NULL; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci if (info->pag) { 71362306a36Sopenharmony_ci xfs_perag_rele(info->pag); 71462306a36Sopenharmony_ci info->pag = NULL; 71562306a36Sopenharmony_ci } else if (pag) { 71662306a36Sopenharmony_ci /* loop termination case */ 71762306a36Sopenharmony_ci xfs_perag_rele(pag); 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return error; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci/* Actually query the rmap btree. */ 72462306a36Sopenharmony_ciSTATIC int 72562306a36Sopenharmony_cixfs_getfsmap_datadev_rmapbt_query( 72662306a36Sopenharmony_ci struct xfs_trans *tp, 72762306a36Sopenharmony_ci struct xfs_getfsmap_info *info, 72862306a36Sopenharmony_ci struct xfs_btree_cur **curpp, 72962306a36Sopenharmony_ci void *priv) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci /* Report any gap at the end of the last AG. */ 73262306a36Sopenharmony_ci if (info->last) 73362306a36Sopenharmony_ci return xfs_getfsmap_datadev_helper(*curpp, &info->high, info); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* Allocate cursor for this AG and query_range it. */ 73662306a36Sopenharmony_ci *curpp = xfs_rmapbt_init_cursor(tp->t_mountp, tp, info->agf_bp, 73762306a36Sopenharmony_ci info->pag); 73862306a36Sopenharmony_ci return xfs_rmap_query_range(*curpp, &info->low, &info->high, 73962306a36Sopenharmony_ci xfs_getfsmap_datadev_helper, info); 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci/* Execute a getfsmap query against the regular data device rmapbt. */ 74362306a36Sopenharmony_ciSTATIC int 74462306a36Sopenharmony_cixfs_getfsmap_datadev_rmapbt( 74562306a36Sopenharmony_ci struct xfs_trans *tp, 74662306a36Sopenharmony_ci const struct xfs_fsmap *keys, 74762306a36Sopenharmony_ci struct xfs_getfsmap_info *info) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci info->missing_owner = XFS_FMR_OWN_FREE; 75062306a36Sopenharmony_ci return __xfs_getfsmap_datadev(tp, keys, info, 75162306a36Sopenharmony_ci xfs_getfsmap_datadev_rmapbt_query, NULL); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci/* Actually query the bno btree. */ 75562306a36Sopenharmony_ciSTATIC int 75662306a36Sopenharmony_cixfs_getfsmap_datadev_bnobt_query( 75762306a36Sopenharmony_ci struct xfs_trans *tp, 75862306a36Sopenharmony_ci struct xfs_getfsmap_info *info, 75962306a36Sopenharmony_ci struct xfs_btree_cur **curpp, 76062306a36Sopenharmony_ci void *priv) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct xfs_alloc_rec_incore *key = priv; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* Report any gap at the end of the last AG. */ 76562306a36Sopenharmony_ci if (info->last) 76662306a36Sopenharmony_ci return xfs_getfsmap_datadev_bnobt_helper(*curpp, &key[1], info); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* Allocate cursor for this AG and query_range it. */ 76962306a36Sopenharmony_ci *curpp = xfs_allocbt_init_cursor(tp->t_mountp, tp, info->agf_bp, 77062306a36Sopenharmony_ci info->pag, XFS_BTNUM_BNO); 77162306a36Sopenharmony_ci key->ar_startblock = info->low.rm_startblock; 77262306a36Sopenharmony_ci key[1].ar_startblock = info->high.rm_startblock; 77362306a36Sopenharmony_ci return xfs_alloc_query_range(*curpp, key, &key[1], 77462306a36Sopenharmony_ci xfs_getfsmap_datadev_bnobt_helper, info); 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci/* Execute a getfsmap query against the regular data device's bnobt. */ 77862306a36Sopenharmony_ciSTATIC int 77962306a36Sopenharmony_cixfs_getfsmap_datadev_bnobt( 78062306a36Sopenharmony_ci struct xfs_trans *tp, 78162306a36Sopenharmony_ci const struct xfs_fsmap *keys, 78262306a36Sopenharmony_ci struct xfs_getfsmap_info *info) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct xfs_alloc_rec_incore akeys[2]; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci memset(akeys, 0, sizeof(akeys)); 78762306a36Sopenharmony_ci info->missing_owner = XFS_FMR_OWN_UNKNOWN; 78862306a36Sopenharmony_ci return __xfs_getfsmap_datadev(tp, keys, info, 78962306a36Sopenharmony_ci xfs_getfsmap_datadev_bnobt_query, &akeys[0]); 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci/* Do we recognize the device? */ 79362306a36Sopenharmony_ciSTATIC bool 79462306a36Sopenharmony_cixfs_getfsmap_is_valid_device( 79562306a36Sopenharmony_ci struct xfs_mount *mp, 79662306a36Sopenharmony_ci struct xfs_fsmap *fm) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci if (fm->fmr_device == 0 || fm->fmr_device == UINT_MAX || 79962306a36Sopenharmony_ci fm->fmr_device == new_encode_dev(mp->m_ddev_targp->bt_dev)) 80062306a36Sopenharmony_ci return true; 80162306a36Sopenharmony_ci if (mp->m_logdev_targp && 80262306a36Sopenharmony_ci fm->fmr_device == new_encode_dev(mp->m_logdev_targp->bt_dev)) 80362306a36Sopenharmony_ci return true; 80462306a36Sopenharmony_ci if (mp->m_rtdev_targp && 80562306a36Sopenharmony_ci fm->fmr_device == new_encode_dev(mp->m_rtdev_targp->bt_dev)) 80662306a36Sopenharmony_ci return true; 80762306a36Sopenharmony_ci return false; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci/* Ensure that the low key is less than the high key. */ 81162306a36Sopenharmony_ciSTATIC bool 81262306a36Sopenharmony_cixfs_getfsmap_check_keys( 81362306a36Sopenharmony_ci struct xfs_fsmap *low_key, 81462306a36Sopenharmony_ci struct xfs_fsmap *high_key) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci if (low_key->fmr_flags & (FMR_OF_SPECIAL_OWNER | FMR_OF_EXTENT_MAP)) { 81762306a36Sopenharmony_ci if (low_key->fmr_offset) 81862306a36Sopenharmony_ci return false; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci if (high_key->fmr_flags != -1U && 82162306a36Sopenharmony_ci (high_key->fmr_flags & (FMR_OF_SPECIAL_OWNER | 82262306a36Sopenharmony_ci FMR_OF_EXTENT_MAP))) { 82362306a36Sopenharmony_ci if (high_key->fmr_offset && high_key->fmr_offset != -1ULL) 82462306a36Sopenharmony_ci return false; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci if (high_key->fmr_length && high_key->fmr_length != -1ULL) 82762306a36Sopenharmony_ci return false; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (low_key->fmr_device > high_key->fmr_device) 83062306a36Sopenharmony_ci return false; 83162306a36Sopenharmony_ci if (low_key->fmr_device < high_key->fmr_device) 83262306a36Sopenharmony_ci return true; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (low_key->fmr_physical > high_key->fmr_physical) 83562306a36Sopenharmony_ci return false; 83662306a36Sopenharmony_ci if (low_key->fmr_physical < high_key->fmr_physical) 83762306a36Sopenharmony_ci return true; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (low_key->fmr_owner > high_key->fmr_owner) 84062306a36Sopenharmony_ci return false; 84162306a36Sopenharmony_ci if (low_key->fmr_owner < high_key->fmr_owner) 84262306a36Sopenharmony_ci return true; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (low_key->fmr_offset > high_key->fmr_offset) 84562306a36Sopenharmony_ci return false; 84662306a36Sopenharmony_ci if (low_key->fmr_offset < high_key->fmr_offset) 84762306a36Sopenharmony_ci return true; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return false; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci/* 85362306a36Sopenharmony_ci * There are only two devices if we didn't configure RT devices at build time. 85462306a36Sopenharmony_ci */ 85562306a36Sopenharmony_ci#ifdef CONFIG_XFS_RT 85662306a36Sopenharmony_ci#define XFS_GETFSMAP_DEVS 3 85762306a36Sopenharmony_ci#else 85862306a36Sopenharmony_ci#define XFS_GETFSMAP_DEVS 2 85962306a36Sopenharmony_ci#endif /* CONFIG_XFS_RT */ 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci/* 86262306a36Sopenharmony_ci * Get filesystem's extents as described in head, and format for output. Fills 86362306a36Sopenharmony_ci * in the supplied records array until there are no more reverse mappings to 86462306a36Sopenharmony_ci * return or head.fmh_entries == head.fmh_count. In the second case, this 86562306a36Sopenharmony_ci * function returns -ECANCELED to indicate that more records would have been 86662306a36Sopenharmony_ci * returned. 86762306a36Sopenharmony_ci * 86862306a36Sopenharmony_ci * Key to Confusion 86962306a36Sopenharmony_ci * ---------------- 87062306a36Sopenharmony_ci * There are multiple levels of keys and counters at work here: 87162306a36Sopenharmony_ci * xfs_fsmap_head.fmh_keys -- low and high fsmap keys passed in; 87262306a36Sopenharmony_ci * these reflect fs-wide sector addrs. 87362306a36Sopenharmony_ci * dkeys -- fmh_keys used to query each device; 87462306a36Sopenharmony_ci * these are fmh_keys but w/ the low key 87562306a36Sopenharmony_ci * bumped up by fmr_length. 87662306a36Sopenharmony_ci * xfs_getfsmap_info.next_daddr -- next disk addr we expect to see; this 87762306a36Sopenharmony_ci * is how we detect gaps in the fsmap 87862306a36Sopenharmony_ci records and report them. 87962306a36Sopenharmony_ci * xfs_getfsmap_info.low/high -- per-AG low/high keys computed from 88062306a36Sopenharmony_ci * dkeys; used to query the metadata. 88162306a36Sopenharmony_ci */ 88262306a36Sopenharmony_ciint 88362306a36Sopenharmony_cixfs_getfsmap( 88462306a36Sopenharmony_ci struct xfs_mount *mp, 88562306a36Sopenharmony_ci struct xfs_fsmap_head *head, 88662306a36Sopenharmony_ci struct fsmap *fsmap_recs) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci struct xfs_trans *tp = NULL; 88962306a36Sopenharmony_ci struct xfs_fsmap dkeys[2]; /* per-dev keys */ 89062306a36Sopenharmony_ci struct xfs_getfsmap_dev handlers[XFS_GETFSMAP_DEVS]; 89162306a36Sopenharmony_ci struct xfs_getfsmap_info info = { NULL }; 89262306a36Sopenharmony_ci bool use_rmap; 89362306a36Sopenharmony_ci int i; 89462306a36Sopenharmony_ci int error = 0; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (head->fmh_iflags & ~FMH_IF_VALID) 89762306a36Sopenharmony_ci return -EINVAL; 89862306a36Sopenharmony_ci if (!xfs_getfsmap_is_valid_device(mp, &head->fmh_keys[0]) || 89962306a36Sopenharmony_ci !xfs_getfsmap_is_valid_device(mp, &head->fmh_keys[1])) 90062306a36Sopenharmony_ci return -EINVAL; 90162306a36Sopenharmony_ci if (!xfs_getfsmap_check_keys(&head->fmh_keys[0], &head->fmh_keys[1])) 90262306a36Sopenharmony_ci return -EINVAL; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci use_rmap = xfs_has_rmapbt(mp) && 90562306a36Sopenharmony_ci has_capability_noaudit(current, CAP_SYS_ADMIN); 90662306a36Sopenharmony_ci head->fmh_entries = 0; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* Set up our device handlers. */ 90962306a36Sopenharmony_ci memset(handlers, 0, sizeof(handlers)); 91062306a36Sopenharmony_ci handlers[0].dev = new_encode_dev(mp->m_ddev_targp->bt_dev); 91162306a36Sopenharmony_ci if (use_rmap) 91262306a36Sopenharmony_ci handlers[0].fn = xfs_getfsmap_datadev_rmapbt; 91362306a36Sopenharmony_ci else 91462306a36Sopenharmony_ci handlers[0].fn = xfs_getfsmap_datadev_bnobt; 91562306a36Sopenharmony_ci if (mp->m_logdev_targp != mp->m_ddev_targp) { 91662306a36Sopenharmony_ci handlers[1].dev = new_encode_dev(mp->m_logdev_targp->bt_dev); 91762306a36Sopenharmony_ci handlers[1].fn = xfs_getfsmap_logdev; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci#ifdef CONFIG_XFS_RT 92062306a36Sopenharmony_ci if (mp->m_rtdev_targp) { 92162306a36Sopenharmony_ci handlers[2].dev = new_encode_dev(mp->m_rtdev_targp->bt_dev); 92262306a36Sopenharmony_ci handlers[2].fn = xfs_getfsmap_rtdev_rtbitmap; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci#endif /* CONFIG_XFS_RT */ 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci xfs_sort(handlers, XFS_GETFSMAP_DEVS, sizeof(struct xfs_getfsmap_dev), 92762306a36Sopenharmony_ci xfs_getfsmap_dev_compare); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* 93062306a36Sopenharmony_ci * To continue where we left off, we allow userspace to use the 93162306a36Sopenharmony_ci * last mapping from a previous call as the low key of the next. 93262306a36Sopenharmony_ci * This is identified by a non-zero length in the low key. We 93362306a36Sopenharmony_ci * have to increment the low key in this scenario to ensure we 93462306a36Sopenharmony_ci * don't return the same mapping again, and instead return the 93562306a36Sopenharmony_ci * very next mapping. 93662306a36Sopenharmony_ci * 93762306a36Sopenharmony_ci * If the low key mapping refers to file data, the same physical 93862306a36Sopenharmony_ci * blocks could be mapped to several other files/offsets. 93962306a36Sopenharmony_ci * According to rmapbt record ordering, the minimal next 94062306a36Sopenharmony_ci * possible record for the block range is the next starting 94162306a36Sopenharmony_ci * offset in the same inode. Therefore, each fsmap backend bumps 94262306a36Sopenharmony_ci * the file offset to continue the search appropriately. For 94362306a36Sopenharmony_ci * all other low key mapping types (attr blocks, metadata), each 94462306a36Sopenharmony_ci * fsmap backend bumps the physical offset as there can be no 94562306a36Sopenharmony_ci * other mapping for the same physical block range. 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_ci dkeys[0] = head->fmh_keys[0]; 94862306a36Sopenharmony_ci memset(&dkeys[1], 0xFF, sizeof(struct xfs_fsmap)); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci info.next_daddr = head->fmh_keys[0].fmr_physical + 95162306a36Sopenharmony_ci head->fmh_keys[0].fmr_length; 95262306a36Sopenharmony_ci info.fsmap_recs = fsmap_recs; 95362306a36Sopenharmony_ci info.head = head; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* For each device we support... */ 95662306a36Sopenharmony_ci for (i = 0; i < XFS_GETFSMAP_DEVS; i++) { 95762306a36Sopenharmony_ci /* Is this device within the range the user asked for? */ 95862306a36Sopenharmony_ci if (!handlers[i].fn) 95962306a36Sopenharmony_ci continue; 96062306a36Sopenharmony_ci if (head->fmh_keys[0].fmr_device > handlers[i].dev) 96162306a36Sopenharmony_ci continue; 96262306a36Sopenharmony_ci if (head->fmh_keys[1].fmr_device < handlers[i].dev) 96362306a36Sopenharmony_ci break; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci /* 96662306a36Sopenharmony_ci * If this device number matches the high key, we have 96762306a36Sopenharmony_ci * to pass the high key to the handler to limit the 96862306a36Sopenharmony_ci * query results. If the device number exceeds the 96962306a36Sopenharmony_ci * low key, zero out the low key so that we get 97062306a36Sopenharmony_ci * everything from the beginning. 97162306a36Sopenharmony_ci */ 97262306a36Sopenharmony_ci if (handlers[i].dev == head->fmh_keys[1].fmr_device) 97362306a36Sopenharmony_ci dkeys[1] = head->fmh_keys[1]; 97462306a36Sopenharmony_ci if (handlers[i].dev > head->fmh_keys[0].fmr_device) 97562306a36Sopenharmony_ci memset(&dkeys[0], 0, sizeof(struct xfs_fsmap)); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* 97862306a36Sopenharmony_ci * Grab an empty transaction so that we can use its recursive 97962306a36Sopenharmony_ci * buffer locking abilities to detect cycles in the rmapbt 98062306a36Sopenharmony_ci * without deadlocking. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_ci error = xfs_trans_alloc_empty(mp, &tp); 98362306a36Sopenharmony_ci if (error) 98462306a36Sopenharmony_ci break; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci info.dev = handlers[i].dev; 98762306a36Sopenharmony_ci info.last = false; 98862306a36Sopenharmony_ci info.pag = NULL; 98962306a36Sopenharmony_ci info.low_daddr = -1ULL; 99062306a36Sopenharmony_ci info.low.rm_blockcount = 0; 99162306a36Sopenharmony_ci error = handlers[i].fn(tp, dkeys, &info); 99262306a36Sopenharmony_ci if (error) 99362306a36Sopenharmony_ci break; 99462306a36Sopenharmony_ci xfs_trans_cancel(tp); 99562306a36Sopenharmony_ci tp = NULL; 99662306a36Sopenharmony_ci info.next_daddr = 0; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (tp) 100062306a36Sopenharmony_ci xfs_trans_cancel(tp); 100162306a36Sopenharmony_ci head->fmh_oflags = FMH_OF_DEV_T; 100262306a36Sopenharmony_ci return error; 100362306a36Sopenharmony_ci} 1004