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