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