@@ -478,6 +478,395 @@ xfs_fsrefs_logdev(
return xfs_fsrefs_helper(tp, info, &frec);
}
+#ifdef CONFIG_XFS_RT
+/* Synthesize fsrefs records from rtbitmap records. */
+STATIC int
+xfs_fsrefs_rtdev_bitmap_helper(
+ struct xfs_rtgroup *rtg,
+ struct xfs_trans *tp,
+ const struct xfs_rtalloc_rec *rec,
+ void *priv)
+{
+ struct xfs_fsrefs_irec frec = {
+ .refcount = 1,
+ };
+ struct xfs_mount *mp = rtg_mount(rtg);
+ struct xfs_fsrefs_info *info = priv;
+ xfs_rtblock_t next_rtb, rec_rtb, rtb;
+ xfs_rgnumber_t next_rgno;
+ xfs_rgblock_t next_rgbno;
+ xfs_rgblock_t rec_rgbno;
+
+ /* Translate the free space record to group and block number. */
+ rec_rtb = xfs_rtx_to_rtb(rtg, rec->ar_startext);
+ rec_rgbno = xfs_rtb_to_rgbno(mp, rec_rtb);
+
+ /*
+ * Figure out if there's a gap between the last fsrefs record we
+ * emitted and this free extent. If there is, report the gap as a
+ * refcount==1 record.
+ */
+ next_rtb = xfs_daddr_to_rtb(mp, info->next_daddr);
+ next_rgno = xfs_rtb_to_rgno(mp, next_rtb);
+ next_rgbno = xfs_rtb_to_rgbno(mp, next_rtb);
+
+ ASSERT(next_rgno >= info->group->xg_gno);
+ ASSERT(rec_rgbno >= next_rgbno);
+
+ /*
+ * If we've already moved on to the next rtgroup, we don't have any
+ * fsrefs records to synthesize.
+ */
+ if (next_rgno > info->group->xg_gno)
+ return 0;
+
+ rtb = xfs_rtx_to_rtb(rtg, rec->ar_startext + rec->ar_extcount);
+ info->next_daddr = xfs_rtb_to_daddr(mp, rtb);
+
+ if (rec_rtb == next_rtb)
+ return 0;
+
+ /* Emit a record for the in-use space. */
+ frec.start_daddr = xfs_rtb_to_daddr(mp, next_rtb);
+ frec.len_daddr = XFS_FSB_TO_BB(mp, rec_rgbno - next_rgbno);
+ frec.rec_key = next_rgbno;
+ return xfs_fsrefs_helper(tp, info, &frec);
+}
+
+/* Emit records to fill a gap in the refcount btree with singly-owned blocks. */
+STATIC int
+xfs_fsrefs_rtdev_fill_refcount_gap(
+ struct xfs_trans *tp,
+ struct xfs_fsrefs_info *info,
+ xfs_rgblock_t rgbno)
+{
+ struct xfs_rtalloc_rec high = { 0 };
+ struct xfs_mount *mp = tp->t_mountp;
+ struct xfs_rtgroup *rtg = to_rtg(info->group);
+ xfs_rtblock_t start_rtbno =
+ xfs_daddr_to_rtb(mp, info->next_daddr);
+ xfs_rtblock_t end_rtbno =
+ xfs_rgbno_to_rtb(rtg, rgbno);
+ xfs_rtxnum_t low_rtx;
+ xfs_daddr_t rec_daddr;
+ int error;
+
+ ASSERT(xfs_rtb_to_rgno(mp, start_rtbno) == info->group->xg_gno);
+
+ low_rtx = xfs_rtb_to_rtx(mp, start_rtbno);
+ if (rgbno == -1U) {
+ /*
+ * If the caller passes in an all 1s high key to signify the
+ * end of the group, set the extent to all 1s as well.
+ */
+ high.ar_startext = -1ULL;
+ } else {
+ high.ar_startext = xfs_rtb_to_rtx(mp,
+ end_rtbno + mp->m_sb.sb_rextsize - 1);
+ }
+ if (low_rtx >= high.ar_startext)
+ return 0;
+
+ error = xfs_rtalloc_query_range(rtg, tp, low_rtx, high.ar_startext,
+ xfs_fsrefs_rtdev_bitmap_helper, info);
+ if (error)
+ return error;
+
+ /*
+ * Synthesize records for single-owner extents between the last
+ * fsrefcount record emitted and the end of the query range.
+ */
+ high.ar_startext = min(high.ar_startext, rtg->rtg_extents);
+ rec_daddr = xfs_rtb_to_daddr(mp, xfs_rtx_to_rtb(rtg, high.ar_startext));
+ if (info->next_daddr > rec_daddr)
+ return 0;
+
+ info->last = true;
+ return xfs_fsrefs_rtdev_bitmap_helper(rtg, tp, &high, info);
+}
+
+/* Transform a absolute-startblock refcount (rtdev, logdev) into a fsrefs */
+STATIC int
+xfs_fsrefs_rtdev_refcountbt_helper(
+ struct xfs_btree_cur *cur,
+ const struct xfs_refcount_irec *rec,
+ void *priv)
+{
+ struct xfs_fsrefs_irec frec = {
+ .refcount = rec->rc_refcount,
+ .rec_key = rec->rc_startblock,
+ };
+ struct xfs_mount *mp = cur->bc_mp;
+ struct xfs_fsrefs_info *info = priv;
+ struct xfs_rtgroup *rtg = to_rtg(info->group);
+ xfs_rtblock_t rec_rtbno;
+ int error;
+
+ /*
+ * Stop once we get to the CoW staging extents; they're all shoved to
+ * the right side of the btree and were already covered by the rtbitmap
+ * scan.
+ */
+ if (rec->rc_domain != XFS_REFC_DOMAIN_SHARED)
+ return -ECANCELED;
+
+ /* Report on any gaps first */
+ error = xfs_fsrefs_rtdev_fill_refcount_gap(cur->bc_tp, info,
+ rec->rc_startblock);
+ if (error)
+ return error;
+
+ /* Report the refcount record from the refcount btree. */
+ rec_rtbno = xfs_rgbno_to_rtb(rtg, rec->rc_startblock);
+ frec.start_daddr = xfs_rtb_to_daddr(mp, rec_rtbno);
+ frec.len_daddr = XFS_FSB_TO_BB(mp, rec->rc_blockcount);
+ info->next_daddr = xfs_rtb_to_daddr(mp, rec_rtbno + rec->rc_blockcount);
+ return xfs_fsrefs_helper(cur->bc_tp, info, &frec);
+}
+
+#define XFS_RTGLOCK_FSREFS (XFS_RTGLOCK_BITMAP | XFS_RTGLOCK_REFCOUNT)
+
+/* Execute a getfsrefs query against the realtime device. */
+STATIC int
+xfs_fsrefs_rtdev(
+ struct xfs_trans *tp,
+ const struct xfs_fsrefs *keys,
+ struct xfs_fsrefs_info *info)
+{
+ struct xfs_mount *mp = tp->t_mountp;
+ struct xfs_rtgroup *rtg = NULL, *locked_rtg = NULL;
+ xfs_rtblock_t start_rtbno;
+ xfs_rtblock_t end_rtbno;
+ xfs_rgnumber_t start_rg;
+ xfs_rgnumber_t end_rg;
+ uint64_t eofs;
+ int error = 0;
+
+ eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks);
+ if (keys[0].fcr_physical >= eofs)
+ return 0;
+ start_rtbno = xfs_daddr_to_rtb(mp, keys[0].fcr_physical);
+ end_rtbno = xfs_daddr_to_rtb(mp, min(eofs - 1, keys[1].fcr_physical));
+
+ info->refc_cur = info->bno_cur = NULL;
+
+ /*
+ * Convert the fsrefs low/high keys to rtgroup based keys. Initialize
+ * low to the fsrefs low key and max out the high key to the end of the
+ * rtgroup.
+ */
+ info->low.rc_startblock = xfs_rtb_to_rgbno(mp, start_rtbno);
+ info->low.rc_blockcount = XFS_BB_TO_FSBT(mp, keys[0].fcr_length);
+ info->low.rc_refcount = 0;
+ info->low.rc_domain = XFS_REFC_DOMAIN_SHARED;
+
+ /* Adjust the low key if we are continuing from where we left off. */
+ if (info->low.rc_blockcount > 0) {
+ info->low.rc_startblock += info->low.rc_blockcount;
+
+ start_rtbno += info->low.rc_blockcount;
+ if (xfs_rtb_to_daddr(mp, start_rtbno) >= eofs)
+ return 0;
+ }
+
+ info->high.rc_startblock = -1U;
+ info->high.rc_blockcount = 0;
+ info->high.rc_refcount = 0;
+ info->high.rc_domain = XFS_REFC_DOMAIN_SHARED;
+
+ start_rg = xfs_rtb_to_rgno(mp, start_rtbno);
+ end_rg = xfs_rtb_to_rgno(mp, end_rtbno);
+
+ /* Query each rtgroup */
+ while ((rtg = xfs_rtgroup_next_range(mp, rtg, start_rg, end_rg))) {
+ info->group = rtg_group(rtg);
+
+ /*
+ * Set the rtgroup high key from the fsrefs high key if this
+ * is the last rtgroup that we're querying.
+ */
+ if (rtg_rgno(rtg) == end_rg)
+ info->high.rc_startblock = xfs_rtb_to_rgbno(mp,
+ end_rtbno);
+
+ if (info->refc_cur) {
+ xfs_btree_del_cursor(info->refc_cur, XFS_BTREE_NOERROR);
+ info->refc_cur = NULL;
+ }
+ if (locked_rtg)
+ xfs_rtgroup_unlock(locked_rtg, XFS_RTGLOCK_FSREFS);
+
+ trace_xfs_fsrefs_low_group_key(mp, info->dev, info->group,
+ &info->low);
+ trace_xfs_fsrefs_high_group_key(mp, info->dev, info->group,
+ &info->high);
+
+ xfs_rtgroup_lock(rtg, XFS_RTGLOCK_FSREFS);
+ locked_rtg = rtg;
+
+ /*
+ * Fill the query with refcount records and synthesize
+ * singly-owned block records from free space data.
+ */
+ if (xfs_has_rtreflink(mp)) {
+ info->refc_cur = xfs_rtrefcountbt_init_cursor(tp, rtg);
+
+ error = xfs_refcount_query_range(info->refc_cur,
+ &info->low, &info->high,
+ xfs_fsrefs_rtdev_refcountbt_helper,
+ info);
+ if (error && error != -ECANCELED)
+ break;
+ }
+
+ /*
+ * Synthesize refcount==1 records from the free space data
+ * between the end of the last fsrefs record reported and the
+ * end of the range. If we don't have refcount support, the
+ * starting point will be the start of the query range.
+ */
+ error = xfs_fsrefs_rtdev_fill_refcount_gap(tp, info,
+ info->high.rc_startblock);
+ if (error)
+ break;
+
+ /*
+ * Set the rtgroup low key to the start of the rtgroup prior to
+ * moving on to the next rtgroup.
+ */
+ if (rtg_rgno(rtg) == start_rg)
+ memset(&info->low, 0, sizeof(info->low));
+ info->group = NULL;
+ }
+
+ if (info->refc_cur) {
+ xfs_btree_del_cursor(info->refc_cur, error);
+ info->refc_cur = NULL;
+ }
+ if (locked_rtg)
+ xfs_rtgroup_unlock(locked_rtg, XFS_RTGLOCK_FSREFS);
+ if (info->group) {
+ xfs_rtgroup_rele(rtg);
+ info->group = NULL;
+ } else if (rtg) {
+ /* loop termination case */
+ xfs_rtgroup_rele(rtg);
+ }
+
+ return error;
+}
+
+/* Synthesize fsrefs records from 64-bit rtbitmap records. */
+STATIC int
+xfs_fsrefs_rtdev_nogroups_helper(
+ struct xfs_rtgroup *rtg,
+ struct xfs_trans *tp,
+ const struct xfs_rtalloc_rec *rec,
+ void *priv)
+{
+ struct xfs_fsrefs_irec frec = {
+ .refcount = 1,
+ };
+ struct xfs_mount *mp = rtg_mount(rtg);
+ struct xfs_fsrefs_info *info = priv;
+ xfs_rtblock_t next_rtb, rec_rtb, rtb;
+
+ /* Translate the free space record to group and block number. */
+ rec_rtb = xfs_rtx_to_rtb(rtg, rec->ar_startext);
+
+ /*
+ * Figure out if there's a gap between the last fsrefs record we
+ * emitted and this free extent. If there is, report the gap as a
+ * refcount==1 record.
+ */
+ next_rtb = xfs_daddr_to_rtb(mp, info->next_daddr);
+
+ ASSERT(rec_rtb >= next_rtb);
+
+ rtb = xfs_rtx_to_rtb(rtg, rec->ar_startext + rec->ar_extcount);
+ info->next_daddr = xfs_rtb_to_daddr(mp, rtb);
+
+ if (rec_rtb == next_rtb)
+ return 0;
+
+ /* Emit records for the in-use space. */
+ frec.start_daddr = xfs_rtb_to_daddr(mp, next_rtb);
+ frec.len_daddr = xfs_rtb_to_daddr(mp, rec_rtb - next_rtb);
+ return xfs_fsrefs_helper(tp, info, &frec);
+}
+
+/*
+ * Synthesize refcount information from the rtbitmap for a pre-rtgroups
+ * filesystem.
+ */
+STATIC int
+xfs_fsrefs_rtdev_nogroups(
+ struct xfs_trans *tp,
+ const struct xfs_fsrefs *keys,
+ struct xfs_fsrefs_info *info)
+{
+ struct xfs_mount *mp = tp->t_mountp;
+ struct xfs_rtgroup *rtg = NULL;
+ xfs_rtblock_t start_rtbno;
+ xfs_rtblock_t end_rtbno;
+ xfs_rtxnum_t low_rtx;
+ xfs_rtxnum_t high_rtx;
+ uint64_t eofs;
+ int error = 0;
+
+ eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks);
+ if (keys[0].fcr_physical >= eofs)
+ return 0;
+ start_rtbno = xfs_daddr_to_rtb(mp, keys[0].fcr_physical);
+ end_rtbno = xfs_daddr_to_rtb(mp, min(eofs - 1, keys[1].fcr_physical));
+
+ info->refc_cur = info->bno_cur = NULL;
+
+ /*
+ * Convert the fsrefs low/high keys to rtgroup based keys. Initialize
+ * low to the fsrefs low key and max out the high key to the end of the
+ * rtgroup.
+ */
+ info->low_daddr = keys[0].fcr_physical;
+
+ /* Adjust the low key if we are continuing from where we left off. */
+ if (keys[0].fcr_length > 0) {
+ info->low_daddr += keys[0].fcr_length;
+ if (info->low_daddr >= eofs)
+ return 0;
+ }
+
+ rtg = xfs_rtgroup_grab(mp, 0);
+ if (!rtg)
+ return -EFSCORRUPTED;
+
+ info->group = rtg_group(rtg);
+
+ trace_xfs_fsrefs_low_linear_key(mp, info->dev, start_rtbno);
+ trace_xfs_fsrefs_high_linear_key(mp, info->dev, end_rtbno);
+
+ xfs_rtgroup_lock(rtg, XFS_RTGLOCK_BITMAP);
+
+ /*
+ * Walk the whole rtbitmap. Without rtgroups, the startext values can
+ * be more than 32-bits wide, which is why we need this separate
+ * implementation.
+ */
+ low_rtx = xfs_rtb_to_rtx(mp, start_rtbno);
+ high_rtx = xfs_rtb_to_rtx(mp, end_rtbno + mp->m_sb.sb_rextsize - 1);
+ if (low_rtx < high_rtx)
+ error = xfs_rtalloc_query_range(rtg, tp, low_rtx, high_rtx,
+ xfs_fsrefs_rtdev_nogroups_helper, info);
+
+ info->group = NULL;
+
+ xfs_rtgroup_unlock(rtg, XFS_RTGLOCK_BITMAP);
+ xfs_rtgroup_rele(rtg);
+
+ return error;
+}
+#endif
+
/* Do we recognize the device? */
STATIC bool
xfs_fsrefs_is_valid_device(
@@ -515,7 +904,14 @@ xfs_fsrefs_check_keys(
return false;
}
+/*
+ * There are only two devices if we didn't configure RT devices at build time.
+ */
+#ifdef CONFIG_XFS_RT
+#define XFS_GETFSREFS_DEVS 3
+#else
#define XFS_GETFSREFS_DEVS 2
+#endif /* CONFIG_XFS_RT */
/*
* Get filesystem's extent refcounts as described in head, and format for
@@ -569,6 +965,15 @@ xfs_getfsrefs(
handlers[1].dev = new_encode_dev(mp->m_logdev_targp->bt_dev);
handlers[1].fn = xfs_fsrefs_logdev;
}
+#ifdef CONFIG_XFS_RT
+ if (mp->m_rtdev_targp) {
+ handlers[2].dev = new_encode_dev(mp->m_rtdev_targp->bt_dev);
+ if (xfs_has_rtgroups(mp))
+ handlers[2].fn = xfs_fsrefs_rtdev;
+ else
+ handlers[2].fn = xfs_fsrefs_rtdev_nogroups;
+ }
+#endif /* CONFIG_XFS_RT */
xfs_sort(handlers, XFS_GETFSREFS_DEVS, sizeof(struct xfs_fsrefs_dev),
xfs_fsrefs_dev_compare);