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