]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - db/freesp.c
c3ca2f3ae20338261c774feed8520e21b8999a6e
[thirdparty/xfsprogs-dev.git] / db / freesp.c
1 /*
2 * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
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
7 * published by the Free Software Foundation.
8 *
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.
13 *
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
17 */
18
19 #include "libxfs.h"
20 #include "command.h"
21 #include "freesp.h"
22 #include "io.h"
23 #include "type.h"
24 #include "output.h"
25 #include "init.h"
26 #include "malloc.h"
27
28 typedef struct histent
29 {
30 int low;
31 int high;
32 long long count;
33 long long blocks;
34 } histent_t;
35
36 static void addhistent(int h);
37 static void addtohist(xfs_agnumber_t agno, xfs_agblock_t agbno,
38 xfs_extlen_t len);
39 static int freesp_f(int argc, char **argv);
40 static void histinit(int maxlen);
41 static int init(int argc, char **argv);
42 static void printhist(void);
43 static void scan_ag(xfs_agnumber_t agno);
44 static void scanfunc_bno(struct xfs_btree_block *block, typnm_t typ, int level,
45 xfs_agf_t *agf);
46 static void scanfunc_cnt(struct xfs_btree_block *block, typnm_t typ, int level,
47 xfs_agf_t *agf);
48 static void scan_freelist(xfs_agf_t *agf);
49 static void scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root, typnm_t typ,
50 int nlevels,
51 void (*func)(struct xfs_btree_block *block, typnm_t typ,
52 int level, xfs_agf_t *agf));
53 static int usage(void);
54
55 static int agcount;
56 static xfs_agnumber_t *aglist;
57 static int alignment;
58 static int countflag;
59 static int dumpflag;
60 static int equalsize;
61 static histent_t *hist;
62 static int histcount;
63 static int multsize;
64 static int seen1;
65 static int summaryflag;
66 static long long totblocks;
67 static long long totexts;
68
69 static const cmdinfo_t freesp_cmd =
70 { "freesp", NULL, freesp_f, 0, -1, 0,
71 "[-bcdfs] [-A alignment] [-a agno]... [-e binsize] [-h h1]... [-m binmult]",
72 "summarize free space for filesystem", NULL };
73
74 static int
75 inaglist(
76 xfs_agnumber_t agno)
77 {
78 int i;
79
80 if (agcount == 0)
81 return 1;
82 for (i = 0; i < agcount; i++)
83 if (aglist[i] == agno)
84 return 1;
85 return 0;
86 }
87
88 /*
89 * Report on freespace usage in xfs filesystem.
90 */
91 static int
92 freesp_f(
93 int argc,
94 char **argv)
95 {
96 xfs_agnumber_t agno;
97
98 if (!init(argc, argv))
99 return 0;
100
101 if (dumpflag)
102 dbprintf("%8s %8s %8s\n", "agno", "agbno", "len");
103
104 for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
105 if (inaglist(agno))
106 scan_ag(agno);
107 }
108 if (histcount)
109 printhist();
110 if (summaryflag) {
111 dbprintf(_("total free extents %lld\n"), totexts);
112 dbprintf(_("total free blocks %lld\n"), totblocks);
113 dbprintf(_("average free extent size %g\n"),
114 (double)totblocks / (double)totexts);
115 }
116 if (aglist)
117 xfree(aglist);
118 if (hist)
119 xfree(hist);
120 return 0;
121 }
122
123 void
124 freesp_init(void)
125 {
126 add_command(&freesp_cmd);
127 }
128
129 static void
130 aglistadd(
131 char *a)
132 {
133 aglist = xrealloc(aglist, (agcount + 1) * sizeof(*aglist));
134 aglist[agcount] = (xfs_agnumber_t)atoi(a);
135 agcount++;
136 }
137
138 static int
139 init(
140 int argc,
141 char **argv)
142 {
143 int c;
144 int speced = 0;
145
146 agcount = countflag = dumpflag = equalsize = multsize = optind = 0;
147 histcount = seen1 = summaryflag = 0;
148 totblocks = totexts = 0;
149 aglist = NULL;
150 hist = NULL;
151 while ((c = getopt(argc, argv, "A:a:bcde:h:m:s")) != EOF) {
152 switch (c) {
153 case 'A':
154 alignment = atoi(optarg);
155 break;
156 case 'a':
157 aglistadd(optarg);
158 break;
159 case 'b':
160 if (speced)
161 return usage();
162 multsize = 2;
163 speced = 1;
164 break;
165 case 'c':
166 countflag = 1;
167 break;
168 case 'd':
169 dumpflag = 1;
170 break;
171 case 'e':
172 if (speced)
173 return usage();
174 equalsize = atoi(optarg);
175 speced = 1;
176 break;
177 case 'h':
178 if (speced && !histcount)
179 return usage();
180 addhistent(atoi(optarg));
181 speced = 1;
182 break;
183 case 'm':
184 if (speced)
185 return usage();
186 multsize = atoi(optarg);
187 speced = 1;
188 break;
189 case 's':
190 summaryflag = 1;
191 break;
192 case '?':
193 return usage();
194 }
195 }
196 if (optind != argc)
197 return usage();
198 if (!speced)
199 multsize = 2;
200 histinit((int)mp->m_sb.sb_agblocks);
201 return 1;
202 }
203
204 static int
205 usage(void)
206 {
207 dbprintf(_("freesp arguments: [-bcds] [-a agno] [-e binsize] [-h h1]... "
208 "[-m binmult]\n"));
209 return 0;
210 }
211
212 static void
213 scan_ag(
214 xfs_agnumber_t agno)
215 {
216 xfs_agf_t *agf;
217
218 push_cur();
219 set_cur(&typtab[TYP_AGF], XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
220 XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
221 agf = iocur_top->data;
222 scan_freelist(agf);
223 if (countflag)
224 scan_sbtree(agf, be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNT]),
225 TYP_CNTBT, be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]),
226 scanfunc_cnt);
227 else
228 scan_sbtree(agf, be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNO]),
229 TYP_BNOBT, be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]),
230 scanfunc_bno);
231 pop_cur();
232 }
233
234 static void
235 scan_freelist(
236 xfs_agf_t *agf)
237 {
238 xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
239 xfs_agfl_t *agfl;
240 xfs_agblock_t bno;
241 int i;
242 __be32 *agfl_bno;
243
244 if (be32_to_cpu(agf->agf_flcount) == 0)
245 return;
246 push_cur();
247 set_cur(&typtab[TYP_AGFL], XFS_AG_DADDR(mp, seqno, XFS_AGFL_DADDR(mp)),
248 XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
249 agfl = iocur_top->data;
250 i = be32_to_cpu(agf->agf_flfirst);
251
252 /* open coded XFS_BUF_TO_AGFL_BNO */
253 agfl_bno = xfs_sb_version_hascrc(&mp->m_sb) ? &agfl->agfl_bno[0]
254 : (__be32 *)agfl;
255
256 /* verify agf values before proceeding */
257 if (be32_to_cpu(agf->agf_flfirst) >= XFS_AGFL_SIZE(mp) ||
258 be32_to_cpu(agf->agf_fllast) >= XFS_AGFL_SIZE(mp)) {
259 dbprintf(_("agf %d freelist blocks bad, skipping "
260 "freelist scan\n"), i);
261 pop_cur();
262 return;
263 }
264
265 for (;;) {
266 bno = be32_to_cpu(agfl_bno[i]);
267 addtohist(seqno, bno, 1);
268 if (i == be32_to_cpu(agf->agf_fllast))
269 break;
270 if (++i == XFS_AGFL_SIZE(mp))
271 i = 0;
272 }
273 pop_cur();
274 }
275
276 static void
277 scan_sbtree(
278 xfs_agf_t *agf,
279 xfs_agblock_t root,
280 typnm_t typ,
281 int nlevels,
282 void (*func)(struct xfs_btree_block *block,
283 typnm_t typ,
284 int level,
285 xfs_agf_t *agf))
286 {
287 xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
288
289 push_cur();
290 set_cur(&typtab[typ], XFS_AGB_TO_DADDR(mp, seqno, root),
291 blkbb, DB_RING_IGN, NULL);
292 if (iocur_top->data == NULL) {
293 dbprintf(_("can't read btree block %u/%u\n"), seqno, root);
294 return;
295 }
296 (*func)(iocur_top->data, typ, nlevels - 1, agf);
297 pop_cur();
298 }
299
300 /*ARGSUSED*/
301 static void
302 scanfunc_bno(
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_ABTB_MAGIC ||
313 be32_to_cpu(block->bb_magic) == XFS_ABTB_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_bno);
327 }
328
329 static void
330 scanfunc_cnt(
331 struct xfs_btree_block *block,
332 typnm_t typ,
333 int level,
334 xfs_agf_t *agf)
335 {
336 int i;
337 xfs_alloc_ptr_t *pp;
338 xfs_alloc_rec_t *rp;
339
340 if (!(be32_to_cpu(block->bb_magic) == XFS_ABTC_MAGIC ||
341 be32_to_cpu(block->bb_magic) == XFS_ABTC_CRC_MAGIC))
342 return;
343
344 if (level == 0) {
345 rp = XFS_ALLOC_REC_ADDR(mp, block, 1);
346 for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
347 addtohist(be32_to_cpu(agf->agf_seqno),
348 be32_to_cpu(rp[i].ar_startblock),
349 be32_to_cpu(rp[i].ar_blockcount));
350 return;
351 }
352 pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
353 for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
354 scan_sbtree(agf, be32_to_cpu(pp[i]), typ, level, scanfunc_cnt);
355 }
356
357 static void
358 addhistent(
359 int h)
360 {
361 hist = xrealloc(hist, (histcount + 1) * sizeof(*hist));
362 if (h == 0)
363 h = 1;
364 hist[histcount].low = h;
365 hist[histcount].count = hist[histcount].blocks = 0;
366 histcount++;
367 if (h == 1)
368 seen1 = 1;
369 }
370
371 static void
372 addtohist(
373 xfs_agnumber_t agno,
374 xfs_agblock_t agbno,
375 xfs_extlen_t len)
376 {
377 int i;
378
379 if (alignment && (XFS_AGB_TO_FSB(mp,agno,agbno) % alignment))
380 return;
381
382 if (dumpflag)
383 dbprintf("%8d %8d %8d\n", agno, agbno, len);
384 totexts++;
385 totblocks += len;
386 for (i = 0; i < histcount; i++) {
387 if (hist[i].high >= len) {
388 hist[i].count++;
389 hist[i].blocks += len;
390 break;
391 }
392 }
393 }
394
395 static int
396 hcmp(
397 const void *a,
398 const void *b)
399 {
400 return ((histent_t *)a)->low - ((histent_t *)b)->low;
401 }
402
403 static void
404 histinit(
405 int maxlen)
406 {
407 int i;
408
409 if (equalsize) {
410 for (i = 1; i < maxlen; i += equalsize)
411 addhistent(i);
412 } else if (multsize) {
413 for (i = 1; i < maxlen; i *= multsize)
414 addhistent(i);
415 } else {
416 if (!seen1)
417 addhistent(1);
418 qsort(hist, histcount, sizeof(*hist), hcmp);
419 }
420 for (i = 0; i < histcount; i++) {
421 if (i < histcount - 1)
422 hist[i].high = hist[i + 1].low - 1;
423 else
424 hist[i].high = maxlen;
425 }
426 }
427
428 static void
429 printhist(void)
430 {
431 int i;
432
433 dbprintf("%7s %7s %7s %7s %6s\n",
434 _("from"), _("to"), _("extents"), _("blocks"), _("pct"));
435 for (i = 0; i < histcount; i++) {
436 if (hist[i].count)
437 dbprintf("%7d %7d %7lld %7lld %6.2f\n", hist[i].low,
438 hist[i].high, hist[i].count, hist[i].blocks,
439 hist[i].blocks * 100.0 / totblocks);
440 }
441 }