]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - spaceman/freesp.c
xfs_spaceman: Free space mapping command
[thirdparty/xfsprogs-dev.git] / spaceman / freesp.c
CommitLineData
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
29typedef struct histent
30{
31 long long low;
32 long long high;
33 long long count;
34 long long blocks;
35} histent_t;
36
37static int agcount;
38static xfs_agnumber_t *aglist;
39static histent_t *hist;
40static int dumpflag;
41static long long equalsize;
42static long long multsize;
43static int histcount;
44static int seen1;
45static int summaryflag;
46static bool rtflag;
47static long long totblocks;
48static long long totexts;
49
50static cmdinfo_t freesp_cmd;
51
52static void
53addhistent(
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
70static void
71addtohist(
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
91static int
92hcmp(
93 const void *a,
94 const void *b)
95{
96 return ((histent_t *)a)->low - ((histent_t *)b)->low;
97}
98
99static void
100histinit(
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
124static void
125printhist(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
139static int
140inaglist(
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
155static void
156scan_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}
229static void
230aglistadd(
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
245static int
246init(
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;
318many_spec:
319 return command_usage(&freesp_cmd);
320}
321
322/*
323 * Report on freespace usage in xfs filesystem.
324 */
325static int
326freesp_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
355static void
356freesp_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
377void
378freesp_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