]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - spaceman/freesp.c
xfsprogs: Release v6.7.0
[thirdparty/xfsprogs-dev.git] / spaceman / freesp.c
CommitLineData
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 19struct 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
27static int agcount;
28static xfs_agnumber_t *aglist;
10cfd61e 29static struct histent *hist;
cccf6abc
DC
30static int dumpflag;
31static long long equalsize;
32static long long multsize;
33static int histcount;
34static int seen1;
35static int summaryflag;
f77f9908 36static int gflag;
cccf6abc
DC
37static bool rtflag;
38static long long totblocks;
39static long long totexts;
40
41static cmdinfo_t freesp_cmd;
42
43static void
44addhistent(
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
61static void
62addtohist(
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
82static int
83hcmp(
84 const void *a,
85 const void *b)
86{
10cfd61e 87 return ((struct histent *)a)->low - ((struct histent *)b)->low;
cccf6abc
DC
88}
89
90static void
91histinit(
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
115static void
116printhist(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
130static int
131inaglist(
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
146static void
147scan_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
231static void
232aglistadd(
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
247static int
248init(
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;
323many_spec:
324 return command_usage(&freesp_cmd);
325}
326
327/*
328 * Report on freespace usage in xfs filesystem.
329 */
330static int
331freesp_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
363static void
364freesp_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
386void
387freesp_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