]>
Commit | Line | Data |
---|---|---|
2bd0ea18 | 1 | /* |
da23017d NS |
2 | * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. |
3 | * All Rights Reserved. | |
dfc130f3 | 4 | * |
da23017d NS |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public License as | |
2bd0ea18 | 7 | * published by the Free Software Foundation. |
dfc130f3 | 8 | * |
da23017d NS |
9 | * This program is distributed in the hope that it would be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
dfc130f3 | 13 | * |
da23017d NS |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write the Free Software Foundation, | |
16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
2bd0ea18 NS |
17 | */ |
18 | ||
6b803e5a | 19 | #include "libxfs.h" |
2bd0ea18 NS |
20 | #include <sys/time.h> |
21 | #include "bmap.h" | |
22 | #include "command.h" | |
2bd0ea18 NS |
23 | #include "frag.h" |
24 | #include "io.h" | |
25 | #include "output.h" | |
26 | #include "type.h" | |
4ca431fc | 27 | #include "init.h" |
2bd0ea18 NS |
28 | #include "malloc.h" |
29 | ||
30 | typedef struct extent { | |
31 | xfs_fileoff_t startoff; | |
32 | xfs_filblks_t blockcount; | |
33 | } extent_t; | |
34 | ||
35 | typedef struct extmap { | |
36 | int naents; | |
37 | int nents; | |
38 | extent_t ents[1]; | |
39 | } extmap_t; | |
40 | #define EXTMAP_SIZE(n) \ | |
41 | (offsetof(extmap_t, ents) + (sizeof(extent_t) * (n))) | |
42 | ||
43 | static int aflag; | |
44 | static int dflag; | |
45 | static __uint64_t extcount_actual; | |
46 | static __uint64_t extcount_ideal; | |
47 | static int fflag; | |
48 | static int lflag; | |
49 | static int qflag; | |
50 | static int Rflag; | |
51 | static int rflag; | |
52 | static int vflag; | |
53 | ||
b3563c19 | 54 | typedef void (*scan_lbtree_f_t)(struct xfs_btree_block *block, |
2bd0ea18 NS |
55 | int level, |
56 | extmap_t **extmapp, | |
57 | typnm_t btype); | |
58 | ||
b3563c19 | 59 | typedef void (*scan_sbtree_f_t)(struct xfs_btree_block *block, |
2bd0ea18 NS |
60 | int level, |
61 | xfs_agf_t *agf); | |
62 | ||
63 | static extmap_t *extmap_alloc(xfs_extnum_t nex); | |
64 | static xfs_extnum_t extmap_ideal(extmap_t *extmap); | |
65 | static void extmap_set_ext(extmap_t **extmapp, xfs_fileoff_t o, | |
66 | xfs_extlen_t c); | |
67 | static int frag_f(int argc, char **argv); | |
68 | static int init(int argc, char **argv); | |
b9652a81 | 69 | static void process_bmbt_reclist(xfs_bmbt_rec_t *rp, int numrecs, |
2bd0ea18 NS |
70 | extmap_t **extmapp); |
71 | static void process_btinode(xfs_dinode_t *dip, extmap_t **extmapp, | |
72 | int whichfork); | |
73 | static void process_exinode(xfs_dinode_t *dip, extmap_t **extmapp, | |
74 | int whichfork); | |
75 | static void process_fork(xfs_dinode_t *dip, int whichfork); | |
76 | static void process_inode(xfs_agf_t *agf, xfs_agino_t agino, | |
77 | xfs_dinode_t *dip); | |
78 | static void scan_ag(xfs_agnumber_t agno); | |
79 | static void scan_lbtree(xfs_fsblock_t root, int nlevels, | |
80 | scan_lbtree_f_t func, extmap_t **extmapp, | |
81 | typnm_t btype); | |
82 | static void scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root, | |
83 | int nlevels, scan_sbtree_f_t func, | |
84 | typnm_t btype); | |
b3563c19 | 85 | static void scanfunc_bmap(struct xfs_btree_block *block, int level, |
2bd0ea18 | 86 | extmap_t **extmapp, typnm_t btype); |
b3563c19 | 87 | static void scanfunc_ino(struct xfs_btree_block *block, int level, |
2bd0ea18 NS |
88 | xfs_agf_t *agf); |
89 | ||
dfc130f3 | 90 | static const cmdinfo_t frag_cmd = |
2bd0ea18 | 91 | { "frag", NULL, frag_f, 0, -1, 0, |
2c794e6e | 92 | "[-a] [-d] [-f] [-l] [-q] [-R] [-r] [-v]", |
2bd0ea18 NS |
93 | "get file fragmentation data", NULL }; |
94 | ||
95 | static extmap_t * | |
96 | extmap_alloc( | |
97 | xfs_extnum_t nex) | |
98 | { | |
99 | extmap_t *extmap; | |
100 | ||
101 | if (nex < 1) | |
102 | nex = 1; | |
103 | extmap = xmalloc(EXTMAP_SIZE(nex)); | |
104 | extmap->naents = nex; | |
105 | extmap->nents = 0; | |
106 | return extmap; | |
107 | } | |
108 | ||
109 | static xfs_extnum_t | |
110 | extmap_ideal( | |
111 | extmap_t *extmap) | |
112 | { | |
113 | extent_t *ep; | |
114 | xfs_extnum_t rval; | |
115 | ||
116 | for (ep = &extmap->ents[0], rval = 0; | |
117 | ep < &extmap->ents[extmap->nents]; | |
118 | ep++) { | |
119 | if (ep == &extmap->ents[0] || | |
120 | ep->startoff != ep[-1].startoff + ep[-1].blockcount) | |
121 | rval++; | |
122 | } | |
123 | return rval; | |
124 | } | |
125 | ||
126 | static void | |
127 | extmap_set_ext( | |
128 | extmap_t **extmapp, | |
129 | xfs_fileoff_t o, | |
130 | xfs_extlen_t c) | |
131 | { | |
132 | extmap_t *extmap; | |
133 | extent_t *ent; | |
134 | ||
135 | extmap = *extmapp; | |
136 | if (extmap->nents == extmap->naents) { | |
137 | extmap->naents++; | |
138 | extmap = xrealloc(extmap, EXTMAP_SIZE(extmap->naents)); | |
139 | *extmapp = extmap; | |
140 | } | |
141 | ent = &extmap->ents[extmap->nents]; | |
142 | ent->startoff = o; | |
143 | ent->blockcount = c; | |
144 | extmap->nents++; | |
145 | } | |
146 | ||
147 | void | |
148 | frag_init(void) | |
149 | { | |
150 | add_command(&frag_cmd); | |
151 | } | |
152 | ||
153 | /* | |
154 | * Get file fragmentation information. | |
155 | */ | |
156 | static int | |
157 | frag_f( | |
158 | int argc, | |
159 | char **argv) | |
160 | { | |
161 | xfs_agnumber_t agno; | |
162 | double answer; | |
163 | ||
164 | if (!init(argc, argv)) | |
165 | return 0; | |
166 | for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) | |
167 | scan_ag(agno); | |
168 | if (extcount_actual) | |
169 | answer = (double)(extcount_actual - extcount_ideal) * 100.0 / | |
170 | (double)extcount_actual; | |
171 | else | |
172 | answer = 0.0; | |
9ee7055c | 173 | dbprintf(_("actual %llu, ideal %llu, fragmentation factor %.2f%%\n"), |
2bd0ea18 NS |
174 | extcount_actual, extcount_ideal, answer); |
175 | return 0; | |
176 | } | |
177 | ||
178 | static int | |
179 | init( | |
180 | int argc, | |
181 | char **argv) | |
182 | { | |
183 | int c; | |
184 | ||
185 | aflag = dflag = fflag = lflag = qflag = Rflag = rflag = vflag = 0; | |
186 | optind = 0; | |
187 | while ((c = getopt(argc, argv, "adflqRrv")) != EOF) { | |
188 | switch (c) { | |
189 | case 'a': | |
190 | aflag = 1; | |
191 | break; | |
192 | case 'd': | |
193 | dflag = 1; | |
194 | break; | |
195 | case 'f': | |
196 | fflag = 1; | |
197 | break; | |
198 | case 'l': | |
199 | lflag = 1; | |
200 | break; | |
201 | case 'q': | |
202 | qflag = 1; | |
203 | break; | |
204 | case 'R': | |
205 | Rflag = 1; | |
206 | break; | |
207 | case 'r': | |
208 | rflag = 1; | |
209 | break; | |
210 | case 'v': | |
211 | vflag = 1; | |
212 | break; | |
213 | default: | |
9ee7055c | 214 | dbprintf(_("bad option for frag command\n")); |
2bd0ea18 NS |
215 | return 0; |
216 | } | |
217 | } | |
218 | if (!aflag && !dflag && !fflag && !lflag && !qflag && !Rflag && !rflag) | |
219 | aflag = dflag = fflag = lflag = qflag = Rflag = rflag = 1; | |
220 | extcount_actual = extcount_ideal = 0; | |
221 | return 1; | |
222 | } | |
223 | ||
224 | static void | |
225 | process_bmbt_reclist( | |
b9652a81 | 226 | xfs_bmbt_rec_t *rp, |
2bd0ea18 NS |
227 | int numrecs, |
228 | extmap_t **extmapp) | |
229 | { | |
5a35bf2c | 230 | xfs_filblks_t c; |
2bd0ea18 NS |
231 | int f; |
232 | int i; | |
5a35bf2c DC |
233 | xfs_fileoff_t o; |
234 | xfs_fsblock_t s; | |
2bd0ea18 NS |
235 | |
236 | for (i = 0; i < numrecs; i++, rp++) { | |
b9652a81 | 237 | convert_extent(rp, &o, &s, &c, &f); |
2bd0ea18 NS |
238 | extmap_set_ext(extmapp, (xfs_fileoff_t)o, (xfs_extlen_t)c); |
239 | } | |
240 | } | |
241 | ||
242 | static void | |
243 | process_btinode( | |
244 | xfs_dinode_t *dip, | |
245 | extmap_t **extmapp, | |
246 | int whichfork) | |
247 | { | |
248 | xfs_bmdr_block_t *dib; | |
249 | int i; | |
250 | xfs_bmbt_ptr_t *pp; | |
2bd0ea18 NS |
251 | |
252 | dib = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork); | |
5e656dbb | 253 | if (be16_to_cpu(dib->bb_level) == 0) { |
b9652a81 | 254 | xfs_bmbt_rec_t *rp = XFS_BMDR_REC_ADDR(dib, 1); |
5e656dbb | 255 | process_bmbt_reclist(rp, be16_to_cpu(dib->bb_numrecs), extmapp); |
2bd0ea18 NS |
256 | return; |
257 | } | |
b3563c19 | 258 | pp = XFS_BMDR_PTR_ADDR(dib, 1, |
ff105f75 | 259 | xfs_bmdr_maxrecs(XFS_DFORK_SIZE(dip, mp, whichfork), 0)); |
5e656dbb | 260 | for (i = 0; i < be16_to_cpu(dib->bb_numrecs); i++) |
f8149110 | 261 | scan_lbtree(be64_to_cpu(pp[i]), be16_to_cpu(dib->bb_level), |
5e656dbb | 262 | scanfunc_bmap, extmapp, |
2bd0ea18 NS |
263 | whichfork == XFS_DATA_FORK ? TYP_BMAPBTD : TYP_BMAPBTA); |
264 | } | |
265 | ||
266 | static void | |
267 | process_exinode( | |
268 | xfs_dinode_t *dip, | |
269 | extmap_t **extmapp, | |
270 | int whichfork) | |
271 | { | |
b9652a81 | 272 | xfs_bmbt_rec_t *rp; |
2bd0ea18 | 273 | |
b9652a81 | 274 | rp = (xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, whichfork); |
5e656dbb | 275 | process_bmbt_reclist(rp, XFS_DFORK_NEXTENTS(dip, whichfork), extmapp); |
2bd0ea18 NS |
276 | } |
277 | ||
278 | static void | |
279 | process_fork( | |
280 | xfs_dinode_t *dip, | |
281 | int whichfork) | |
282 | { | |
283 | extmap_t *extmap; | |
284 | int nex; | |
285 | ||
5e656dbb | 286 | nex = XFS_DFORK_NEXTENTS(dip, whichfork); |
2bd0ea18 NS |
287 | if (!nex) |
288 | return; | |
289 | extmap = extmap_alloc(nex); | |
290 | switch (XFS_DFORK_FORMAT(dip, whichfork)) { | |
291 | case XFS_DINODE_FMT_EXTENTS: | |
292 | process_exinode(dip, &extmap, whichfork); | |
293 | break; | |
294 | case XFS_DINODE_FMT_BTREE: | |
295 | process_btinode(dip, &extmap, whichfork); | |
296 | break; | |
297 | } | |
298 | extcount_actual += extmap->nents; | |
299 | extcount_ideal += extmap_ideal(extmap); | |
300 | xfree(extmap); | |
301 | } | |
302 | ||
303 | static void | |
304 | process_inode( | |
305 | xfs_agf_t *agf, | |
306 | xfs_agino_t agino, | |
307 | xfs_dinode_t *dip) | |
308 | { | |
309 | __uint64_t actual; | |
2bd0ea18 NS |
310 | __uint64_t ideal; |
311 | xfs_ino_t ino; | |
312 | int skipa; | |
313 | int skipd; | |
314 | ||
5e656dbb | 315 | ino = XFS_AGINO_TO_INO(mp, be32_to_cpu(agf->agf_seqno), agino); |
56b2de80 | 316 | switch (be16_to_cpu(dip->di_mode) & S_IFMT) { |
322f2a29 | 317 | case S_IFDIR: |
2bd0ea18 NS |
318 | skipd = !dflag; |
319 | break; | |
322f2a29 | 320 | case S_IFREG: |
56b2de80 | 321 | if (!rflag && (be16_to_cpu(dip->di_flags) & XFS_DIFLAG_REALTIME)) |
2bd0ea18 NS |
322 | skipd = 1; |
323 | else if (!Rflag && | |
324 | (ino == mp->m_sb.sb_rbmino || | |
325 | ino == mp->m_sb.sb_rsumino)) | |
326 | skipd = 1; | |
327 | else if (!qflag && | |
328 | (ino == mp->m_sb.sb_uquotino || | |
0340d706 CS |
329 | ino == mp->m_sb.sb_gquotino || |
330 | ino == mp->m_sb.sb_pquotino)) | |
2bd0ea18 NS |
331 | skipd = 1; |
332 | else | |
333 | skipd = !fflag; | |
334 | break; | |
322f2a29 | 335 | case S_IFLNK: |
2bd0ea18 NS |
336 | skipd = !lflag; |
337 | break; | |
338 | default: | |
339 | skipd = 1; | |
340 | break; | |
341 | } | |
342 | actual = extcount_actual; | |
343 | ideal = extcount_ideal; | |
344 | if (!skipd) | |
345 | process_fork(dip, XFS_DATA_FORK); | |
346 | skipa = !aflag || !XFS_DFORK_Q(dip); | |
347 | if (!skipa) | |
348 | process_fork(dip, XFS_ATTR_FORK); | |
349 | if (vflag && (!skipd || !skipa)) | |
9ee7055c | 350 | dbprintf(_("inode %lld actual %lld ideal %lld\n"), |
2bd0ea18 NS |
351 | ino, extcount_actual - actual, extcount_ideal - ideal); |
352 | } | |
353 | ||
354 | static void | |
355 | scan_ag( | |
356 | xfs_agnumber_t agno) | |
357 | { | |
358 | xfs_agf_t *agf; | |
359 | xfs_agi_t *agi; | |
360 | ||
361 | push_cur(); | |
9440d84d NS |
362 | set_cur(&typtab[TYP_AGF], |
363 | XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)), | |
364 | XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL); | |
2bd0ea18 | 365 | if ((agf = iocur_top->data) == NULL) { |
9ee7055c | 366 | dbprintf(_("can't read agf block for ag %u\n"), agno); |
2bd0ea18 NS |
367 | pop_cur(); |
368 | return; | |
369 | } | |
370 | push_cur(); | |
9440d84d NS |
371 | set_cur(&typtab[TYP_AGI], |
372 | XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)), | |
373 | XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL); | |
2bd0ea18 | 374 | if ((agi = iocur_top->data) == NULL) { |
9ee7055c | 375 | dbprintf(_("can't read agi block for ag %u\n"), agno); |
2bd0ea18 NS |
376 | pop_cur(); |
377 | pop_cur(); | |
378 | return; | |
379 | } | |
f8149110 | 380 | scan_sbtree(agf, be32_to_cpu(agi->agi_root), |
5e656dbb | 381 | be32_to_cpu(agi->agi_level), scanfunc_ino, TYP_INOBT); |
2bd0ea18 NS |
382 | pop_cur(); |
383 | pop_cur(); | |
384 | } | |
385 | ||
386 | static void | |
387 | scan_lbtree( | |
388 | xfs_fsblock_t root, | |
389 | int nlevels, | |
390 | scan_lbtree_f_t func, | |
391 | extmap_t **extmapp, | |
392 | typnm_t btype) | |
393 | { | |
394 | push_cur(); | |
395 | set_cur(&typtab[btype], XFS_FSB_TO_DADDR(mp, root), blkbb, DB_RING_IGN, | |
396 | NULL); | |
397 | if (iocur_top->data == NULL) { | |
9ee7055c | 398 | dbprintf(_("can't read btree block %u/%u\n"), |
2bd0ea18 NS |
399 | XFS_FSB_TO_AGNO(mp, root), |
400 | XFS_FSB_TO_AGBNO(mp, root)); | |
401 | return; | |
402 | } | |
403 | (*func)(iocur_top->data, nlevels - 1, extmapp, btype); | |
404 | pop_cur(); | |
405 | } | |
406 | ||
407 | static void | |
408 | scan_sbtree( | |
409 | xfs_agf_t *agf, | |
410 | xfs_agblock_t root, | |
411 | int nlevels, | |
412 | scan_sbtree_f_t func, | |
413 | typnm_t btype) | |
414 | { | |
5e656dbb | 415 | xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno); |
2bd0ea18 NS |
416 | |
417 | push_cur(); | |
418 | set_cur(&typtab[btype], XFS_AGB_TO_DADDR(mp, seqno, root), | |
419 | blkbb, DB_RING_IGN, NULL); | |
420 | if (iocur_top->data == NULL) { | |
9ee7055c | 421 | dbprintf(_("can't read btree block %u/%u\n"), seqno, root); |
2bd0ea18 NS |
422 | return; |
423 | } | |
424 | (*func)(iocur_top->data, nlevels - 1, agf); | |
425 | pop_cur(); | |
426 | } | |
427 | ||
428 | static void | |
429 | scanfunc_bmap( | |
b3563c19 | 430 | struct xfs_btree_block *block, |
2bd0ea18 NS |
431 | int level, |
432 | extmap_t **extmapp, | |
433 | typnm_t btype) | |
434 | { | |
2bd0ea18 NS |
435 | int i; |
436 | xfs_bmbt_ptr_t *pp; | |
5e656dbb | 437 | xfs_bmbt_rec_t *rp; |
8ad2cf44 ES |
438 | int nrecs; |
439 | ||
440 | nrecs = be16_to_cpu(block->bb_numrecs); | |
2bd0ea18 NS |
441 | |
442 | if (level == 0) { | |
8ad2cf44 ES |
443 | if (nrecs > mp->m_bmap_dmxr[0]) { |
444 | dbprintf(_("invalid numrecs (%u) in %s block\n"), | |
445 | nrecs, typtab[btype].name); | |
446 | return; | |
447 | } | |
b3563c19 | 448 | rp = XFS_BMBT_REC_ADDR(mp, block, 1); |
b9652a81 | 449 | process_bmbt_reclist(rp, nrecs, extmapp); |
8ad2cf44 ES |
450 | return; |
451 | } | |
452 | ||
453 | if (nrecs > mp->m_bmap_dmxr[1]) { | |
454 | dbprintf(_("invalid numrecs (%u) in %s block\n"), | |
455 | nrecs, typtab[btype].name); | |
2bd0ea18 NS |
456 | return; |
457 | } | |
b3563c19 | 458 | pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[0]); |
8ad2cf44 | 459 | for (i = 0; i < nrecs; i++) |
f8149110 | 460 | scan_lbtree(be64_to_cpu(pp[i]), level, scanfunc_bmap, extmapp, |
5e656dbb | 461 | btype); |
2bd0ea18 NS |
462 | } |
463 | ||
464 | static void | |
465 | scanfunc_ino( | |
b3563c19 | 466 | struct xfs_btree_block *block, |
2bd0ea18 NS |
467 | int level, |
468 | xfs_agf_t *agf) | |
469 | { | |
470 | xfs_agino_t agino; | |
5e656dbb | 471 | xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno); |
2bd0ea18 NS |
472 | int i; |
473 | int j; | |
474 | int off; | |
475 | xfs_inobt_ptr_t *pp; | |
476 | xfs_inobt_rec_t *rp; | |
477 | ||
478 | if (level == 0) { | |
b3563c19 | 479 | rp = XFS_INOBT_REC_ADDR(mp, block, 1); |
5e656dbb BN |
480 | for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) { |
481 | agino = be32_to_cpu(rp[i].ir_startino); | |
2bd0ea18 NS |
482 | off = XFS_INO_TO_OFFSET(mp, agino); |
483 | push_cur(); | |
484 | set_cur(&typtab[TYP_INODE], | |
485 | XFS_AGB_TO_DADDR(mp, seqno, | |
486 | XFS_AGINO_TO_AGBNO(mp, agino)), | |
ff105f75 | 487 | XFS_FSB_TO_BB(mp, mp->m_ialloc_blks), |
2bd0ea18 NS |
488 | DB_RING_IGN, NULL); |
489 | if (iocur_top->data == NULL) { | |
9ee7055c | 490 | dbprintf(_("can't read inode block %u/%u\n"), |
2bd0ea18 NS |
491 | seqno, XFS_AGINO_TO_AGBNO(mp, agino)); |
492 | continue; | |
493 | } | |
494 | for (j = 0; j < XFS_INODES_PER_CHUNK; j++) { | |
b34acbba | 495 | if (XFS_INOBT_IS_FREE_DISK(&rp[i], j)) |
2bd0ea18 | 496 | continue; |
5e656dbb | 497 | process_inode(agf, agino + j, (xfs_dinode_t *) |
f8149110 | 498 | ((char *)iocur_top->data + |
5e656dbb | 499 | ((off + j) << mp->m_sb.sb_inodelog))); |
2bd0ea18 NS |
500 | } |
501 | pop_cur(); | |
502 | } | |
503 | return; | |
504 | } | |
b3563c19 | 505 | pp = XFS_INOBT_PTR_ADDR(mp, block, 1, mp->m_inobt_mxr[1]); |
5e656dbb | 506 | for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) |
f8149110 | 507 | scan_sbtree(agf, be32_to_cpu(pp[i]), level, scanfunc_ino, |
5e656dbb | 508 | TYP_INOBT); |
2bd0ea18 | 509 | } |