]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - spaceman/freesp.c
libxfs: refactor manage_zones()
[thirdparty/xfsprogs-dev.git] / spaceman / freesp.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
4 * Copyright (c) 2012 Red Hat, Inc.
5 * Copyright (c) 2017 Oracle.
6 * All Rights Reserved.
7 */
8
9 #include "libxfs.h"
10 #include <linux/fiemap.h>
11 #include "command.h"
12 #include "init.h"
13 #include "path.h"
14 #include "space.h"
15 #include "input.h"
16
17 typedef struct histent
18 {
19 long long low;
20 long long high;
21 long long count;
22 long long blocks;
23 } histent_t;
24
25 static int agcount;
26 static xfs_agnumber_t *aglist;
27 static histent_t *hist;
28 static int dumpflag;
29 static long long equalsize;
30 static long long multsize;
31 static int histcount;
32 static int seen1;
33 static int summaryflag;
34 static int gflag;
35 static bool rtflag;
36 static long long totblocks;
37 static long long totexts;
38
39 static cmdinfo_t freesp_cmd;
40
41 static void
42 addhistent(
43 long long h)
44 {
45 if (histcount == INT_MAX) {
46 printf(_("Too many histogram buckets.\n"));
47 return;
48 }
49 hist = realloc(hist, (histcount + 1) * sizeof(*hist));
50 if (h == 0)
51 h = 1;
52 hist[histcount].low = h;
53 hist[histcount].count = hist[histcount].blocks = 0;
54 histcount++;
55 if (h == 1)
56 seen1 = 1;
57 }
58
59 static void
60 addtohist(
61 xfs_agnumber_t agno,
62 xfs_agblock_t agbno,
63 off64_t len)
64 {
65 long i;
66
67 if (dumpflag)
68 printf("%8d %8d %8"PRId64"\n", agno, agbno, len);
69 totexts++;
70 totblocks += len;
71 for (i = 0; i < histcount; i++) {
72 if (hist[i].high >= len) {
73 hist[i].count++;
74 hist[i].blocks += len;
75 break;
76 }
77 }
78 }
79
80 static int
81 hcmp(
82 const void *a,
83 const void *b)
84 {
85 return ((histent_t *)a)->low - ((histent_t *)b)->low;
86 }
87
88 static void
89 histinit(
90 long long maxlen)
91 {
92 long long i;
93
94 if (equalsize) {
95 for (i = 1; i < maxlen; i += equalsize)
96 addhistent(i);
97 } else if (multsize) {
98 for (i = 1; i < maxlen; i *= multsize)
99 addhistent(i);
100 } else {
101 if (!seen1)
102 addhistent(1);
103 qsort(hist, histcount, sizeof(*hist), hcmp);
104 }
105 for (i = 0; i < histcount; i++) {
106 if (i < histcount - 1)
107 hist[i].high = hist[i + 1].low - 1;
108 else
109 hist[i].high = maxlen;
110 }
111 }
112
113 static void
114 printhist(void)
115 {
116 int i;
117
118 printf("%7s %7s %7s %7s %6s\n",
119 _("from"), _("to"), _("extents"), _("blocks"), _("pct"));
120 for (i = 0; i < histcount; i++) {
121 if (hist[i].count)
122 printf("%7lld %7lld %7lld %7lld %6.2f\n", hist[i].low,
123 hist[i].high, hist[i].count, hist[i].blocks,
124 hist[i].blocks * 100.0 / totblocks);
125 }
126 }
127
128 static int
129 inaglist(
130 xfs_agnumber_t agno)
131 {
132 int i;
133
134 if (agcount == 0)
135 return 1;
136 for (i = 0; i < agcount; i++)
137 if (aglist[i] == agno)
138 return 1;
139 return 0;
140 }
141
142 #define NR_EXTENTS 128
143
144 static void
145 scan_ag(
146 xfs_agnumber_t agno)
147 {
148 struct fsmap_head *fsmap;
149 struct fsmap *extent;
150 struct fsmap *l, *h;
151 struct fsmap *p;
152 off64_t blocksize = file->geom.blocksize;
153 off64_t bperag;
154 off64_t aglen;
155 xfs_agblock_t agbno;
156 unsigned long long freeblks = 0;
157 unsigned long long freeexts = 0;
158 int ret;
159 int i;
160
161 bperag = (off64_t)file->geom.agblocks * blocksize;
162
163 fsmap = malloc(fsmap_sizeof(NR_EXTENTS));
164 if (!fsmap) {
165 fprintf(stderr, _("%s: fsmap malloc failed.\n"), progname);
166 exitcode = 1;
167 return;
168 }
169
170 memset(fsmap, 0, sizeof(*fsmap));
171 fsmap->fmh_count = NR_EXTENTS;
172 l = fsmap->fmh_keys;
173 h = fsmap->fmh_keys + 1;
174 if (agno != NULLAGNUMBER) {
175 l->fmr_physical = agno * bperag;
176 h->fmr_physical = ((agno + 1) * bperag) - 1;
177 l->fmr_device = h->fmr_device = file->fs_path.fs_datadev;
178 } else {
179 l->fmr_physical = 0;
180 h->fmr_physical = ULLONG_MAX;
181 l->fmr_device = h->fmr_device = file->fs_path.fs_rtdev;
182 }
183 h->fmr_owner = ULLONG_MAX;
184 h->fmr_flags = UINT_MAX;
185 h->fmr_offset = ULLONG_MAX;
186
187 while (true) {
188 ret = ioctl(file->fd, FS_IOC_GETFSMAP, fsmap);
189 if (ret < 0) {
190 fprintf(stderr, _("%s: FS_IOC_GETFSMAP [\"%s\"]: %s\n"),
191 progname, file->name, strerror(errno));
192 free(fsmap);
193 exitcode = 1;
194 return;
195 }
196
197 /* No more extents to map, exit */
198 if (!fsmap->fmh_entries)
199 break;
200
201 for (i = 0, extent = fsmap->fmh_recs;
202 i < fsmap->fmh_entries;
203 i++, extent++) {
204 if (!(extent->fmr_flags & FMR_OF_SPECIAL_OWNER) ||
205 extent->fmr_owner != XFS_FMR_OWN_FREE)
206 continue;
207 agbno = (extent->fmr_physical - (bperag * agno)) /
208 blocksize;
209 aglen = extent->fmr_length / blocksize;
210 freeblks += aglen;
211 freeexts++;
212
213 addtohist(agno, agbno, aglen);
214 }
215
216 p = &fsmap->fmh_recs[fsmap->fmh_entries - 1];
217 if (p->fmr_flags & FMR_OF_LAST)
218 break;
219 fsmap_advance(fsmap);
220 }
221
222 if (gflag) {
223 if (agno == NULLAGNUMBER)
224 printf(_(" rtdev %10llu %10llu\n"), freeexts,
225 freeblks);
226 else
227 printf(_("%10u %10llu %10llu\n"), agno, freeexts,
228 freeblks);
229 }
230 free(fsmap);
231 }
232
233 static void
234 aglistadd(
235 char *a)
236 {
237 xfs_agnumber_t x;
238
239 aglist = realloc(aglist, (agcount + 1) * sizeof(*aglist));
240 x = cvt_u32(a, 0);
241 if (errno) {
242 printf(_("Unrecognized AG number: %s\n"), a);
243 return;
244 }
245 aglist[agcount] = x;
246 agcount++;
247 }
248
249 static int
250 init(
251 int argc,
252 char **argv)
253 {
254 long long x;
255 int c;
256 int speced = 0; /* only one of -b -e -h or -m */
257
258 agcount = dumpflag = equalsize = multsize = optind = gflag = 0;
259 histcount = seen1 = summaryflag = 0;
260 totblocks = totexts = 0;
261 aglist = NULL;
262 hist = NULL;
263 rtflag = false;
264
265 while ((c = getopt(argc, argv, "a:bde:gh:m:rs")) != EOF) {
266 switch (c) {
267 case 'a':
268 aglistadd(optarg);
269 break;
270 case 'b':
271 if (speced)
272 goto many_spec;
273 multsize = 2;
274 speced = 1;
275 break;
276 case 'd':
277 dumpflag = 1;
278 break;
279 case 'e':
280 if (speced)
281 goto many_spec;
282 equalsize = cvt_s64(optarg, 0);
283 if (errno)
284 return command_usage(&freesp_cmd);
285 speced = 1;
286 break;
287 case 'g':
288 histcount = 0;
289 gflag++;
290 break;
291 case 'h':
292 if (speced && !histcount)
293 goto many_spec;
294 /* addhistent increments histcount */
295 x = cvt_s64(optarg, 0);
296 if (errno)
297 return command_usage(&freesp_cmd);
298 addhistent(x);
299 speced = 1;
300 break;
301 case 'm':
302 if (speced)
303 goto many_spec;
304 multsize = cvt_s64(optarg, 0);
305 if (errno)
306 return command_usage(&freesp_cmd);
307 speced = 1;
308 break;
309 case 'r':
310 rtflag = true;
311 break;
312 case 's':
313 summaryflag = 1;
314 break;
315 case '?':
316 default:
317 return command_usage(&freesp_cmd);
318 }
319 }
320 if (optind != argc)
321 return 0;
322 if (!speced)
323 multsize = 2;
324 histinit(file->geom.agblocks);
325 return 1;
326 many_spec:
327 return command_usage(&freesp_cmd);
328 }
329
330 /*
331 * Report on freespace usage in xfs filesystem.
332 */
333 static int
334 freesp_f(
335 int argc,
336 char **argv)
337 {
338 xfs_agnumber_t agno;
339
340 if (!init(argc, argv))
341 return 0;
342 if (gflag)
343 printf(_(" AG extents blocks\n"));
344 if (rtflag)
345 scan_ag(NULLAGNUMBER);
346 for (agno = 0; !rtflag && agno < file->geom.agcount; agno++) {
347 if (inaglist(agno))
348 scan_ag(agno);
349 }
350 if (histcount && !gflag)
351 printhist();
352 if (summaryflag) {
353 printf(_("total free extents %lld\n"), totexts);
354 printf(_("total free blocks %lld\n"), totblocks);
355 printf(_("average free extent size %g\n"),
356 (double)totblocks / (double)totexts);
357 }
358 if (aglist)
359 free(aglist);
360 if (hist)
361 free(hist);
362 return 0;
363 }
364
365 static void
366 freesp_help(void)
367 {
368 printf(_(
369 "\n"
370 "Examine filesystem free space\n"
371 "\n"
372 " -a agno -- Scan only the given AG agno.\n"
373 " -b -- binary histogram bin size\n"
374 " -d -- debug output\n"
375 " -e bsize -- Use fixed histogram bin size of bsize\n"
376 " -g -- Print only a per-AG summary.\n"
377 " -h hbsz -- Use custom histogram bin size of h1.\n"
378 " Multiple specifications are allowed.\n"
379 " -m bmult -- Use histogram bin size multiplier of bmult.\n"
380 " -r -- Display realtime device free space information.\n"
381 " -s -- Emit freespace summary information.\n"
382 "\n"
383 "Only one of -b, -e, -h, or -m may be specified.\n"
384 "\n"));
385
386 }
387
388 void
389 freesp_init(void)
390 {
391 freesp_cmd.name = "freesp";
392 freesp_cmd.altname = "fsp";
393 freesp_cmd.cfunc = freesp_f;
394 freesp_cmd.argmin = 0;
395 freesp_cmd.argmax = -1;
396 freesp_cmd.args = "[-dgrs] [-a agno]... [ -b | -e bsize | -h h1... | -m bmult ]";
397 freesp_cmd.flags = CMD_FLAG_ONESHOT;
398 freesp_cmd.oneline = _("Examine filesystem free space");
399 freesp_cmd.help = freesp_help;
400
401 add_command(&freesp_cmd);
402 }
403