diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2019-02-01 12:08:51 -0500 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2019-02-11 19:06:39 -0500 |
commit | b9454fe056bda3b208225e4ac76bcc7c912f350a (patch) | |
tree | 692d4f1aae2cf6f37f868ced8eae43994c17a64d | |
parent | a1954242facb01350265183675f1a544e1e1e646 (diff) |
xfs: clean up the inode cluster checking in the inobt scrub
The code to check inobt records against inode clusters is a mess of
poorly named variables and unnecessary parameters. Clean the
unnecessary inode number parameters out of _check_cluster_freemask in
favor of computing them inside the function instead of making the caller
do it. In xchk_iallocbt_check_cluster, rename the variables to make it
more obvious just what chunk_ino and cluster_ino represent.
Add a tracepoint to make it easier to track each inode cluster as we
scrub it.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
-rw-r--r-- | fs/xfs/scrub/ialloc.c | 165 | ||||
-rw-r--r-- | fs/xfs/scrub/trace.h | 45 |
2 files changed, 152 insertions, 58 deletions
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c index 0ce793d92995..708f6607db71 100644 --- a/fs/xfs/scrub/ialloc.c +++ b/fs/xfs/scrub/ialloc.c | |||
@@ -134,41 +134,69 @@ xchk_iallocbt_freecount( | |||
134 | return hweight64(freemask); | 134 | return hweight64(freemask); |
135 | } | 135 | } |
136 | 136 | ||
137 | /* Check a particular inode with ir_free. */ | 137 | /* |
138 | * Check that an inode's allocation status matches ir_free in the inobt | ||
139 | * record. First we try querying the in-core inode state, and if the inode | ||
140 | * isn't loaded we examine the on-disk inode directly. | ||
141 | * | ||
142 | * Since there can be 1:M and M:1 mappings between inobt records and inode | ||
143 | * clusters, we pass in the inode location information as an inobt record; | ||
144 | * the index of an inode cluster within the inobt record (as well as the | ||
145 | * cluster buffer itself); and the index of the inode within the cluster. | ||
146 | * | ||
147 | * @irec is the inobt record. | ||
148 | * @cluster_base is the inode offset of the cluster within the @irec. | ||
149 | * @cluster_bp is the cluster buffer. | ||
150 | * @cluster_index is the inode offset within the inode cluster. | ||
151 | */ | ||
138 | STATIC int | 152 | STATIC int |
139 | xchk_iallocbt_check_cluster_freemask( | 153 | xchk_iallocbt_check_cluster_ifree( |
140 | struct xchk_btree *bs, | 154 | struct xchk_btree *bs, |
141 | xfs_ino_t fsino, | ||
142 | xfs_agino_t chunkino, | ||
143 | xfs_agino_t clusterino, | ||
144 | struct xfs_inobt_rec_incore *irec, | 155 | struct xfs_inobt_rec_incore *irec, |
145 | struct xfs_buf *bp) | 156 | unsigned int cluster_base, |
157 | struct xfs_buf *cluster_bp, | ||
158 | unsigned int cluster_index) | ||
146 | { | 159 | { |
147 | struct xfs_dinode *dip; | ||
148 | struct xfs_mount *mp = bs->cur->bc_mp; | 160 | struct xfs_mount *mp = bs->cur->bc_mp; |
149 | bool inode_is_free = false; | 161 | struct xfs_dinode *dip; |
162 | xfs_ino_t fsino; | ||
163 | xfs_agino_t agino; | ||
164 | unsigned int offset; | ||
165 | bool irec_free; | ||
166 | bool ino_inuse; | ||
150 | bool freemask_ok; | 167 | bool freemask_ok; |
151 | bool inuse; | 168 | int error; |
152 | int error = 0; | ||
153 | 169 | ||
154 | if (xchk_should_terminate(bs->sc, &error)) | 170 | if (xchk_should_terminate(bs->sc, &error)) |
155 | return error; | 171 | return error; |
156 | 172 | ||
157 | dip = xfs_buf_offset(bp, clusterino * mp->m_sb.sb_inodesize); | 173 | /* |
174 | * Given an inobt record, an offset of a cluster within the record, and | ||
175 | * an offset of an inode within a cluster, compute which fs inode we're | ||
176 | * talking about and the offset of that inode within the buffer. | ||
177 | */ | ||
178 | agino = irec->ir_startino + cluster_base + cluster_index; | ||
179 | fsino = XFS_AGINO_TO_INO(mp, bs->cur->bc_private.a.agno, agino); | ||
180 | offset = cluster_index * mp->m_sb.sb_inodesize; | ||
181 | if (offset >= BBTOB(cluster_bp->b_length)) { | ||
182 | xchk_btree_set_corrupt(bs->sc, bs->cur, 0); | ||
183 | goto out; | ||
184 | } | ||
185 | dip = xfs_buf_offset(cluster_bp, offset); | ||
186 | irec_free = (irec->ir_free & XFS_INOBT_MASK(cluster_base + | ||
187 | cluster_index)); | ||
188 | |||
158 | if (be16_to_cpu(dip->di_magic) != XFS_DINODE_MAGIC || | 189 | if (be16_to_cpu(dip->di_magic) != XFS_DINODE_MAGIC || |
159 | (dip->di_version >= 3 && | 190 | (dip->di_version >= 3 && be64_to_cpu(dip->di_ino) != fsino)) { |
160 | be64_to_cpu(dip->di_ino) != fsino + clusterino)) { | ||
161 | xchk_btree_set_corrupt(bs->sc, bs->cur, 0); | 191 | xchk_btree_set_corrupt(bs->sc, bs->cur, 0); |
162 | goto out; | 192 | goto out; |
163 | } | 193 | } |
164 | 194 | ||
165 | if (irec->ir_free & XFS_INOBT_MASK(chunkino + clusterino)) | 195 | error = xfs_icache_inode_is_allocated(mp, bs->cur->bc_tp, fsino, |
166 | inode_is_free = true; | 196 | &ino_inuse); |
167 | error = xfs_icache_inode_is_allocated(mp, bs->cur->bc_tp, | ||
168 | fsino + clusterino, &inuse); | ||
169 | if (error == -ENODATA) { | 197 | if (error == -ENODATA) { |
170 | /* Not cached, just read the disk buffer */ | 198 | /* Not cached, just read the disk buffer */ |
171 | freemask_ok = inode_is_free ^ !!(dip->di_mode); | 199 | freemask_ok = irec_free ^ !!(dip->di_mode); |
172 | if (!bs->sc->try_harder && !freemask_ok) | 200 | if (!bs->sc->try_harder && !freemask_ok) |
173 | return -EDEADLOCK; | 201 | return -EDEADLOCK; |
174 | } else if (error < 0) { | 202 | } else if (error < 0) { |
@@ -180,7 +208,7 @@ xchk_iallocbt_check_cluster_freemask( | |||
180 | goto out; | 208 | goto out; |
181 | } else { | 209 | } else { |
182 | /* Inode is all there. */ | 210 | /* Inode is all there. */ |
183 | freemask_ok = inode_is_free ^ inuse; | 211 | freemask_ok = irec_free ^ ino_inuse; |
184 | } | 212 | } |
185 | if (!freemask_ok) | 213 | if (!freemask_ok) |
186 | xchk_btree_set_corrupt(bs->sc, bs->cur, 0); | 214 | xchk_btree_set_corrupt(bs->sc, bs->cur, 0); |
@@ -188,43 +216,57 @@ out: | |||
188 | return 0; | 216 | return 0; |
189 | } | 217 | } |
190 | 218 | ||
191 | /* Check an inode cluster. */ | 219 | /* |
220 | * Check that the holemask and freemask of a hypothetical inode cluster match | ||
221 | * what's actually on disk. If sparse inodes are enabled, the cluster does | ||
222 | * not actually have to map to inodes if the corresponding holemask bit is set. | ||
223 | * | ||
224 | * @cluster_base is the first inode in the cluster within the @irec. | ||
225 | */ | ||
192 | STATIC int | 226 | STATIC int |
193 | xchk_iallocbt_check_cluster( | 227 | xchk_iallocbt_check_cluster( |
194 | struct xchk_btree *bs, | 228 | struct xchk_btree *bs, |
195 | struct xfs_inobt_rec_incore *irec, | 229 | struct xfs_inobt_rec_incore *irec, |
196 | xfs_agino_t agino) | 230 | unsigned int cluster_base) |
197 | { | 231 | { |
198 | struct xfs_imap imap; | 232 | struct xfs_imap imap; |
199 | struct xfs_mount *mp = bs->cur->bc_mp; | 233 | struct xfs_mount *mp = bs->cur->bc_mp; |
200 | struct xfs_dinode *dip; | 234 | struct xfs_dinode *dip; |
201 | struct xfs_buf *bp; | 235 | struct xfs_buf *cluster_bp; |
202 | xfs_ino_t fsino; | ||
203 | unsigned int nr_inodes; | 236 | unsigned int nr_inodes; |
204 | xfs_agino_t chunkino; | 237 | xfs_agnumber_t agno = bs->cur->bc_private.a.agno; |
205 | xfs_agino_t clusterino; | ||
206 | xfs_agblock_t agbno; | 238 | xfs_agblock_t agbno; |
207 | uint16_t holemask; | 239 | unsigned int cluster_index; |
240 | uint16_t cluster_mask = 0; | ||
208 | uint16_t ir_holemask; | 241 | uint16_t ir_holemask; |
209 | int error = 0; | 242 | int error = 0; |
210 | 243 | ||
211 | /* Make sure the freemask matches the inode records. */ | ||
212 | nr_inodes = min_t(unsigned int, XFS_INODES_PER_CHUNK, | 244 | nr_inodes = min_t(unsigned int, XFS_INODES_PER_CHUNK, |
213 | mp->m_inodes_per_cluster); | 245 | mp->m_inodes_per_cluster); |
214 | 246 | ||
215 | fsino = XFS_AGINO_TO_INO(mp, bs->cur->bc_private.a.agno, agino); | 247 | /* Map this inode cluster */ |
216 | chunkino = agino - irec->ir_startino; | 248 | agbno = XFS_AGINO_TO_AGBNO(mp, irec->ir_startino + cluster_base); |
217 | agbno = XFS_AGINO_TO_AGBNO(mp, agino); | ||
218 | 249 | ||
219 | /* Compute the holemask mask for this cluster. */ | 250 | /* Compute a bitmask for this cluster that can be used for holemask. */ |
220 | for (clusterino = 0, holemask = 0; clusterino < nr_inodes; | 251 | for (cluster_index = 0; |
221 | clusterino += XFS_INODES_PER_HOLEMASK_BIT) | 252 | cluster_index < nr_inodes; |
222 | holemask |= XFS_INOBT_MASK((chunkino + clusterino) / | 253 | cluster_index += XFS_INODES_PER_HOLEMASK_BIT) |
254 | cluster_mask |= XFS_INOBT_MASK((cluster_base + cluster_index) / | ||
223 | XFS_INODES_PER_HOLEMASK_BIT); | 255 | XFS_INODES_PER_HOLEMASK_BIT); |
224 | 256 | ||
257 | ir_holemask = (irec->ir_holemask & cluster_mask); | ||
258 | imap.im_blkno = XFS_AGB_TO_DADDR(mp, agno, agbno); | ||
259 | imap.im_len = XFS_FSB_TO_BB(mp, mp->m_blocks_per_cluster); | ||
260 | imap.im_boffset = 0; | ||
261 | |||
262 | trace_xchk_iallocbt_check_cluster(mp, agno, irec->ir_startino, | ||
263 | imap.im_blkno, imap.im_len, cluster_base, nr_inodes, | ||
264 | cluster_mask, ir_holemask, | ||
265 | XFS_INO_TO_OFFSET(mp, irec->ir_startino + | ||
266 | cluster_base)); | ||
267 | |||
225 | /* The whole cluster must be a hole or not a hole. */ | 268 | /* The whole cluster must be a hole or not a hole. */ |
226 | ir_holemask = (irec->ir_holemask & holemask); | 269 | if (ir_holemask != cluster_mask && ir_holemask != 0) { |
227 | if (ir_holemask != holemask && ir_holemask != 0) { | ||
228 | xchk_btree_set_corrupt(bs->sc, bs->cur, 0); | 270 | xchk_btree_set_corrupt(bs->sc, bs->cur, 0); |
229 | return 0; | 271 | return 0; |
230 | } | 272 | } |
@@ -241,40 +283,47 @@ xchk_iallocbt_check_cluster( | |||
241 | &XFS_RMAP_OINFO_INODES); | 283 | &XFS_RMAP_OINFO_INODES); |
242 | 284 | ||
243 | /* Grab the inode cluster buffer. */ | 285 | /* Grab the inode cluster buffer. */ |
244 | imap.im_blkno = XFS_AGB_TO_DADDR(mp, bs->cur->bc_private.a.agno, agbno); | 286 | error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap, &dip, &cluster_bp, |
245 | imap.im_len = XFS_FSB_TO_BB(mp, mp->m_blocks_per_cluster); | 287 | 0, 0); |
246 | imap.im_boffset = 0; | ||
247 | |||
248 | error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap, &dip, &bp, 0, 0); | ||
249 | if (!xchk_btree_xref_process_error(bs->sc, bs->cur, 0, &error)) | 288 | if (!xchk_btree_xref_process_error(bs->sc, bs->cur, 0, &error)) |
250 | return 0; | 289 | return error; |
251 | 290 | ||
252 | /* Which inodes are free? */ | 291 | /* Check free status of each inode within this cluster. */ |
253 | for (clusterino = 0; clusterino < nr_inodes; clusterino++) { | 292 | for (cluster_index = 0; cluster_index < nr_inodes; cluster_index++) { |
254 | error = xchk_iallocbt_check_cluster_freemask(bs, fsino, | 293 | error = xchk_iallocbt_check_cluster_ifree(bs, irec, |
255 | chunkino, clusterino, irec, bp); | 294 | cluster_base, cluster_bp, cluster_index); |
256 | if (error) | 295 | if (error) |
257 | break; | 296 | break; |
258 | } | 297 | } |
259 | 298 | ||
260 | xfs_trans_brelse(bs->cur->bc_tp, bp); | 299 | xfs_trans_brelse(bs->cur->bc_tp, cluster_bp); |
261 | return error; | 300 | return error; |
262 | } | 301 | } |
263 | 302 | ||
264 | /* Make sure the free mask is consistent with what the inodes think. */ | 303 | /* |
304 | * For all the inode clusters that could map to this inobt record, make sure | ||
305 | * that the holemask makes sense and that the allocation status of each inode | ||
306 | * matches the freemask. | ||
307 | */ | ||
265 | STATIC int | 308 | STATIC int |
266 | xchk_iallocbt_check_freemask( | 309 | xchk_iallocbt_check_clusters( |
267 | struct xchk_btree *bs, | 310 | struct xchk_btree *bs, |
268 | struct xfs_inobt_rec_incore *irec) | 311 | struct xfs_inobt_rec_incore *irec) |
269 | { | 312 | { |
270 | struct xfs_mount *mp = bs->cur->bc_mp; | 313 | unsigned int cluster_base; |
271 | xfs_agino_t agino; | ||
272 | int error = 0; | 314 | int error = 0; |
273 | 315 | ||
274 | for (agino = irec->ir_startino; | 316 | /* |
275 | agino < irec->ir_startino + XFS_INODES_PER_CHUNK; | 317 | * For the common case where this inobt record maps to multiple inode |
276 | agino += mp->m_inodes_per_cluster) { | 318 | * clusters this will call _check_cluster for each cluster. |
277 | error = xchk_iallocbt_check_cluster(bs, irec, agino); | 319 | * |
320 | * For the case that multiple inobt records map to a single cluster, | ||
321 | * this will call _check_cluster once. | ||
322 | */ | ||
323 | for (cluster_base = 0; | ||
324 | cluster_base < XFS_INODES_PER_CHUNK; | ||
325 | cluster_base += bs->sc->mp->m_inodes_per_cluster) { | ||
326 | error = xchk_iallocbt_check_cluster(bs, irec, cluster_base); | ||
278 | if (error) | 327 | if (error) |
279 | break; | 328 | break; |
280 | } | 329 | } |
@@ -415,7 +464,7 @@ xchk_iallocbt_rec( | |||
415 | 464 | ||
416 | if (!xchk_iallocbt_chunk(bs, &irec, agino, len)) | 465 | if (!xchk_iallocbt_chunk(bs, &irec, agino, len)) |
417 | goto out; | 466 | goto out; |
418 | goto check_freemask; | 467 | goto check_clusters; |
419 | } | 468 | } |
420 | 469 | ||
421 | /* Check each chunk of a sparse inode cluster. */ | 470 | /* Check each chunk of a sparse inode cluster. */ |
@@ -441,8 +490,8 @@ xchk_iallocbt_rec( | |||
441 | holecount + irec.ir_count != XFS_INODES_PER_CHUNK) | 490 | holecount + irec.ir_count != XFS_INODES_PER_CHUNK) |
442 | xchk_btree_set_corrupt(bs->sc, bs->cur, 0); | 491 | xchk_btree_set_corrupt(bs->sc, bs->cur, 0); |
443 | 492 | ||
444 | check_freemask: | 493 | check_clusters: |
445 | error = xchk_iallocbt_check_freemask(bs, &irec); | 494 | error = xchk_iallocbt_check_clusters(bs, &irec); |
446 | if (error) | 495 | if (error) |
447 | goto out; | 496 | goto out; |
448 | 497 | ||
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 8344b14031ef..3c83e8b3b39c 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h | |||
@@ -545,6 +545,51 @@ TRACE_EVENT(xchk_xref_error, | |||
545 | __entry->ret_ip) | 545 | __entry->ret_ip) |
546 | ); | 546 | ); |
547 | 547 | ||
548 | TRACE_EVENT(xchk_iallocbt_check_cluster, | ||
549 | TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, | ||
550 | xfs_agino_t startino, xfs_daddr_t map_daddr, | ||
551 | unsigned short map_len, unsigned int chunk_ino, | ||
552 | unsigned int nr_inodes, uint16_t cluster_mask, | ||
553 | uint16_t holemask, unsigned int cluster_ino), | ||
554 | TP_ARGS(mp, agno, startino, map_daddr, map_len, chunk_ino, nr_inodes, | ||
555 | cluster_mask, holemask, cluster_ino), | ||
556 | TP_STRUCT__entry( | ||
557 | __field(dev_t, dev) | ||
558 | __field(xfs_agnumber_t, agno) | ||
559 | __field(xfs_agino_t, startino) | ||
560 | __field(xfs_daddr_t, map_daddr) | ||
561 | __field(unsigned short, map_len) | ||
562 | __field(unsigned int, chunk_ino) | ||
563 | __field(unsigned int, nr_inodes) | ||
564 | __field(unsigned int, cluster_ino) | ||
565 | __field(uint16_t, cluster_mask) | ||
566 | __field(uint16_t, holemask) | ||
567 | ), | ||
568 | TP_fast_assign( | ||
569 | __entry->dev = mp->m_super->s_dev; | ||
570 | __entry->agno = agno; | ||
571 | __entry->startino = startino; | ||
572 | __entry->map_daddr = map_daddr; | ||
573 | __entry->map_len = map_len; | ||
574 | __entry->chunk_ino = chunk_ino; | ||
575 | __entry->nr_inodes = nr_inodes; | ||
576 | __entry->cluster_mask = cluster_mask; | ||
577 | __entry->holemask = holemask; | ||
578 | __entry->cluster_ino = cluster_ino; | ||
579 | ), | ||
580 | TP_printk("dev %d:%d agno %d startino %u daddr 0x%llx len %d chunkino %u nr_inodes %u cluster_mask 0x%x holemask 0x%x cluster_ino %u", | ||
581 | MAJOR(__entry->dev), MINOR(__entry->dev), | ||
582 | __entry->agno, | ||
583 | __entry->startino, | ||
584 | __entry->map_daddr, | ||
585 | __entry->map_len, | ||
586 | __entry->chunk_ino, | ||
587 | __entry->nr_inodes, | ||
588 | __entry->cluster_mask, | ||
589 | __entry->holemask, | ||
590 | __entry->cluster_ino) | ||
591 | ) | ||
592 | |||
548 | /* repair tracepoints */ | 593 | /* repair tracepoints */ |
549 | #if IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) | 594 | #if IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) |
550 | 595 | ||