]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2bd0ea18 | 2 | /* |
da23017d NS |
3 | * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. |
4 | * All Rights Reserved. | |
2bd0ea18 NS |
5 | */ |
6 | ||
6b803e5a | 7 | #include "libxfs.h" |
2bd0ea18 | 8 | #include "command.h" |
2bd0ea18 NS |
9 | #include "freesp.h" |
10 | #include "io.h" | |
11 | #include "type.h" | |
12 | #include "output.h" | |
4ca431fc | 13 | #include "init.h" |
2bd0ea18 | 14 | #include "malloc.h" |
c7a71a26 | 15 | #include "libfrog/histogram.h" |
2bd0ea18 NS |
16 | |
17 | static void addhistent(int h); | |
18 | static void addtohist(xfs_agnumber_t agno, xfs_agblock_t agbno, | |
19 | xfs_extlen_t len); | |
20 | static int freesp_f(int argc, char **argv); | |
21 | static void histinit(int maxlen); | |
22 | static int init(int argc, char **argv); | |
23 | static void printhist(void); | |
24 | static void scan_ag(xfs_agnumber_t agno); | |
b3563c19 | 25 | static void scanfunc_bno(struct xfs_btree_block *block, typnm_t typ, int level, |
2bd0ea18 | 26 | xfs_agf_t *agf); |
b3563c19 | 27 | static void scanfunc_cnt(struct xfs_btree_block *block, typnm_t typ, int level, |
2bd0ea18 NS |
28 | xfs_agf_t *agf); |
29 | static void scan_freelist(xfs_agf_t *agf); | |
30 | static void scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root, typnm_t typ, | |
31 | int nlevels, | |
b3563c19 | 32 | void (*func)(struct xfs_btree_block *block, typnm_t typ, |
2bd0ea18 NS |
33 | int level, xfs_agf_t *agf)); |
34 | static int usage(void); | |
35 | ||
36 | static int agcount; | |
37 | static xfs_agnumber_t *aglist; | |
732f8f5a | 38 | static int alignment; |
2bd0ea18 NS |
39 | static int countflag; |
40 | static int dumpflag; | |
41 | static int equalsize; | |
c7a71a26 | 42 | static struct histogram freesp_hist; |
2bd0ea18 NS |
43 | static int multsize; |
44 | static int seen1; | |
45 | static int summaryflag; | |
2bd0ea18 NS |
46 | |
47 | static const cmdinfo_t freesp_cmd = | |
48 | { "freesp", NULL, freesp_f, 0, -1, 0, | |
732f8f5a | 49 | "[-bcdfs] [-A alignment] [-a agno]... [-e binsize] [-h h1]... [-m binmult]", |
2bd0ea18 NS |
50 | "summarize free space for filesystem", NULL }; |
51 | ||
52 | static int | |
53 | inaglist( | |
54 | xfs_agnumber_t agno) | |
55 | { | |
56 | int i; | |
57 | ||
58 | if (agcount == 0) | |
59 | return 1; | |
60 | for (i = 0; i < agcount; i++) | |
61 | if (aglist[i] == agno) | |
62 | return 1; | |
63 | return 0; | |
64 | } | |
65 | ||
66 | /* | |
67 | * Report on freespace usage in xfs filesystem. | |
68 | */ | |
69 | static int | |
70 | freesp_f( | |
71 | int argc, | |
72 | char **argv) | |
73 | { | |
74 | xfs_agnumber_t agno; | |
75 | ||
76 | if (!init(argc, argv)) | |
77 | return 0; | |
982e5c7e ES |
78 | |
79 | if (dumpflag) | |
80 | dbprintf("%8s %8s %8s\n", "agno", "agbno", "len"); | |
81 | ||
2bd0ea18 NS |
82 | for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) { |
83 | if (inaglist(agno)) | |
84 | scan_ag(agno); | |
85 | } | |
c7a71a26 | 86 | if (hist_buckets(&freesp_hist)) |
2bd0ea18 NS |
87 | printhist(); |
88 | if (summaryflag) { | |
c7a71a26 DW |
89 | struct histogram_strings hstr = { |
90 | .sum = _("total free blocks"), | |
91 | .observations = _("total free extents"), | |
92 | .averages = _("average free extent size"), | |
93 | }; | |
94 | ||
95 | hist_summarize(&freesp_hist, &hstr); | |
2bd0ea18 NS |
96 | } |
97 | if (aglist) | |
98 | xfree(aglist); | |
c7a71a26 | 99 | hist_free(&freesp_hist); |
2bd0ea18 NS |
100 | return 0; |
101 | } | |
102 | ||
103 | void | |
104 | freesp_init(void) | |
105 | { | |
106 | add_command(&freesp_cmd); | |
107 | } | |
108 | ||
109 | static void | |
110 | aglistadd( | |
111 | char *a) | |
112 | { | |
113 | aglist = xrealloc(aglist, (agcount + 1) * sizeof(*aglist)); | |
114 | aglist[agcount] = (xfs_agnumber_t)atoi(a); | |
115 | agcount++; | |
116 | } | |
117 | ||
118 | static int | |
119 | init( | |
120 | int argc, | |
121 | char **argv) | |
122 | { | |
123 | int c; | |
124 | int speced = 0; | |
125 | ||
126 | agcount = countflag = dumpflag = equalsize = multsize = optind = 0; | |
c7a71a26 | 127 | seen1 = summaryflag = 0; |
2bd0ea18 | 128 | aglist = NULL; |
c7a71a26 | 129 | |
732f8f5a | 130 | while ((c = getopt(argc, argv, "A:a:bcde:h:m:s")) != EOF) { |
2bd0ea18 | 131 | switch (c) { |
732f8f5a ES |
132 | case 'A': |
133 | alignment = atoi(optarg); | |
134 | break; | |
2bd0ea18 NS |
135 | case 'a': |
136 | aglistadd(optarg); | |
137 | break; | |
138 | case 'b': | |
dfc130f3 | 139 | if (speced) |
2bd0ea18 NS |
140 | return usage(); |
141 | multsize = 2; | |
142 | speced = 1; | |
143 | break; | |
144 | case 'c': | |
145 | countflag = 1; | |
146 | break; | |
147 | case 'd': | |
148 | dumpflag = 1; | |
149 | break; | |
150 | case 'e': | |
151 | if (speced) | |
152 | return usage(); | |
153 | equalsize = atoi(optarg); | |
154 | speced = 1; | |
155 | break; | |
156 | case 'h': | |
c7a71a26 | 157 | if (speced && hist_buckets(&freesp_hist) == 0) |
2bd0ea18 NS |
158 | return usage(); |
159 | addhistent(atoi(optarg)); | |
160 | speced = 1; | |
161 | break; | |
162 | case 'm': | |
163 | if (speced) | |
164 | return usage(); | |
165 | multsize = atoi(optarg); | |
166 | speced = 1; | |
167 | break; | |
168 | case 's': | |
169 | summaryflag = 1; | |
170 | break; | |
78aeaffd | 171 | default: |
2bd0ea18 NS |
172 | return usage(); |
173 | } | |
174 | } | |
175 | if (optind != argc) | |
176 | return usage(); | |
177 | if (!speced) | |
178 | multsize = 2; | |
179 | histinit((int)mp->m_sb.sb_agblocks); | |
180 | return 1; | |
181 | } | |
182 | ||
183 | static int | |
184 | usage(void) | |
185 | { | |
6fe93c03 | 186 | dbprintf(_("freesp arguments: [-bcds] [-a agno] [-e binsize] [-h h1]... " |
9ee7055c | 187 | "[-m binmult]\n")); |
2bd0ea18 NS |
188 | return 0; |
189 | } | |
190 | ||
191 | static void | |
192 | scan_ag( | |
193 | xfs_agnumber_t agno) | |
194 | { | |
195 | xfs_agf_t *agf; | |
196 | ||
197 | push_cur(); | |
5e656dbb BN |
198 | set_cur(&typtab[TYP_AGF], XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)), |
199 | XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL); | |
2bd0ea18 NS |
200 | agf = iocur_top->data; |
201 | scan_freelist(agf); | |
202 | if (countflag) | |
ea3d0c07 CH |
203 | scan_sbtree(agf, be32_to_cpu(agf->agf_cnt_root), |
204 | TYP_CNTBT, be32_to_cpu(agf->agf_cnt_level), | |
2bd0ea18 NS |
205 | scanfunc_cnt); |
206 | else | |
ea3d0c07 CH |
207 | scan_sbtree(agf, be32_to_cpu(agf->agf_bno_root), |
208 | TYP_BNOBT, be32_to_cpu(agf->agf_bno_level), | |
2bd0ea18 NS |
209 | scanfunc_bno); |
210 | pop_cur(); | |
211 | } | |
212 | ||
581c24aa DW |
213 | static int |
214 | scan_agfl( | |
215 | struct xfs_mount *mp, | |
216 | xfs_agblock_t bno, | |
217 | void *priv) | |
218 | { | |
219 | addtohist(*(xfs_agnumber_t *)priv, bno, 1); | |
220 | return 0; | |
221 | } | |
222 | ||
2bd0ea18 NS |
223 | static void |
224 | scan_freelist( | |
225 | xfs_agf_t *agf) | |
226 | { | |
5e656dbb | 227 | xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno); |
2bd0ea18 | 228 | |
5e656dbb | 229 | if (be32_to_cpu(agf->agf_flcount) == 0) |
2bd0ea18 NS |
230 | return; |
231 | push_cur(); | |
5e656dbb BN |
232 | set_cur(&typtab[TYP_AGFL], XFS_AG_DADDR(mp, seqno, XFS_AGFL_DADDR(mp)), |
233 | XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL); | |
84232448 | 234 | |
a529cc7f | 235 | /* verify agf values before proceeding */ |
b8165508 DC |
236 | if (be32_to_cpu(agf->agf_flfirst) >= libxfs_agfl_size(mp) || |
237 | be32_to_cpu(agf->agf_fllast) >= libxfs_agfl_size(mp)) { | |
a529cc7f | 238 | dbprintf(_("agf %d freelist blocks bad, skipping " |
581c24aa | 239 | "freelist scan\n"), seqno); |
a529cc7f ES |
240 | pop_cur(); |
241 | return; | |
242 | } | |
243 | ||
581c24aa | 244 | libxfs_agfl_walk(mp, agf, iocur_top->bp, scan_agfl, &seqno); |
2bd0ea18 NS |
245 | pop_cur(); |
246 | } | |
247 | ||
248 | static void | |
249 | scan_sbtree( | |
250 | xfs_agf_t *agf, | |
251 | xfs_agblock_t root, | |
252 | typnm_t typ, | |
253 | int nlevels, | |
b3563c19 | 254 | void (*func)(struct xfs_btree_block *block, |
2bd0ea18 NS |
255 | typnm_t typ, |
256 | int level, | |
257 | xfs_agf_t *agf)) | |
258 | { | |
5e656dbb | 259 | xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno); |
30b0c726 | 260 | |
2bd0ea18 | 261 | push_cur(); |
30b0c726 | 262 | set_cur(&typtab[typ], XFS_AGB_TO_DADDR(mp, seqno, root), |
2bd0ea18 | 263 | blkbb, DB_RING_IGN, NULL); |
30b0c726 | 264 | if (iocur_top->data == NULL) { |
9ee7055c | 265 | dbprintf(_("can't read btree block %u/%u\n"), seqno, root); |
dfc130f3 | 266 | return; |
30b0c726 | 267 | } |
b3563c19 | 268 | (*func)(iocur_top->data, typ, nlevels - 1, agf); |
2bd0ea18 NS |
269 | pop_cur(); |
270 | } | |
271 | ||
272 | /*ARGSUSED*/ | |
273 | static void | |
274 | scanfunc_bno( | |
b3563c19 | 275 | struct xfs_btree_block *block, |
2bd0ea18 NS |
276 | typnm_t typ, |
277 | int level, | |
278 | xfs_agf_t *agf) | |
279 | { | |
2bd0ea18 NS |
280 | int i; |
281 | xfs_alloc_ptr_t *pp; | |
282 | xfs_alloc_rec_t *rp; | |
283 | ||
c2907bd7 DC |
284 | if (!(be32_to_cpu(block->bb_magic) == XFS_ABTB_MAGIC || |
285 | be32_to_cpu(block->bb_magic) == XFS_ABTB_CRC_MAGIC)) | |
65b04d8c PW |
286 | return; |
287 | ||
2bd0ea18 | 288 | if (level == 0) { |
b3563c19 | 289 | rp = XFS_ALLOC_REC_ADDR(mp, block, 1); |
5e656dbb BN |
290 | for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) |
291 | addtohist(be32_to_cpu(agf->agf_seqno), | |
292 | be32_to_cpu(rp[i].ar_startblock), | |
293 | be32_to_cpu(rp[i].ar_blockcount)); | |
2bd0ea18 NS |
294 | return; |
295 | } | |
b3563c19 | 296 | pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]); |
5e656dbb BN |
297 | for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) |
298 | scan_sbtree(agf, be32_to_cpu(pp[i]), typ, level, scanfunc_bno); | |
2bd0ea18 NS |
299 | } |
300 | ||
301 | static void | |
302 | scanfunc_cnt( | |
b3563c19 | 303 | struct xfs_btree_block *block, |
2bd0ea18 NS |
304 | typnm_t typ, |
305 | int level, | |
306 | xfs_agf_t *agf) | |
307 | { | |
2bd0ea18 NS |
308 | int i; |
309 | xfs_alloc_ptr_t *pp; | |
310 | xfs_alloc_rec_t *rp; | |
311 | ||
c2907bd7 DC |
312 | if (!(be32_to_cpu(block->bb_magic) == XFS_ABTC_MAGIC || |
313 | be32_to_cpu(block->bb_magic) == XFS_ABTC_CRC_MAGIC)) | |
65b04d8c PW |
314 | return; |
315 | ||
2bd0ea18 | 316 | if (level == 0) { |
b3563c19 | 317 | rp = XFS_ALLOC_REC_ADDR(mp, block, 1); |
5e656dbb BN |
318 | for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) |
319 | addtohist(be32_to_cpu(agf->agf_seqno), | |
320 | be32_to_cpu(rp[i].ar_startblock), | |
321 | be32_to_cpu(rp[i].ar_blockcount)); | |
2bd0ea18 NS |
322 | return; |
323 | } | |
b3563c19 | 324 | pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]); |
5e656dbb BN |
325 | for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) |
326 | scan_sbtree(agf, be32_to_cpu(pp[i]), typ, level, scanfunc_cnt); | |
2bd0ea18 NS |
327 | } |
328 | ||
329 | static void | |
330 | addhistent( | |
331 | int h) | |
332 | { | |
c7a71a26 | 333 | hist_add_bucket(&freesp_hist, h); |
2bd0ea18 NS |
334 | } |
335 | ||
336 | static void | |
337 | addtohist( | |
338 | xfs_agnumber_t agno, | |
339 | xfs_agblock_t agbno, | |
340 | xfs_extlen_t len) | |
341 | { | |
732f8f5a ES |
342 | if (alignment && (XFS_AGB_TO_FSB(mp,agno,agbno) % alignment)) |
343 | return; | |
344 | ||
2bd0ea18 NS |
345 | if (dumpflag) |
346 | dbprintf("%8d %8d %8d\n", agno, agbno, len); | |
c7a71a26 | 347 | hist_add(&freesp_hist, len); |
2bd0ea18 NS |
348 | } |
349 | ||
350 | static void | |
351 | histinit( | |
352 | int maxlen) | |
353 | { | |
354 | int i; | |
355 | ||
c7a71a26 | 356 | hist_init(&freesp_hist); |
2bd0ea18 NS |
357 | if (equalsize) { |
358 | for (i = 1; i < maxlen; i += equalsize) | |
359 | addhistent(i); | |
360 | } else if (multsize) { | |
361 | for (i = 1; i < maxlen; i *= multsize) | |
362 | addhistent(i); | |
363 | } else { | |
364 | if (!seen1) | |
365 | addhistent(1); | |
2bd0ea18 | 366 | } |
c7a71a26 | 367 | hist_prepare(&freesp_hist, maxlen); |
2bd0ea18 NS |
368 | } |
369 | ||
370 | static void | |
371 | printhist(void) | |
372 | { | |
c7a71a26 DW |
373 | struct histogram_strings hstr = { |
374 | .sum = _("blocks"), | |
375 | .observations = _("extents"), | |
376 | }; | |
2bd0ea18 | 377 | |
c7a71a26 | 378 | hist_print(&freesp_hist, &hstr); |
2bd0ea18 | 379 | } |