2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc., 59
21 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
28 * For further information regarding this notice, see:
30 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
46 typedef struct extent
{
47 xfs_fileoff_t startoff
;
48 xfs_filblks_t blockcount
;
51 typedef struct extmap
{
56 #define EXTMAP_SIZE(n) \
57 (offsetof(extmap_t, ents) + (sizeof(extent_t) * (n)))
61 static __uint64_t extcount_actual
;
62 static __uint64_t extcount_ideal
;
70 typedef void (*scan_lbtree_f_t
)(xfs_btree_lblock_t
*block
,
75 typedef void (*scan_sbtree_f_t
)(xfs_btree_sblock_t
*block
,
79 static extmap_t
*extmap_alloc(xfs_extnum_t nex
);
80 static xfs_extnum_t
extmap_ideal(extmap_t
*extmap
);
81 static void extmap_set_ext(extmap_t
**extmapp
, xfs_fileoff_t o
,
83 static int frag_f(int argc
, char **argv
);
84 static int init(int argc
, char **argv
);
85 static void process_bmbt_reclist(xfs_bmbt_rec_32_t
*rp
, int numrecs
,
87 static void process_btinode(xfs_dinode_t
*dip
, extmap_t
**extmapp
,
89 static void process_exinode(xfs_dinode_t
*dip
, extmap_t
**extmapp
,
91 static void process_fork(xfs_dinode_t
*dip
, int whichfork
);
92 static void process_inode(xfs_agf_t
*agf
, xfs_agino_t agino
,
94 static void scan_ag(xfs_agnumber_t agno
);
95 static void scan_lbtree(xfs_fsblock_t root
, int nlevels
,
96 scan_lbtree_f_t func
, extmap_t
**extmapp
,
98 static void scan_sbtree(xfs_agf_t
*agf
, xfs_agblock_t root
,
99 int nlevels
, scan_sbtree_f_t func
,
101 static void scanfunc_bmap(xfs_btree_lblock_t
*ablock
, int level
,
102 extmap_t
**extmapp
, typnm_t btype
);
103 static void scanfunc_ino(xfs_btree_sblock_t
*ablock
, int level
,
106 static const cmdinfo_t frag_cmd
=
107 { "frag", NULL
, frag_f
, 0, -1, 0,
108 "[-a] [-d] [-f] [-l] [-r]",
109 "get file fragmentation data", NULL
};
119 extmap
= xmalloc(EXTMAP_SIZE(nex
));
120 extmap
->naents
= nex
;
132 for (ep
= &extmap
->ents
[0], rval
= 0;
133 ep
< &extmap
->ents
[extmap
->nents
];
135 if (ep
== &extmap
->ents
[0] ||
136 ep
->startoff
!= ep
[-1].startoff
+ ep
[-1].blockcount
)
152 if (extmap
->nents
== extmap
->naents
) {
154 extmap
= xrealloc(extmap
, EXTMAP_SIZE(extmap
->naents
));
157 ent
= &extmap
->ents
[extmap
->nents
];
166 add_command(&frag_cmd
);
170 * Get file fragmentation information.
180 if (!init(argc
, argv
))
182 for (agno
= 0; agno
< mp
->m_sb
.sb_agcount
; agno
++)
185 answer
= (double)(extcount_actual
- extcount_ideal
) * 100.0 /
186 (double)extcount_actual
;
189 dbprintf("actual %llu, ideal %llu, fragmentation factor %.2f%%\n",
190 extcount_actual
, extcount_ideal
, answer
);
201 aflag
= dflag
= fflag
= lflag
= qflag
= Rflag
= rflag
= vflag
= 0;
203 while ((c
= getopt(argc
, argv
, "adflqRrv")) != EOF
) {
230 dbprintf("bad option for frag command\n");
234 if (!aflag
&& !dflag
&& !fflag
&& !lflag
&& !qflag
&& !Rflag
&& !rflag
)
235 aflag
= dflag
= fflag
= lflag
= qflag
= Rflag
= rflag
= 1;
236 extcount_actual
= extcount_ideal
= 0;
241 process_bmbt_reclist(
242 xfs_bmbt_rec_32_t
*rp
,
252 for (i
= 0; i
< numrecs
; i
++, rp
++) {
253 convert_extent((xfs_bmbt_rec_64_t
*)rp
, &o
, &s
, &c
, &f
);
254 extmap_set_ext(extmapp
, (xfs_fileoff_t
)o
, (xfs_extlen_t
)c
);
264 xfs_bmdr_block_t
*dib
;
267 xfs_bmbt_rec_32_t
*rp
;
269 dib
= (xfs_bmdr_block_t
*)XFS_DFORK_PTR(dip
, whichfork
);
270 if (INT_GET(dib
->bb_level
, ARCH_CONVERT
) == 0) {
271 rp
= (xfs_bmbt_rec_32_t
*)XFS_BTREE_REC_ADDR(
272 XFS_DFORK_SIZE(dip
, mp
, whichfork
),
274 XFS_BTREE_BLOCK_MAXRECS(XFS_DFORK_SIZE(dip
, mp
,
277 process_bmbt_reclist(rp
, INT_GET(dib
->bb_numrecs
, ARCH_CONVERT
), extmapp
);
280 pp
= XFS_BTREE_PTR_ADDR(XFS_DFORK_SIZE(dip
, mp
, whichfork
),
282 XFS_BTREE_BLOCK_MAXRECS(XFS_DFORK_SIZE(dip
, mp
, whichfork
),
284 for (i
= 0; i
< INT_GET(dib
->bb_numrecs
, ARCH_CONVERT
); i
++)
285 scan_lbtree((xfs_fsblock_t
)INT_GET(pp
[i
], ARCH_CONVERT
), INT_GET(dib
->bb_level
, ARCH_CONVERT
), scanfunc_bmap
,
287 whichfork
== XFS_DATA_FORK
? TYP_BMAPBTD
: TYP_BMAPBTA
);
296 xfs_bmbt_rec_32_t
*rp
;
298 rp
= (xfs_bmbt_rec_32_t
*)XFS_DFORK_PTR(dip
, whichfork
);
299 process_bmbt_reclist(rp
, XFS_DFORK_NEXTENTS(dip
, whichfork
), extmapp
);
310 nex
= XFS_DFORK_NEXTENTS(dip
, whichfork
);
313 extmap
= extmap_alloc(nex
);
314 switch (XFS_DFORK_FORMAT(dip
, whichfork
)) {
315 case XFS_DINODE_FMT_EXTENTS
:
316 process_exinode(dip
, &extmap
, whichfork
);
318 case XFS_DINODE_FMT_BTREE
:
319 process_btinode(dip
, &extmap
, whichfork
);
322 extcount_actual
+= extmap
->nents
;
323 extcount_ideal
+= extmap_ideal(extmap
);
334 xfs_dinode_core_t
*dic
;
341 ino
= XFS_AGINO_TO_INO(mp
, INT_GET(agf
->agf_seqno
, ARCH_CONVERT
), agino
);
342 switch (dic
->di_mode
& IFMT
) {
347 if (!rflag
&& (dic
->di_flags
& XFS_DIFLAG_REALTIME
))
350 (ino
== mp
->m_sb
.sb_rbmino
||
351 ino
== mp
->m_sb
.sb_rsumino
))
354 (ino
== mp
->m_sb
.sb_uquotino
||
355 ino
== mp
->m_sb
.sb_gquotino
))
367 actual
= extcount_actual
;
368 ideal
= extcount_ideal
;
370 process_fork(dip
, XFS_DATA_FORK
);
371 skipa
= !aflag
|| !XFS_DFORK_Q(dip
);
373 process_fork(dip
, XFS_ATTR_FORK
);
374 if (vflag
&& (!skipd
|| !skipa
))
375 dbprintf("inode %lld actual %lld ideal %lld\n",
376 ino
, extcount_actual
- actual
, extcount_ideal
- ideal
);
387 set_cur(&typtab
[TYP_AGF
], XFS_AG_DADDR(mp
, agno
, XFS_AGF_DADDR
), 1,
389 if ((agf
= iocur_top
->data
) == NULL
) {
390 dbprintf("can't read agf block for ag %u\n", agno
);
395 set_cur(&typtab
[TYP_AGI
], XFS_AG_DADDR(mp
, agno
, XFS_AGI_DADDR
), 1,
397 if ((agi
= iocur_top
->data
) == NULL
) {
398 dbprintf("can't read agi block for ag %u\n", agno
);
404 INT_GET(agi
->agi_root
, ARCH_CONVERT
),
405 INT_GET(agi
->agi_level
, ARCH_CONVERT
),
406 scanfunc_ino
, TYP_INOBT
);
415 scan_lbtree_f_t func
,
420 set_cur(&typtab
[btype
], XFS_FSB_TO_DADDR(mp
, root
), blkbb
, DB_RING_IGN
,
422 if (iocur_top
->data
== NULL
) {
423 dbprintf("can't read btree block %u/%u\n",
424 XFS_FSB_TO_AGNO(mp
, root
),
425 XFS_FSB_TO_AGBNO(mp
, root
));
428 (*func
)(iocur_top
->data
, nlevels
- 1, extmapp
, btype
);
437 scan_sbtree_f_t func
,
440 xfs_agnumber_t seqno
= INT_GET(agf
->agf_seqno
, ARCH_CONVERT
);
443 set_cur(&typtab
[btype
], XFS_AGB_TO_DADDR(mp
, seqno
, root
),
444 blkbb
, DB_RING_IGN
, NULL
);
445 if (iocur_top
->data
== NULL
) {
446 dbprintf("can't read btree block %u/%u\n", seqno
, root
);
449 (*func
)(iocur_top
->data
, nlevels
- 1, agf
);
455 xfs_btree_lblock_t
*ablock
,
460 xfs_bmbt_block_t
*block
= (xfs_bmbt_block_t
*)ablock
;
463 xfs_bmbt_rec_32_t
*rp
;
466 rp
= (xfs_bmbt_rec_32_t
*)
467 XFS_BTREE_REC_ADDR(mp
->m_sb
.sb_blocksize
, xfs_bmbt
,
468 block
, 1, mp
->m_bmap_dmxr
[0]);
469 process_bmbt_reclist(rp
, INT_GET(block
->bb_numrecs
, ARCH_CONVERT
), extmapp
);
472 pp
= XFS_BTREE_PTR_ADDR(mp
->m_sb
.sb_blocksize
, xfs_bmbt
, block
, 1,
474 for (i
= 0; i
< INT_GET(block
->bb_numrecs
, ARCH_CONVERT
); i
++)
475 scan_lbtree(INT_GET(pp
[i
], ARCH_CONVERT
), level
, scanfunc_bmap
, extmapp
, btype
);
480 xfs_btree_sblock_t
*ablock
,
485 xfs_inobt_block_t
*block
= (xfs_inobt_block_t
*)ablock
;
486 xfs_agnumber_t seqno
= INT_GET(agf
->agf_seqno
, ARCH_CONVERT
);
494 rp
= XFS_BTREE_REC_ADDR(mp
->m_sb
.sb_blocksize
, xfs_inobt
, block
,
495 1, mp
->m_inobt_mxr
[0]);
496 for (i
= 0; i
< INT_GET(block
->bb_numrecs
, ARCH_CONVERT
); i
++) {
497 agino
= INT_GET(rp
[i
].ir_startino
, ARCH_CONVERT
);
498 off
= XFS_INO_TO_OFFSET(mp
, agino
);
500 set_cur(&typtab
[TYP_INODE
],
501 XFS_AGB_TO_DADDR(mp
, seqno
,
502 XFS_AGINO_TO_AGBNO(mp
, agino
)),
503 (int)XFS_FSB_TO_BB(mp
, XFS_IALLOC_BLOCKS(mp
)),
505 if (iocur_top
->data
== NULL
) {
506 dbprintf("can't read inode block %u/%u\n",
507 seqno
, XFS_AGINO_TO_AGBNO(mp
, agino
));
510 for (j
= 0; j
< XFS_INODES_PER_CHUNK
; j
++) {
512 xfs_dinode_core_t tdic
;
514 dip
=(xfs_dinode_t
*)((char *)iocur_top
->data
+ ((off
+ j
) << mp
->m_sb
.sb_inodelog
));
516 /* convert the core, then copy it back into the inode */
517 libxfs_xlate_dinode_core( (xfs_caddr_t
)
518 &dip
->di_core
, &tdic
, 1, ARCH_CONVERT
);
519 memcpy(&dip
->di_core
, &tdic
, sizeof(xfs_dinode_core_t
));
521 if (XFS_INOBT_IS_FREE(&rp
[i
], j
, ARCH_CONVERT
))
523 process_inode(agf
, agino
+ j
,
524 (xfs_dinode_t
*)((char *)iocur_top
->data
+ ((off
+ j
) << mp
->m_sb
.sb_inodelog
)));
530 pp
= XFS_BTREE_PTR_ADDR(mp
->m_sb
.sb_blocksize
, xfs_inobt
, block
, 1,
532 for (i
= 0; i
< INT_GET(block
->bb_numrecs
, ARCH_CONVERT
); i
++)
533 scan_sbtree(agf
, INT_GET(pp
[i
], ARCH_CONVERT
), level
, scanfunc_ino
, TYP_INOBT
);