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