Skip to content

Commit ca6448a

Browse files
Zizhi WoChandan Babu R
authored andcommitted
xfs: Fix missing interval for missing_owner in xfs fsmap
In the fsmap query of xfs, there is an interval missing problem: [root@fedora ~]# xfs_io -c 'fsmap -vvvv' /mnt EXT: DEV BLOCK-RANGE OWNER FILE-OFFSET AG AG-OFFSET TOTAL 0: 253:16 [0..7]: static fs metadata 0 (0..7) 8 1: 253:16 [8..23]: per-AG metadata 0 (8..23) 16 2: 253:16 [24..39]: inode btree 0 (24..39) 16 3: 253:16 [40..47]: per-AG metadata 0 (40..47) 8 4: 253:16 [48..55]: refcount btree 0 (48..55) 8 5: 253:16 [56..103]: per-AG metadata 0 (56..103) 48 6: 253:16 [104..127]: free space 0 (104..127) 24 ...... BUG: [root@fedora ~]# xfs_io -c 'fsmap -vvvv -d 104 107' /mnt [root@fedora ~]# Normally, we should be able to get [104, 107), but we got nothing. The problem is caused by shifting. The query for the problem-triggered scenario is for the missing_owner interval (e.g. freespace in rmapbt/ unknown space in bnobt), which is obtained by subtraction (gap). For this scenario, the interval is obtained by info->last. However, rec_daddr is calculated based on the start_block recorded in key[1], which is converted by calling XFS_BB_TO_FSBT. Then if rec_daddr does not exceed info->next_daddr, which means keys[1].fmr_physical >> (mp)->m_blkbb_log <= info->next_daddr, no records will be displayed. In the above example, 104 >> (mp)->m_blkbb_log = 12 and 107 >> (mp)->m_blkbb_log = 12, so the two are reduced to 0 and the gap is ignored: before calculate ----------------> after shifting 104(st) 107(ed) 12(st/ed) |---------| | sector size block size Resolve this issue by introducing the "end_daddr" field in xfs_getfsmap_info. This records |key[1].fmr_physical + key[1].length| at the granularity of sector. If the current query is the last, the rec_daddr is end_daddr to prevent missing interval problems caused by shifting. We only need to focus on the last query, because xfs disks are internally aligned with disk blocksize that are powers of two and minimum 512, so there is no problem with shifting in previous queries. After applying this patch, the above problem have been solved: [root@fedora ~]# xfs_io -c 'fsmap -vvvv -d 104 107' /mnt EXT: DEV BLOCK-RANGE OWNER FILE-OFFSET AG AG-OFFSET TOTAL 0: 253:16 [104..106]: free space 0 (104..106) 3 Fixes: e89c041 ("xfs: implement the GETFSMAP ioctl") Signed-off-by: Zizhi Wo <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> [djwong: limit the range of end_addr correctly] Signed-off-by: Darrick J. Wong <[email protected]> Signed-off-by: Chandan Babu R <[email protected]>
1 parent 6b35cc8 commit ca6448a

File tree

1 file changed

+23
-1
lines changed

1 file changed

+23
-1
lines changed

fs/xfs/xfs_fsmap.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ struct xfs_getfsmap_info {
162162
xfs_daddr_t next_daddr; /* next daddr we expect */
163163
/* daddr of low fsmap key when we're using the rtbitmap */
164164
xfs_daddr_t low_daddr;
165+
xfs_daddr_t end_daddr; /* daddr of high fsmap key */
165166
u64 missing_owner; /* owner of holes */
166167
u32 dev; /* device id */
167168
/*
@@ -182,6 +183,7 @@ struct xfs_getfsmap_dev {
182183
int (*fn)(struct xfs_trans *tp,
183184
const struct xfs_fsmap *keys,
184185
struct xfs_getfsmap_info *info);
186+
sector_t nr_sectors;
185187
};
186188

187189
/* Compare two getfsmap device handlers. */
@@ -294,6 +296,18 @@ xfs_getfsmap_helper(
294296
return 0;
295297
}
296298

299+
/*
300+
* For an info->last query, we're looking for a gap between the last
301+
* mapping emitted and the high key specified by userspace. If the
302+
* user's query spans less than 1 fsblock, then info->high and
303+
* info->low will have the same rm_startblock, which causes rec_daddr
304+
* and next_daddr to be the same. Therefore, use the end_daddr that
305+
* we calculated from userspace's high key to synthesize the record.
306+
* Note that if the btree query found a mapping, there won't be a gap.
307+
*/
308+
if (info->last && info->end_daddr != XFS_BUF_DADDR_NULL)
309+
rec_daddr = info->end_daddr;
310+
297311
/* Are we just counting mappings? */
298312
if (info->head->fmh_count == 0) {
299313
if (info->head->fmh_entries == UINT_MAX)
@@ -904,17 +918,21 @@ xfs_getfsmap(
904918

905919
/* Set up our device handlers. */
906920
memset(handlers, 0, sizeof(handlers));
921+
handlers[0].nr_sectors = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
907922
handlers[0].dev = new_encode_dev(mp->m_ddev_targp->bt_dev);
908923
if (use_rmap)
909924
handlers[0].fn = xfs_getfsmap_datadev_rmapbt;
910925
else
911926
handlers[0].fn = xfs_getfsmap_datadev_bnobt;
912927
if (mp->m_logdev_targp != mp->m_ddev_targp) {
928+
handlers[1].nr_sectors = XFS_FSB_TO_BB(mp,
929+
mp->m_sb.sb_logblocks);
913930
handlers[1].dev = new_encode_dev(mp->m_logdev_targp->bt_dev);
914931
handlers[1].fn = xfs_getfsmap_logdev;
915932
}
916933
#ifdef CONFIG_XFS_RT
917934
if (mp->m_rtdev_targp) {
935+
handlers[2].nr_sectors = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks);
918936
handlers[2].dev = new_encode_dev(mp->m_rtdev_targp->bt_dev);
919937
handlers[2].fn = xfs_getfsmap_rtdev_rtbitmap;
920938
}
@@ -946,6 +964,7 @@ xfs_getfsmap(
946964

947965
info.next_daddr = head->fmh_keys[0].fmr_physical +
948966
head->fmh_keys[0].fmr_length;
967+
info.end_daddr = XFS_BUF_DADDR_NULL;
949968
info.fsmap_recs = fsmap_recs;
950969
info.head = head;
951970

@@ -966,8 +985,11 @@ xfs_getfsmap(
966985
* low key, zero out the low key so that we get
967986
* everything from the beginning.
968987
*/
969-
if (handlers[i].dev == head->fmh_keys[1].fmr_device)
988+
if (handlers[i].dev == head->fmh_keys[1].fmr_device) {
970989
dkeys[1] = head->fmh_keys[1];
990+
info.end_daddr = min(handlers[i].nr_sectors - 1,
991+
dkeys[1].fmr_physical);
992+
}
971993
if (handlers[i].dev > head->fmh_keys[0].fmr_device)
972994
memset(&dkeys[0], 0, sizeof(struct xfs_fsmap));
973995

0 commit comments

Comments
 (0)