]>
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>
47 static void usage(const char *prog
)
49 fprintf(stderr
, "usage: %s [-c chunksize in kb] [-h] "
50 "device_name\n", prog
);
56 static int ul_log2(unsigned long arg
)
68 static void init_chunk_info(ext2_filsys fs
, struct chunk_info
*info
)
72 info
->blocksize_bits
= ul_log2((unsigned long)fs
->blocksize
);
73 if (info
->chunkbytes
) {
74 info
->chunkbits
= ul_log2(info
->chunkbytes
);
75 info
->blks_in_chunk
= info
->chunkbytes
>> info
->blocksize_bits
;
77 info
->chunkbits
= ul_log2(DEFAULT_CHUNKSIZE
);
78 info
->blks_in_chunk
= DEFAULT_CHUNKSIZE
>> info
->blocksize_bits
;
82 info
->max
= info
->avg
= 0;
83 info
->real_free_chunks
= 0;
85 for (i
= 0; i
< MAX_HIST
; i
++) {
86 info
->histogram
.fc_chunks
[i
] = 0;
87 info
->histogram
.fc_blocks
[i
] = 0;
91 static void update_chunk_stats(struct chunk_info
*info
,
92 unsigned long chunk_size
)
96 idx
= ul_log2(chunk_size
) + 1;
99 info
->histogram
.fc_chunks
[idx
]++;
100 info
->histogram
.fc_blocks
[idx
] += chunk_size
;
102 if (chunk_size
> info
->max
)
103 info
->max
= chunk_size
;
104 if (chunk_size
< info
->min
)
105 info
->min
= chunk_size
;
106 info
->avg
+= chunk_size
;
107 info
->real_free_chunks
++;
110 static void scan_block_bitmap(ext2_filsys fs
, struct chunk_info
*info
)
112 unsigned long long blocks_count
= ext2fs_blocks_count(fs
->super
);
113 unsigned long long chunks
= (blocks_count
+ info
->blks_in_chunk
) >>
114 (info
->chunkbits
- info
->blocksize_bits
);
115 unsigned long long chunk_num
;
116 unsigned long last_chunk_size
= 0;
117 unsigned long long chunk_start_blk
= 0;
120 for (chunk_num
= 0; chunk_num
< chunks
; chunk_num
++) {
121 unsigned long long blk
, num_blks
;
124 /* Last chunk may be smaller */
125 if (chunk_start_blk
+ info
->blks_in_chunk
> blocks_count
)
126 num_blks
= blocks_count
- chunk_start_blk
;
128 num_blks
= info
->blks_in_chunk
;
132 /* Initialize starting block for first chunk correctly else
133 * there is a segfault when blocksize = 1024 in which case
134 * block_map->start = 1 */
135 for (blk
= 0; blk
< num_blks
; blk
++, chunk_start_blk
++) {
136 if (chunk_num
== 0 && blk
== 0) {
137 blk
= fs
->super
->s_first_data_block
;
138 chunk_start_blk
= blk
;
140 used
= ext2fs_fast_test_block_bitmap2(fs
->block_map
,
141 chunk_start_blk
>> fs
->cluster_ratio_bits
);
147 if (used
&& last_chunk_size
!= 0) {
148 update_chunk_stats(info
, last_chunk_size
);
153 if (chunk_free
== info
->blks_in_chunk
)
156 if (last_chunk_size
!= 0)
157 update_chunk_stats(info
, last_chunk_size
);
160 #if defined(HAVE_EXT2_IOCTLS) && !defined(DEBUGFS)
161 # define FSMAP_EXTENTS 1024
162 static int scan_online(ext2_filsys fs
, struct chunk_info
*info
)
164 struct fsmap_head
*fsmap
;
165 struct fsmap
*extent
;
167 char mntpoint
[PATH_MAX
+ 1];
174 /* Try to open the mountpoint for a live query. */
175 retval
= ext2fs_check_mount_point(fs
->device_name
, &mount_flags
,
178 com_err(fs
->device_name
, retval
, "while checking mount status");
181 if (!mount_flags
& EXT2_MF_MOUNTED
)
183 fd
= open(mntpoint
, O_RDONLY
);
185 com_err(mntpoint
, errno
, "while opening mount point");
189 fsmap
= malloc(fsmap_sizeof(FSMAP_EXTENTS
));
191 com_err(fs
->device_name
, errno
, "while allocating memory");
195 memset(fsmap
, 0, sizeof(*fsmap
));
196 fsmap
->fmh_count
= FSMAP_EXTENTS
;
197 fsmap
->fmh_keys
[1].fmr_device
= UINT_MAX
;
198 fsmap
->fmh_keys
[1].fmr_physical
= ULLONG_MAX
;
199 fsmap
->fmh_keys
[1].fmr_owner
= ULLONG_MAX
;
200 fsmap
->fmh_keys
[1].fmr_offset
= ULLONG_MAX
;
201 fsmap
->fmh_keys
[1].fmr_flags
= UINT_MAX
;
203 /* Fill the extent histogram with live data */
205 ret
= ioctl(fd
, FS_IOC_GETFSMAP
, fsmap
);
207 com_err(fs
->device_name
, errno
, "while calling fsmap");
212 /* No more extents to map, exit */
213 if (!fsmap
->fmh_entries
)
216 for (i
= 0, extent
= fsmap
->fmh_recs
;
217 i
< fsmap
->fmh_entries
;
219 if (!(extent
->fmr_flags
& FMR_OF_SPECIAL_OWNER
) ||
220 extent
->fmr_owner
!= FMR_OWN_FREE
)
222 update_chunk_stats(info
,
223 extent
->fmr_length
/ fs
->blocksize
);
226 p
= &fsmap
->fmh_recs
[fsmap
->fmh_entries
- 1];
227 if (p
->fmr_flags
& FMR_OF_LAST
)
229 fsmap_advance(fsmap
);
235 # define scan_online(fs, info) (0)
236 #endif /* HAVE_EXT2_IOCTLS */
238 static errcode_t
scan_offline(ext2_filsys fs
, struct chunk_info
*info
)
242 retval
= ext2fs_read_block_bitmap(fs
);
245 scan_block_bitmap(fs
, info
);
249 static errcode_t
dump_chunk_info(ext2_filsys fs
, struct chunk_info
*info
,
252 unsigned long total_chunks
;
253 const char *unitp
= "KMGTPEZY";
255 unsigned long start
= 0, end
;
258 fprintf(f
, "Total blocks: %llu\nFree blocks: %u (%0.1f%%)\n",
259 ext2fs_blocks_count(fs
->super
), fs
->super
->s_free_blocks_count
,
260 (double)fs
->super
->s_free_blocks_count
* 100 /
261 ext2fs_blocks_count(fs
->super
));
263 if (info
->chunkbytes
) {
264 fprintf(f
, "\nChunksize: %lu bytes (%u blocks)\n",
265 info
->chunkbytes
, info
->blks_in_chunk
);
266 total_chunks
= (ext2fs_blocks_count(fs
->super
) +
267 info
->blks_in_chunk
) >>
268 (info
->chunkbits
- info
->blocksize_bits
);
269 fprintf(f
, "Total chunks: %lu\nFree chunks: %lu (%0.1f%%)\n",
270 total_chunks
, info
->free_chunks
,
271 (double)info
->free_chunks
* 100 / total_chunks
);
274 /* Display chunk information in KB */
275 if (info
->real_free_chunks
) {
276 unsigned int scale
= fs
->blocksize
>> 10;
277 info
->min
= info
->min
* scale
;
278 info
->max
= info
->max
* scale
;
279 info
->avg
= info
->avg
/ info
->real_free_chunks
* scale
;
284 fprintf(f
, "\nMin. free extent: %lu KB \nMax. free extent: %lu KB\n"
285 "Avg. free extent: %lu KB\n", info
->min
, info
->max
, info
->avg
);
286 fprintf(f
, "Num. free extent: %lu\n", info
->real_free_chunks
);
288 fprintf(f
, "\nHISTOGRAM OF FREE EXTENT SIZES:\n");
289 fprintf(f
, "%s : %12s %12s %7s\n", "Extent Size Range",
290 "Free extents", "Free Blocks", "Percent");
291 for (i
= 0; i
< MAX_HIST
; i
++) {
292 end
= 1 << (i
+ info
->blocksize_bits
- units
);
293 if (info
->histogram
.fc_chunks
[i
] != 0) {
296 sprintf(end_str
, "%5lu%c-", end
, *unitp
);
298 strcpy(end_str
, "max ");
299 fprintf(f
, "%5lu%c...%7s : %12lu %12lu %6.2f%%\n",
300 start
, *unitp
, end_str
,
301 info
->histogram
.fc_chunks
[i
],
302 info
->histogram
.fc_blocks
[i
],
303 (double)info
->histogram
.fc_blocks
[i
] * 100 /
304 fs
->super
->s_free_blocks_count
);
307 if (start
== 1<<10) {
317 static void close_device(char *device_name
, ext2_filsys fs
)
319 int retval
= ext2fs_close_free(&fs
);
322 com_err(device_name
, retval
, "while closing the filesystem.\n");
325 static void collect_info(ext2_filsys fs
, struct chunk_info
*chunk_info
, FILE *f
)
327 unsigned int retval
= 0;
329 fprintf(f
, "Device: %s\n", fs
->device_name
);
330 fprintf(f
, "Blocksize: %u bytes\n", fs
->blocksize
);
332 init_chunk_info(fs
, chunk_info
);
333 if (!scan_online(fs
, chunk_info
)) {
334 init_chunk_info(fs
, chunk_info
);
335 retval
= scan_offline(fs
, chunk_info
);
338 com_err(fs
->device_name
, retval
, "while reading block bitmap");
339 close_device(fs
->device_name
, fs
);
343 retval
= dump_chunk_info(fs
, chunk_info
, f
);
345 com_err(fs
->device_name
, retval
, "while dumping chunk info");
346 close_device(fs
->device_name
, fs
);
352 static void open_device(char *device_name
, ext2_filsys
*fs
)
355 int flag
= EXT2_FLAG_FORCE
| EXT2_FLAG_64BITS
;
357 retval
= ext2fs_open(device_name
, flag
, 0, 0, unix_io_manager
, fs
);
359 com_err(device_name
, retval
, "while opening filesystem");
362 (*fs
)->default_bitmap_type
= EXT2FS_BMAP64_RBTREE
;
369 void do_freefrag(int argc
, char **argv
)
371 int main(int argc
, char *argv
[])
374 struct chunk_info chunk_info
;
375 ext2_filsys fs
= NULL
;
381 if (check_fs_open(argv
[0]))
387 add_error_table(&et_ext2_error_table
);
390 memset(&chunk_info
, 0, sizeof(chunk_info
));
392 while ((c
= getopt(argc
, argv
, "c:h")) != EOF
) {
395 chunk_info
.chunkbytes
= strtoull(optarg
, &end
, 0);
397 fprintf(stderr
, "%s: bad chunk size '%s'\n",
401 if (chunk_info
.chunkbytes
&
402 (chunk_info
.chunkbytes
- 1)) {
403 fprintf(stderr
, "%s: chunk size must be a "
404 "power of 2.\n", argv
[0]);
407 chunk_info
.chunkbytes
*= 1024;
417 if (optind
== argc
) {
418 fprintf(stderr
, "%s: missing device name.\n", progname
);
422 device_name
= argv
[optind
];
424 open_device(device_name
, &fs
);
429 if (chunk_info
.chunkbytes
&& (chunk_info
.chunkbytes
< fs
->blocksize
)) {
430 fprintf(stderr
, "%s: chunksize must be greater than or equal "
431 "to filesystem blocksize.\n", progname
);
434 collect_info(fs
, &chunk_info
, stdout
);
436 close_device(device_name
, fs
);