]>
Commit | Line | Data |
---|---|---|
1 | // SPDX-License-Identifier: GPL-2.0 | |
2 | /* | |
3 | * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. | |
4 | * All Rights Reserved. | |
5 | */ | |
6 | ||
7 | #include "libxfs.h" | |
8 | #include "command.h" | |
9 | #include "freesp.h" | |
10 | #include "io.h" | |
11 | #include "type.h" | |
12 | #include "output.h" | |
13 | #include "init.h" | |
14 | #include "malloc.h" | |
15 | #include "libfrog/histogram.h" | |
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); | |
25 | static void scanfunc_bno(struct xfs_btree_block *block, typnm_t typ, int level, | |
26 | xfs_agf_t *agf); | |
27 | static void scanfunc_cnt(struct xfs_btree_block *block, typnm_t typ, int level, | |
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, | |
32 | void (*func)(struct xfs_btree_block *block, typnm_t typ, | |
33 | int level, xfs_agf_t *agf)); | |
34 | static int usage(void); | |
35 | ||
36 | static int agcount; | |
37 | static xfs_agnumber_t *aglist; | |
38 | static int alignment; | |
39 | static int countflag; | |
40 | static int dumpflag; | |
41 | static int equalsize; | |
42 | static struct histogram freesp_hist; | |
43 | static int multsize; | |
44 | static int seen1; | |
45 | static int summaryflag; | |
46 | ||
47 | static const cmdinfo_t freesp_cmd = | |
48 | { "freesp", NULL, freesp_f, 0, -1, 0, | |
49 | "[-bcdfs] [-A alignment] [-a agno]... [-e binsize] [-h h1]... [-m binmult]", | |
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; | |
78 | ||
79 | if (dumpflag) | |
80 | dbprintf("%8s %8s %8s\n", "agno", "agbno", "len"); | |
81 | ||
82 | for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) { | |
83 | if (inaglist(agno)) | |
84 | scan_ag(agno); | |
85 | } | |
86 | if (hist_buckets(&freesp_hist)) | |
87 | printhist(); | |
88 | if (summaryflag) { | |
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); | |
96 | } | |
97 | if (aglist) | |
98 | xfree(aglist); | |
99 | hist_free(&freesp_hist); | |
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; | |
127 | seen1 = summaryflag = 0; | |
128 | aglist = NULL; | |
129 | ||
130 | while ((c = getopt(argc, argv, "A:a:bcde:h:m:s")) != EOF) { | |
131 | switch (c) { | |
132 | case 'A': | |
133 | alignment = atoi(optarg); | |
134 | break; | |
135 | case 'a': | |
136 | aglistadd(optarg); | |
137 | break; | |
138 | case 'b': | |
139 | if (speced) | |
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': | |
157 | if (speced && hist_buckets(&freesp_hist) == 0) | |
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; | |
171 | default: | |
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 | { | |
186 | dbprintf(_("freesp arguments: [-bcds] [-a agno] [-e binsize] [-h h1]... " | |
187 | "[-m binmult]\n")); | |
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(); | |
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); | |
200 | agf = iocur_top->data; | |
201 | scan_freelist(agf); | |
202 | if (countflag) | |
203 | scan_sbtree(agf, be32_to_cpu(agf->agf_cnt_root), | |
204 | TYP_CNTBT, be32_to_cpu(agf->agf_cnt_level), | |
205 | scanfunc_cnt); | |
206 | else | |
207 | scan_sbtree(agf, be32_to_cpu(agf->agf_bno_root), | |
208 | TYP_BNOBT, be32_to_cpu(agf->agf_bno_level), | |
209 | scanfunc_bno); | |
210 | pop_cur(); | |
211 | } | |
212 | ||
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 | ||
223 | static void | |
224 | scan_freelist( | |
225 | xfs_agf_t *agf) | |
226 | { | |
227 | xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno); | |
228 | ||
229 | if (be32_to_cpu(agf->agf_flcount) == 0) | |
230 | return; | |
231 | push_cur(); | |
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); | |
234 | ||
235 | /* verify agf values before proceeding */ | |
236 | if (be32_to_cpu(agf->agf_flfirst) >= libxfs_agfl_size(mp) || | |
237 | be32_to_cpu(agf->agf_fllast) >= libxfs_agfl_size(mp)) { | |
238 | dbprintf(_("agf %d freelist blocks bad, skipping " | |
239 | "freelist scan\n"), seqno); | |
240 | pop_cur(); | |
241 | return; | |
242 | } | |
243 | ||
244 | libxfs_agfl_walk(mp, agf, iocur_top->bp, scan_agfl, &seqno); | |
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, | |
254 | void (*func)(struct xfs_btree_block *block, | |
255 | typnm_t typ, | |
256 | int level, | |
257 | xfs_agf_t *agf)) | |
258 | { | |
259 | xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno); | |
260 | ||
261 | push_cur(); | |
262 | set_cur(&typtab[typ], XFS_AGB_TO_DADDR(mp, seqno, root), | |
263 | blkbb, DB_RING_IGN, NULL); | |
264 | if (iocur_top->data == NULL) { | |
265 | dbprintf(_("can't read btree block %u/%u\n"), seqno, root); | |
266 | return; | |
267 | } | |
268 | (*func)(iocur_top->data, typ, nlevels - 1, agf); | |
269 | pop_cur(); | |
270 | } | |
271 | ||
272 | /*ARGSUSED*/ | |
273 | static void | |
274 | scanfunc_bno( | |
275 | struct xfs_btree_block *block, | |
276 | typnm_t typ, | |
277 | int level, | |
278 | xfs_agf_t *agf) | |
279 | { | |
280 | int i; | |
281 | xfs_alloc_ptr_t *pp; | |
282 | xfs_alloc_rec_t *rp; | |
283 | ||
284 | if (!(be32_to_cpu(block->bb_magic) == XFS_ABTB_MAGIC || | |
285 | be32_to_cpu(block->bb_magic) == XFS_ABTB_CRC_MAGIC)) | |
286 | return; | |
287 | ||
288 | if (level == 0) { | |
289 | rp = XFS_ALLOC_REC_ADDR(mp, block, 1); | |
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)); | |
294 | return; | |
295 | } | |
296 | pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]); | |
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); | |
299 | } | |
300 | ||
301 | static void | |
302 | scanfunc_cnt( | |
303 | struct xfs_btree_block *block, | |
304 | typnm_t typ, | |
305 | int level, | |
306 | xfs_agf_t *agf) | |
307 | { | |
308 | int i; | |
309 | xfs_alloc_ptr_t *pp; | |
310 | xfs_alloc_rec_t *rp; | |
311 | ||
312 | if (!(be32_to_cpu(block->bb_magic) == XFS_ABTC_MAGIC || | |
313 | be32_to_cpu(block->bb_magic) == XFS_ABTC_CRC_MAGIC)) | |
314 | return; | |
315 | ||
316 | if (level == 0) { | |
317 | rp = XFS_ALLOC_REC_ADDR(mp, block, 1); | |
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)); | |
322 | return; | |
323 | } | |
324 | pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]); | |
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); | |
327 | } | |
328 | ||
329 | static void | |
330 | addhistent( | |
331 | int h) | |
332 | { | |
333 | hist_add_bucket(&freesp_hist, h); | |
334 | } | |
335 | ||
336 | static void | |
337 | addtohist( | |
338 | xfs_agnumber_t agno, | |
339 | xfs_agblock_t agbno, | |
340 | xfs_extlen_t len) | |
341 | { | |
342 | if (alignment && (XFS_AGB_TO_FSB(mp,agno,agbno) % alignment)) | |
343 | return; | |
344 | ||
345 | if (dumpflag) | |
346 | dbprintf("%8d %8d %8d\n", agno, agbno, len); | |
347 | hist_add(&freesp_hist, len); | |
348 | } | |
349 | ||
350 | static void | |
351 | histinit( | |
352 | int maxlen) | |
353 | { | |
354 | int i; | |
355 | ||
356 | hist_init(&freesp_hist); | |
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); | |
366 | } | |
367 | hist_prepare(&freesp_hist, maxlen); | |
368 | } | |
369 | ||
370 | static void | |
371 | printhist(void) | |
372 | { | |
373 | struct histogram_strings hstr = { | |
374 | .sum = _("blocks"), | |
375 | .observations = _("extents"), | |
376 | }; | |
377 | ||
378 | hist_print(&freesp_hist, &hstr); | |
379 | } |