]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2bd0ea18 | 2 | /* |
da23017d NS |
3 | * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. |
4 | * All Rights Reserved. | |
2bd0ea18 NS |
5 | */ |
6 | ||
6b803e5a | 7 | #include "libxfs.h" |
1164bde5 DC |
8 | #include "threads.h" |
9 | #include "prefetch.h" | |
2bd0ea18 NS |
10 | #include "avl.h" |
11 | #include "globals.h" | |
12 | #include "agheader.h" | |
13 | #include "incore.h" | |
14 | #include "protos.h" | |
15 | #include "err_protos.h" | |
16 | #include "dinode.h" | |
2bd0ea18 NS |
17 | #include "bmap.h" |
18 | #include "versions.h" | |
19 | #include "dir2.h" | |
06fbdda9 | 20 | #include "progress.h" |
9e0f480e DW |
21 | #include "slab.h" |
22 | #include "rmap.h" | |
2bd0ea18 | 23 | |
9e0f480e | 24 | bool collect_rmaps; |
2bd0ea18 | 25 | |
2bd0ea18 NS |
26 | /* |
27 | * null out quota inode fields in sb if they point to non-existent inodes. | |
28 | * this isn't as redundant as it looks since it's possible that the sb field | |
29 | * might be set but the imap and inode(s) agree that the inode is | |
30 | * free in which case they'd never be cleared so the fields wouldn't | |
31 | * be cleared by process_dinode(). | |
32 | */ | |
8b8a6b02 | 33 | static void |
2bd0ea18 NS |
34 | quotino_check(xfs_mount_t *mp) |
35 | { | |
36 | ino_tree_node_t *irec; | |
37 | ||
38 | if (mp->m_sb.sb_uquotino != NULLFSINO && mp->m_sb.sb_uquotino != 0) { | |
017e979e | 39 | if (!libxfs_verify_ino(mp, mp->m_sb.sb_uquotino)) |
6411f39c BN |
40 | irec = NULL; |
41 | else | |
1ae311d5 | 42 | irec = find_inode_rec(mp, |
6411f39c BN |
43 | XFS_INO_TO_AGNO(mp, mp->m_sb.sb_uquotino), |
44 | XFS_INO_TO_AGINO(mp, mp->m_sb.sb_uquotino)); | |
2bd0ea18 NS |
45 | |
46 | if (irec == NULL || is_inode_free(irec, | |
47 | mp->m_sb.sb_uquotino - irec->ino_startnum)) { | |
48 | mp->m_sb.sb_uquotino = NULLFSINO; | |
49 | lost_uquotino = 1; | |
50 | } else | |
51 | lost_uquotino = 0; | |
52 | } | |
53 | ||
b36eef04 | 54 | if (mp->m_sb.sb_gquotino != NULLFSINO && mp->m_sb.sb_gquotino != 0) { |
017e979e | 55 | if (!libxfs_verify_ino(mp, mp->m_sb.sb_gquotino)) |
6411f39c BN |
56 | irec = NULL; |
57 | else | |
1ae311d5 | 58 | irec = find_inode_rec(mp, |
6411f39c BN |
59 | XFS_INO_TO_AGNO(mp, mp->m_sb.sb_gquotino), |
60 | XFS_INO_TO_AGINO(mp, mp->m_sb.sb_gquotino)); | |
2bd0ea18 NS |
61 | |
62 | if (irec == NULL || is_inode_free(irec, | |
b36eef04 NS |
63 | mp->m_sb.sb_gquotino - irec->ino_startnum)) { |
64 | mp->m_sb.sb_gquotino = NULLFSINO; | |
0340d706 | 65 | lost_gquotino = 1; |
2bd0ea18 | 66 | } else |
0340d706 CS |
67 | lost_gquotino = 0; |
68 | } | |
69 | ||
70 | if (mp->m_sb.sb_pquotino != NULLFSINO && mp->m_sb.sb_pquotino != 0) { | |
017e979e | 71 | if (!libxfs_verify_ino(mp, mp->m_sb.sb_pquotino)) |
0340d706 CS |
72 | irec = NULL; |
73 | else | |
74 | irec = find_inode_rec(mp, | |
75 | XFS_INO_TO_AGNO(mp, mp->m_sb.sb_pquotino), | |
76 | XFS_INO_TO_AGINO(mp, mp->m_sb.sb_pquotino)); | |
77 | ||
78 | if (irec == NULL || is_inode_free(irec, | |
79 | mp->m_sb.sb_pquotino - irec->ino_startnum)) { | |
80 | mp->m_sb.sb_pquotino = NULLFSINO; | |
81 | lost_pquotino = 1; | |
82 | } else | |
83 | lost_pquotino = 0; | |
2bd0ea18 NS |
84 | } |
85 | } | |
86 | ||
8b8a6b02 | 87 | static void |
2bd0ea18 NS |
88 | quota_sb_check(xfs_mount_t *mp) |
89 | { | |
90 | /* | |
91 | * if the sb says we have quotas and we lost both, | |
92 | * signal a superblock downgrade. that will cause | |
93 | * the quota flags to get zeroed. (if we only lost | |
94 | * one quota inode, do nothing and complain later.) | |
95 | * | |
96 | * if the sb says we have quotas but we didn't start out | |
97 | * with any quota inodes, signal a superblock downgrade. | |
98 | * | |
99 | * The sb downgrades are so that older systems can mount | |
100 | * the filesystem. | |
101 | * | |
102 | * if the sb says we don't have quotas but it looks like | |
103 | * we do have quota inodes, then signal a superblock upgrade. | |
104 | * | |
105 | * if the sb says we don't have quotas and we have no | |
106 | * quota inodes, then leave will enough alone. | |
107 | */ | |
108 | ||
109 | if (fs_quotas && | |
110 | (mp->m_sb.sb_uquotino == NULLFSINO || mp->m_sb.sb_uquotino == 0) && | |
0340d706 CS |
111 | (mp->m_sb.sb_gquotino == NULLFSINO || mp->m_sb.sb_gquotino == 0) && |
112 | (mp->m_sb.sb_pquotino == NULLFSINO || mp->m_sb.sb_pquotino == 0)) { | |
2bd0ea18 NS |
113 | lost_quotas = 1; |
114 | fs_quotas = 0; | |
017e979e DW |
115 | } else if (libxfs_verify_ino(mp, mp->m_sb.sb_uquotino) && |
116 | libxfs_verify_ino(mp, mp->m_sb.sb_gquotino) && | |
117 | libxfs_verify_ino(mp, mp->m_sb.sb_pquotino)) { | |
2bd0ea18 NS |
118 | fs_quotas = 1; |
119 | } | |
120 | } | |
121 | ||
122 | ||
2556c98b BN |
123 | static void |
124 | process_ag_func( | |
62843f36 | 125 | struct workqueue *wq, |
2556c98b BN |
126 | xfs_agnumber_t agno, |
127 | void *arg) | |
3b6ac903 | 128 | { |
2556c98b | 129 | wait_for_inode_prefetch(arg); |
3b6ac903 | 130 | do_log(_(" - agno = %d\n"), agno); |
62843f36 | 131 | process_aginodes(wq->wq_ctx, arg, agno, 0, 1, 0); |
bd758142 | 132 | blkmap_free_final(); |
2556c98b | 133 | cleanup_inode_prefetch(arg); |
3b6ac903 MV |
134 | |
135 | /* | |
136 | * now recycle the per-AG duplicate extent records | |
137 | */ | |
138 | release_dup_extent_tree(agno); | |
139 | } | |
140 | ||
2556c98b BN |
141 | static void |
142 | process_ags( | |
143 | xfs_mount_t *mp) | |
144 | { | |
b7f12e53 DW |
145 | xfs_agnumber_t i; |
146 | int error; | |
147 | ||
1164bde5 | 148 | do_inode_prefetch(mp, ag_stride, process_ag_func, true, false); |
b7f12e53 | 149 | for (i = 0; i < mp->m_sb.sb_agcount; i++) { |
2d273771 | 150 | error = rmap_finish_collecting_fork_recs(mp, i); |
b7f12e53 DW |
151 | if (error) |
152 | do_error( | |
153 | _("unable to finish adding attr/data fork reverse-mapping data for AG %u.\n"), | |
154 | i); | |
155 | } | |
2556c98b BN |
156 | } |
157 | ||
713b6817 DW |
158 | static void |
159 | check_rmap_btrees( | |
62843f36 | 160 | struct workqueue*wq, |
713b6817 DW |
161 | xfs_agnumber_t agno, |
162 | void *arg) | |
163 | { | |
164 | int error; | |
165 | ||
62843f36 | 166 | error = rmap_add_fixed_ag_rec(wq->wq_ctx, agno); |
713b6817 DW |
167 | if (error) |
168 | do_error( | |
169 | _("unable to add AG %u metadata reverse-mapping data.\n"), agno); | |
170 | ||
62843f36 | 171 | error = rmap_fold_raw_recs(wq->wq_ctx, agno); |
713b6817 DW |
172 | if (error) |
173 | do_error( | |
174 | _("unable to merge AG %u metadata reverse-mapping data.\n"), agno); | |
11b9e510 | 175 | |
62843f36 | 176 | error = rmaps_verify_btree(wq->wq_ctx, agno); |
11b9e510 DW |
177 | if (error) |
178 | do_error( | |
179 | _("%s while checking reverse-mappings"), | |
180 | strerror(-error)); | |
713b6817 DW |
181 | } |
182 | ||
00f34bca DW |
183 | static void |
184 | compute_ag_refcounts( | |
62843f36 | 185 | struct workqueue*wq, |
00f34bca DW |
186 | xfs_agnumber_t agno, |
187 | void *arg) | |
188 | { | |
189 | int error; | |
190 | ||
62843f36 | 191 | error = compute_refcounts(wq->wq_ctx, agno); |
00f34bca DW |
192 | if (error) |
193 | do_error( | |
194 | _("%s while computing reference count records.\n"), | |
195 | strerror(-error)); | |
196 | } | |
197 | ||
ca8d7d6a DW |
198 | static void |
199 | process_inode_reflink_flags( | |
62843f36 | 200 | struct workqueue *wq, |
ca8d7d6a DW |
201 | xfs_agnumber_t agno, |
202 | void *arg) | |
203 | { | |
204 | int error; | |
205 | ||
62843f36 | 206 | error = fix_inode_reflink_flags(wq->wq_ctx, agno); |
ca8d7d6a DW |
207 | if (error) |
208 | do_error( | |
209 | _("%s while fixing inode reflink flags.\n"), | |
210 | strerror(-error)); | |
211 | } | |
212 | ||
80dbc783 DW |
213 | static void |
214 | check_refcount_btrees( | |
62843f36 | 215 | struct workqueue*wq, |
80dbc783 DW |
216 | xfs_agnumber_t agno, |
217 | void *arg) | |
218 | { | |
219 | int error; | |
220 | ||
62843f36 | 221 | error = check_refcounts(wq->wq_ctx, agno); |
80dbc783 DW |
222 | if (error) |
223 | do_error( | |
224 | _("%s while checking reference counts"), | |
225 | strerror(-error)); | |
226 | } | |
227 | ||
713b6817 DW |
228 | static void |
229 | process_rmap_data( | |
230 | struct xfs_mount *mp) | |
231 | { | |
62843f36 | 232 | struct workqueue wq; |
713b6817 DW |
233 | xfs_agnumber_t i; |
234 | ||
2d273771 | 235 | if (!rmap_needs_work(mp)) |
713b6817 DW |
236 | return; |
237 | ||
4b45ff6f | 238 | create_work_queue(&wq, mp, platform_nproc()); |
713b6817 DW |
239 | for (i = 0; i < mp->m_sb.sb_agcount; i++) |
240 | queue_work(&wq, check_rmap_btrees, i, NULL); | |
241 | destroy_work_queue(&wq); | |
00f34bca | 242 | |
2660e653 | 243 | if (!xfs_has_reflink(mp)) |
00f34bca DW |
244 | return; |
245 | ||
4b45ff6f | 246 | create_work_queue(&wq, mp, platform_nproc()); |
00f34bca DW |
247 | for (i = 0; i < mp->m_sb.sb_agcount; i++) |
248 | queue_work(&wq, compute_ag_refcounts, i, NULL); | |
249 | destroy_work_queue(&wq); | |
ca8d7d6a | 250 | |
4b45ff6f | 251 | create_work_queue(&wq, mp, platform_nproc()); |
80dbc783 | 252 | for (i = 0; i < mp->m_sb.sb_agcount; i++) { |
ca8d7d6a | 253 | queue_work(&wq, process_inode_reflink_flags, i, NULL); |
80dbc783 DW |
254 | queue_work(&wq, check_refcount_btrees, i, NULL); |
255 | } | |
ca8d7d6a | 256 | destroy_work_queue(&wq); |
713b6817 | 257 | } |
2556c98b | 258 | |
2bd0ea18 NS |
259 | void |
260 | phase4(xfs_mount_t *mp) | |
261 | { | |
262 | ino_tree_node_t *irec; | |
5a35bf2c DC |
263 | xfs_rtblock_t bno; |
264 | xfs_rtblock_t rt_start; | |
2bd0ea18 NS |
265 | xfs_extlen_t rt_len; |
266 | xfs_agnumber_t i; | |
267 | xfs_agblock_t j; | |
268 | xfs_agblock_t ag_end; | |
8961bfde | 269 | xfs_extlen_t blen; |
2bd0ea18 NS |
270 | int ag_hdr_len = 4 * mp->m_sb.sb_sectsize; |
271 | int ag_hdr_block; | |
272 | int bstate; | |
dfc130f3 | 273 | |
2d273771 | 274 | if (rmap_needs_work(mp)) |
9e0f480e | 275 | collect_rmaps = true; |
2bd0ea18 NS |
276 | ag_hdr_block = howmany(ag_hdr_len, mp->m_sb.sb_blocksize); |
277 | ||
507f4e33 NS |
278 | do_log(_("Phase 4 - check for duplicate blocks...\n")); |
279 | do_log(_(" - setting up duplicate extent list...\n")); | |
2bd0ea18 | 280 | |
14f8b681 | 281 | set_progress_msg(PROG_FMT_DUP_EXTENT, (uint64_t) glob_agcount); |
06fbdda9 | 282 | |
1ae311d5 | 283 | irec = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino), |
2bd0ea18 NS |
284 | XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino)); |
285 | ||
286 | /* | |
287 | * we always have a root inode, even if it's free... | |
288 | * if the root is free, forget it, lost+found is already gone | |
289 | */ | |
290 | if (is_inode_free(irec, 0) || !inode_isadir(irec, 0)) { | |
291 | need_root_inode = 1; | |
292 | if (no_modify) | |
507f4e33 | 293 | do_warn(_("root inode would be lost\n")); |
2bd0ea18 | 294 | else |
507f4e33 | 295 | do_warn(_("root inode lost\n")); |
2bd0ea18 NS |
296 | } |
297 | ||
2bd0ea18 NS |
298 | for (i = 0; i < mp->m_sb.sb_agcount; i++) { |
299 | ag_end = (i < mp->m_sb.sb_agcount - 1) ? mp->m_sb.sb_agblocks : | |
300 | mp->m_sb.sb_dblocks - | |
5a35bf2c | 301 | (xfs_rfsblock_t) mp->m_sb.sb_agblocks * i; |
8961bfde | 302 | |
2bd0ea18 NS |
303 | /* |
304 | * set up duplicate extent list for this ag | |
305 | */ | |
8961bfde BN |
306 | for (j = ag_hdr_block; j < ag_end; j += blen) { |
307 | bstate = get_bmap_ext(i, j, ag_end, &blen); | |
308 | switch (bstate) { | |
f4cea8e8 DW |
309 | case XR_E_FREE1: |
310 | if (no_modify) | |
311 | do_warn( | |
312 | _("free space (%u,%u-%u) only seen by one free space btree\n"), | |
313 | i, j, j + blen - 1); | |
314 | break; | |
2bd0ea18 NS |
315 | case XR_E_BAD_STATE: |
316 | default: | |
507f4e33 | 317 | do_warn( |
32e11be9 DW |
318 | _("unknown block state, ag %d, blocks %u-%u\n"), |
319 | i, j, j + blen - 1); | |
7c432f77 | 320 | fallthrough; |
2bd0ea18 | 321 | case XR_E_UNKNOWN: |
2bd0ea18 NS |
322 | case XR_E_FREE: |
323 | case XR_E_INUSE: | |
324 | case XR_E_INUSE_FS: | |
325 | case XR_E_INO: | |
326 | case XR_E_FS_MAP: | |
2bd0ea18 NS |
327 | break; |
328 | case XR_E_MULT: | |
8961bfde | 329 | add_dup_extent(i, j, blen); |
2bd0ea18 NS |
330 | break; |
331 | } | |
332 | } | |
8961bfde | 333 | |
06fbdda9 | 334 | PROG_RPT_INC(prog_rpt_done[i], 1); |
2bd0ea18 | 335 | } |
06fbdda9 | 336 | print_final_rpt(); |
2bd0ea18 NS |
337 | |
338 | /* | |
339 | * initialize realtime bitmap | |
340 | */ | |
341 | rt_start = 0; | |
342 | rt_len = 0; | |
343 | ||
344 | for (bno = 0; bno < mp->m_sb.sb_rextents; bno++) { | |
95650c4d | 345 | bstate = get_rtbmap(bno); |
2bd0ea18 NS |
346 | switch (bstate) { |
347 | case XR_E_BAD_STATE: | |
348 | default: | |
5d1b7f0f CH |
349 | do_warn( |
350 | _("unknown rt extent state, extent %" PRIu64 "\n"), | |
507f4e33 | 351 | bno); |
7c432f77 | 352 | fallthrough; |
2bd0ea18 NS |
353 | case XR_E_UNKNOWN: |
354 | case XR_E_FREE1: | |
355 | case XR_E_FREE: | |
356 | case XR_E_INUSE: | |
357 | case XR_E_INUSE_FS: | |
358 | case XR_E_INO: | |
359 | case XR_E_FS_MAP: | |
360 | if (rt_start == 0) | |
361 | continue; | |
362 | else { | |
363 | /* | |
364 | * add extent and reset extent state | |
365 | */ | |
366 | add_rt_dup_extent(rt_start, rt_len); | |
367 | rt_start = 0; | |
368 | rt_len = 0; | |
369 | } | |
370 | break; | |
371 | case XR_E_MULT: | |
372 | if (rt_start == 0) { | |
373 | rt_start = bno; | |
374 | rt_len = 1; | |
375 | } else if (rt_len == MAXEXTLEN) { | |
376 | /* | |
377 | * large extent case | |
378 | */ | |
379 | add_rt_dup_extent(rt_start, rt_len); | |
380 | rt_start = bno; | |
381 | rt_len = 1; | |
382 | } else | |
383 | rt_len++; | |
384 | break; | |
385 | } | |
386 | } | |
387 | ||
388 | /* | |
389 | * catch tail-case, extent hitting the end of the ag | |
390 | */ | |
391 | if (rt_start != 0) | |
392 | add_rt_dup_extent(rt_start, rt_len); | |
393 | ||
394 | /* | |
395 | * initialize bitmaps for all AGs | |
396 | */ | |
c1f7a46c | 397 | reset_bmaps(mp); |
2bd0ea18 | 398 | |
507f4e33 | 399 | do_log(_(" - check for inodes claiming duplicate blocks...\n")); |
14f8b681 | 400 | set_progress_msg(PROG_FMT_DUP_BLOCKS, (uint64_t) mp->m_sb.sb_icount); |
add3cb90 BN |
401 | |
402 | /* | |
403 | * ok, now process the inodes -- signal 2-pass check per inode. | |
404 | * first pass checks if the inode conflicts with a known | |
405 | * duplicate extent. if so, the inode is cleared and second | |
406 | * pass is skipped. second pass sets the block bitmap | |
407 | * for all blocks claimed by the inode. directory | |
408 | * and attribute processing is turned OFF since we did that | |
409 | * already in phase 3. | |
410 | */ | |
2556c98b | 411 | process_ags(mp); |
713b6817 DW |
412 | |
413 | /* | |
414 | * Process all the reverse-mapping data that we collected. This | |
00f34bca DW |
415 | * involves checking the rmap data against the btree, computing |
416 | * reference counts based on the rmap data, and checking the counts | |
417 | * against the refcount btree. | |
713b6817 DW |
418 | */ |
419 | process_rmap_data(mp); | |
420 | ||
06fbdda9 | 421 | print_final_rpt(); |
2bd0ea18 NS |
422 | |
423 | /* | |
424 | * free up memory used to track trealtime duplicate extents | |
425 | */ | |
426 | if (rt_start != 0) | |
427 | free_rt_dup_extent_tree(mp); | |
428 | ||
429 | /* | |
430 | * ensure consistency of quota inode pointers in superblock, | |
431 | * make sure they point to real inodes | |
432 | */ | |
433 | quotino_check(mp); | |
434 | quota_sb_check(mp); | |
435 | } |