#endif
        return error;
 }
+
+struct xfs_group *
+xfs_group_get_by_fsb(
+       struct xfs_mount        *mp,
+       xfs_fsblock_t           fsbno,
+       enum xfs_group_type     type)
+{
+       return xfs_group_get(mp, xfs_fsb_to_gno(mp, fsbno, type), type);
+}
 
 
 struct xfs_group *xfs_group_get(struct xfs_mount *mp, uint32_t index,
                enum xfs_group_type type);
+struct xfs_group *xfs_group_get_by_fsb(struct xfs_mount *mp,
+               xfs_fsblock_t fsbno, enum xfs_group_type type);
 struct xfs_group *xfs_group_hold(struct xfs_group *xg);
 void xfs_group_put(struct xfs_group *xg);
 
 #define xfs_group_marked(_mp, _type, _mark) \
        xa_marked(&(_mp)->m_groups[(_type)].xa, (_mark))
 
+static inline xfs_agblock_t
+xfs_group_max_blocks(
+       struct xfs_group        *xg)
+{
+       return xg->xg_mount->m_groups[xg->xg_type].blocks;
+}
+
+static inline xfs_fsblock_t
+xfs_group_start_fsb(
+       struct xfs_group        *xg)
+{
+       return ((xfs_fsblock_t)xg->xg_gno) <<
+               xg->xg_mount->m_groups[xg->xg_type].blklog;
+}
+
+static inline xfs_fsblock_t
+xfs_gbno_to_fsb(
+       struct xfs_group        *xg,
+       xfs_agblock_t           gbno)
+{
+       return xfs_group_start_fsb(xg) | gbno;
+}
+
+static inline xfs_daddr_t
+xfs_gbno_to_daddr(
+       struct xfs_group        *xg,
+       xfs_agblock_t           gbno)
+{
+       struct xfs_mount        *mp = xg->xg_mount;
+       uint32_t                blocks = mp->m_groups[xg->xg_type].blocks;
+
+       return XFS_FSB_TO_BB(mp, (xfs_fsblock_t)xg->xg_gno * blocks + gbno);
+}
+
+static inline uint32_t
+xfs_fsb_to_gno(
+       struct xfs_mount        *mp,
+       xfs_fsblock_t           fsbno,
+       enum xfs_group_type     type)
+{
+       if (!mp->m_groups[type].blklog)
+               return 0;
+       return fsbno >> mp->m_groups[type].blklog;
+}
+
+static inline xfs_agblock_t
+xfs_fsb_to_gbno(
+       struct xfs_mount        *mp,
+       xfs_fsblock_t           fsbno,
+       enum xfs_group_type     type)
+{
+       return fsbno & mp->m_groups[type].blkmask;
+}
+
 #endif /* __LIBXFS_GROUP_H */
 
        struct xfs_mount        *mp,
        struct xfs_sb           *sbp)
 {
+       struct xfs_groups       *ags = &mp->m_groups[XG_TYPE_AG];
+
        mp->m_agfrotor = 0;
        atomic_set(&mp->m_agirotor, 0);
        mp->m_maxagi = mp->m_sb.sb_agcount;
        mp->m_blockmask = sbp->sb_blocksize - 1;
        mp->m_blockwsize = sbp->sb_blocksize >> XFS_WORDLOG;
        mp->m_blockwmask = mp->m_blockwsize - 1;
+
+       ags->blocks = mp->m_sb.sb_agblocks;
+       ags->blklog = mp->m_sb.sb_agblklog;
+       ags->blkmask = xfs_mask32lo(mp->m_sb.sb_agblklog);
+
        xfs_mount_sb_set_rextsize(mp, sbp);
 
        mp->m_alloc_mxr[0] = xfs_allocbt_maxrecs(mp, sbp->sb_blocksize, true);
 
        unsigned int            cpu;
 };
 
+/*
+ * Container for each type of groups, used to look up individual groups and
+ * describes the geometry.
+ */
 struct xfs_groups {
        struct xarray           xa;
+
+       /*
+        * Maximum capacity of the group in FSBs.
+        *
+        * Each group is laid out densely in the daddr space.  For the
+        * degenerate case of a pre-rtgroups filesystem, the incore rtgroup
+        * pretends to have a zero-block and zero-blklog rtgroup.
+        */
+       uint32_t                blocks;
+
+       /*
+        * Log(2) of the logical size of each group.
+        *
+        * Compared to the blocks field above this is rounded up to the next
+        * power of two, and thus lays out the xfs_fsblock_t/xfs_rtblock_t
+        * space sparsely with a hole from blocks to (1 << blklog) at the end
+        * of each group.
+        */
+       uint8_t                 blklog;
+
+       /*
+        * Mask to extract the group-relative block number from a FSB.
+        * For a pre-rtgroups filesystem we pretend to have one very large
+        * rtgroup, so this mask must be 64-bit.
+        */
+       uint64_t                blkmask;
 };
 
 /*