]>
git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - misc/e2freefrag.c
2 * e2freefrag - report filesystem free-space fragmentation
4 * Copyright (C) 2009 Sun Microsystems, Inc.
6 * Author: Rupesh Thakare <rupesh@sun.com>
7 * Andreas Dilger <adilger@sun.com>
10 * This file may be redistributed under the terms of the GNU Public
28 #if defined(HAVE_EXT2_IOCTLS) && !defined(DEBUGFS)
29 # include <sys/ioctl.h>
30 # include <sys/types.h>
31 # include <sys/stat.h>
36 #include "ext2fs/ext2_fs.h"
37 #include "ext2fs/ext2fs.h"
38 #include "e2freefrag.h"
40 #if defined(HAVE_EXT2_IOCTLS) && !defined(DEBUGFS)
41 # ifdef HAVE_LINUX_FSMAP_H
42 # include <linux/fsmap.h>
51 static void usage(const char *prog
)
53 fprintf(stderr
, "usage: %s [-c chunksize in kb] [-h] "
54 "device_name\n", prog
);
60 static int ul_log2(unsigned long arg
)
72 static void init_chunk_info(ext2_filsys fs
, struct chunk_info
*info
)
76 info
->blocksize_bits
= ul_log2((unsigned long)fs
->blocksize
);
77 if (info
->chunkbytes
) {
78 info
->chunkbits
= ul_log2(info
->chunkbytes
);
79 info
->blks_in_chunk
= info
->chunkbytes
>> info
->blocksize_bits
;
81 info
->chunkbits
= ul_log2(DEFAULT_CHUNKSIZE
);
82 info
->blks_in_chunk
= DEFAULT_CHUNKSIZE
>> info
->blocksize_bits
;
86 info
->max
= info
->avg
= 0;
87 info
->real_free_chunks
= 0;
89 for (i
= 0; i
< MAX_HIST
; i
++) {
90 info
->histogram
.fc_chunks
[i
] = 0;
91 info
->histogram
.fc_blocks
[i
] = 0;
95 static void update_chunk_stats(struct chunk_info
*info
,
96 unsigned long chunk_size
)
100 idx
= ul_log2(chunk_size
) + 1;
103 info
->histogram
.fc_chunks
[idx
]++;
104 info
->histogram
.fc_blocks
[idx
] += chunk_size
;
106 if (chunk_size
> info
->max
)
107 info
->max
= chunk_size
;
108 if (chunk_size
< info
->min
)
109 info
->min
= chunk_size
;
110 info
->avg
+= chunk_size
;
111 info
->real_free_chunks
++;
114 static void scan_block_bitmap(ext2_filsys fs
, struct chunk_info
*info
)
116 unsigned long long blocks_count
= ext2fs_blocks_count(fs
->super
);
117 unsigned long long chunks
= (blocks_count
+ info
->blks_in_chunk
) >>
118 (info
->chunkbits
- info
->blocksize_bits
);
119 unsigned long long chunk_num
;
120 unsigned long last_chunk_size
= 0;
121 unsigned long long chunk_start_blk
= 0;
124 for (chunk_num
= 0; chunk_num
< chunks
; chunk_num
++) {
125 unsigned long long blk
, num_blks
;
128 /* Last chunk may be smaller */
129 if (chunk_start_blk
+ info
->blks_in_chunk
> blocks_count
)
130 num_blks
= blocks_count
- chunk_start_blk
;
132 num_blks
= info
->blks_in_chunk
;
136 /* Initialize starting block for first chunk correctly else
137 * there is a segfault when blocksize = 1024 in which case
138 * block_map->start = 1 */
139 for (blk
= 0; blk
< num_blks
; blk
++, chunk_start_blk
++) {
140 if (chunk_num
== 0 && blk
== 0) {
141 blk
= fs
->super
->s_first_data_block
;
142 chunk_start_blk
= blk
;
144 used
= ext2fs_fast_test_block_bitmap2(fs
->block_map
,
145 chunk_start_blk
>> fs
->cluster_ratio_bits
);
151 if (used
&& last_chunk_size
!= 0) {
152 update_chunk_stats(info
, last_chunk_size
);
157 if (chunk_free
== info
->blks_in_chunk
)
160 if (last_chunk_size
!= 0)
161 update_chunk_stats(info
, last_chunk_size
);
164 #if defined(HAVE_EXT2_IOCTLS) && !defined(DEBUGFS)
165 # define FSMAP_EXTENTS 1024
166 static int scan_online(ext2_filsys fs
, struct chunk_info
*info
,
169 struct fsmap_head
*fsmap
;
170 struct fsmap
*extent
;
172 char mntpoint
[PATH_MAX
+ 1];
179 /* Try to open the mountpoint for a live query. */
180 retval
= ext2fs_check_mount_point(fs
->device_name
, &mount_flags
,
183 com_err(fs
->device_name
, retval
, "while checking mount status");
186 if (!(mount_flags
& EXT2_MF_MOUNTED
))
188 fd
= open(mntpoint
, O_RDONLY
);
190 com_err(mntpoint
, errno
, "while opening mount point");
194 fsmap
= malloc(fsmap_sizeof(FSMAP_EXTENTS
));
196 com_err(fs
->device_name
, errno
, "while allocating memory");
200 memset(fsmap
, 0, sizeof(*fsmap
));
201 fsmap
->fmh_count
= FSMAP_EXTENTS
;
202 fsmap
->fmh_keys
[1].fmr_device
= UINT_MAX
;
203 fsmap
->fmh_keys
[1].fmr_physical
= ULLONG_MAX
;
204 fsmap
->fmh_keys
[1].fmr_owner
= ULLONG_MAX
;
205 fsmap
->fmh_keys
[1].fmr_offset
= ULLONG_MAX
;
206 fsmap
->fmh_keys
[1].fmr_flags
= UINT_MAX
;
209 /* Fill the extent histogram with live data */
211 ret
= ioctl(fd
, FS_IOC_GETFSMAP
, fsmap
);
213 com_err(fs
->device_name
, errno
, "while calling fsmap");
218 /* No more extents to map, exit */
219 if (!fsmap
->fmh_entries
)
222 for (i
= 0, extent
= fsmap
->fmh_recs
;
223 i
< fsmap
->fmh_entries
;
225 if (!(extent
->fmr_flags
& FMR_OF_SPECIAL_OWNER
) ||
226 extent
->fmr_owner
!= FMR_OWN_FREE
)
228 update_chunk_stats(info
,
229 extent
->fmr_length
/ fs
->blocksize
);
230 *free_blks
+= (extent
->fmr_length
/ fs
->blocksize
);
233 p
= &fsmap
->fmh_recs
[fsmap
->fmh_entries
- 1];
234 if (p
->fmr_flags
& FMR_OF_LAST
)
236 fsmap_advance(fsmap
);
242 # define scan_online(fs, info, free_blks) (0)
243 #endif /* HAVE_EXT2_IOCTLS */
245 static errcode_t
scan_offline(ext2_filsys fs
, struct chunk_info
*info
,
250 *free_blks
= ext2fs_free_blocks_count(fs
->super
);
251 retval
= ext2fs_read_block_bitmap(fs
);
254 scan_block_bitmap(fs
, info
);
258 static errcode_t
dump_chunk_info(ext2_filsys fs
, struct chunk_info
*info
,
259 FILE *f
, blk64_t free_blks
)
261 unsigned long total_chunks
;
262 const char *unitp
= "KMGTPEZY";
264 unsigned long start
= 0, end
;
267 fprintf(f
, "Total blocks: %llu\nFree blocks: %llu (%0.1f%%)\n",
268 ext2fs_blocks_count(fs
->super
),
270 (double)free_blks
* 100 /
271 ext2fs_blocks_count(fs
->super
));
273 if (info
->chunkbytes
) {
274 fprintf(f
, "\nChunksize: %lu bytes (%u blocks)\n",
275 info
->chunkbytes
, info
->blks_in_chunk
);
276 total_chunks
= (ext2fs_blocks_count(fs
->super
) +
277 info
->blks_in_chunk
) >>
278 (info
->chunkbits
- info
->blocksize_bits
);
279 fprintf(f
, "Total chunks: %lu\nFree chunks: %lu (%0.1f%%)\n",
280 total_chunks
, info
->free_chunks
,
281 (double)info
->free_chunks
* 100 / total_chunks
);
284 /* Display chunk information in KB */
285 if (info
->real_free_chunks
) {
286 unsigned int scale
= fs
->blocksize
>> 10;
287 info
->min
= info
->min
* scale
;
288 info
->max
= info
->max
* scale
;
289 info
->avg
= info
->avg
/ info
->real_free_chunks
* scale
;
294 fprintf(f
, "\nMin. free extent: %lu KB \nMax. free extent: %lu KB\n"
295 "Avg. free extent: %lu KB\n", info
->min
, info
->max
, info
->avg
);
296 fprintf(f
, "Num. free extent: %lu\n", info
->real_free_chunks
);
298 fprintf(f
, "\nHISTOGRAM OF FREE EXTENT SIZES:\n");
299 fprintf(f
, "%s : %12s %12s %7s\n", "Extent Size Range",
300 "Free extents", "Free Blocks", "Percent");
301 for (i
= 0; i
< MAX_HIST
; i
++) {
302 end
= 1 << (i
+ info
->blocksize_bits
- units
);
303 if (info
->histogram
.fc_chunks
[i
] != 0) {
306 sprintf(end_str
, "%5lu%c-", end
, *unitp
);
308 strcpy(end_str
, "max ");
309 fprintf(f
, "%5lu%c...%7s : %12lu %12lu %6.2f%%\n",
310 start
, *unitp
, end_str
,
311 info
->histogram
.fc_chunks
[i
],
312 info
->histogram
.fc_blocks
[i
],
313 (double)info
->histogram
.fc_blocks
[i
] * 100 /
317 if (start
== 1<<10) {
327 static void close_device(char *device_name
, ext2_filsys fs
)
329 int retval
= ext2fs_close_free(&fs
);
332 com_err(device_name
, retval
, "while closing the filesystem.\n");
335 static void collect_info(ext2_filsys fs
, struct chunk_info
*chunk_info
, FILE *f
)
337 unsigned int retval
= 0;
338 blk64_t free_blks
= 0;
340 fprintf(f
, "Device: %s\n", fs
->device_name
);
341 fprintf(f
, "Blocksize: %u bytes\n", fs
->blocksize
);
343 init_chunk_info(fs
, chunk_info
);
344 if (!scan_online(fs
, chunk_info
, &free_blks
)) {
345 init_chunk_info(fs
, chunk_info
);
346 retval
= scan_offline(fs
, chunk_info
, &free_blks
);
349 com_err(fs
->device_name
, retval
, "while reading block bitmap");
350 close_device(fs
->device_name
, fs
);
354 retval
= dump_chunk_info(fs
, chunk_info
, f
, free_blks
);
356 com_err(fs
->device_name
, retval
, "while dumping chunk info");
357 close_device(fs
->device_name
, fs
);
363 static void open_device(char *device_name
, ext2_filsys
*fs
)
366 int flag
= EXT2_FLAG_FORCE
| EXT2_FLAG_64BITS
;
368 retval
= ext2fs_open(device_name
, flag
, 0, 0, unix_io_manager
, fs
);
370 com_err(device_name
, retval
, "while opening filesystem");
373 (*fs
)->default_bitmap_type
= EXT2FS_BMAP64_RBTREE
;
380 void do_freefrag(int argc
, char **argv
, int sci_idx
EXT2FS_ATTR((unused
)),
381 void *infop
EXT2FS_ATTR((unused
)))
383 int main(int argc
, char *argv
[])
386 struct chunk_info chunk_info
;
387 ext2_filsys fs
= NULL
;
393 if (check_fs_open(argv
[0]))
399 add_error_table(&et_ext2_error_table
);
402 memset(&chunk_info
, 0, sizeof(chunk_info
));
404 while ((c
= getopt(argc
, argv
, "c:h")) != EOF
) {
407 chunk_info
.chunkbytes
= strtoull(optarg
, &end
, 0);
409 fprintf(stderr
, "%s: bad chunk size '%s'\n",
413 if (chunk_info
.chunkbytes
&
414 (chunk_info
.chunkbytes
- 1)) {
415 fprintf(stderr
, "%s: chunk size must be a "
416 "power of 2.\n", argv
[0]);
419 chunk_info
.chunkbytes
*= 1024;
429 if (optind
== argc
) {
430 fprintf(stderr
, "%s: missing device name.\n", progname
);
434 device_name
= argv
[optind
];
436 open_device(device_name
, &fs
);
441 if (chunk_info
.chunkbytes
&& (chunk_info
.chunkbytes
< fs
->blocksize
)) {
442 fprintf(stderr
, "%s: chunksize must be greater than or equal "
443 "to filesystem blocksize.\n", progname
);
446 collect_info(fs
, &chunk_info
, stdout
);
448 close_device(device_name
, fs
);