]>
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
)
168 struct fsmap_head
*fsmap
;
169 struct fsmap
*extent
;
171 char mntpoint
[PATH_MAX
+ 1];
178 /* Try to open the mountpoint for a live query. */
179 retval
= ext2fs_check_mount_point(fs
->device_name
, &mount_flags
,
182 com_err(fs
->device_name
, retval
, "while checking mount status");
185 if (!mount_flags
& EXT2_MF_MOUNTED
)
187 fd
= open(mntpoint
, O_RDONLY
);
189 com_err(mntpoint
, errno
, "while opening mount point");
193 fsmap
= malloc(fsmap_sizeof(FSMAP_EXTENTS
));
195 com_err(fs
->device_name
, errno
, "while allocating memory");
199 memset(fsmap
, 0, sizeof(*fsmap
));
200 fsmap
->fmh_count
= FSMAP_EXTENTS
;
201 fsmap
->fmh_keys
[1].fmr_device
= UINT_MAX
;
202 fsmap
->fmh_keys
[1].fmr_physical
= ULLONG_MAX
;
203 fsmap
->fmh_keys
[1].fmr_owner
= ULLONG_MAX
;
204 fsmap
->fmh_keys
[1].fmr_offset
= ULLONG_MAX
;
205 fsmap
->fmh_keys
[1].fmr_flags
= UINT_MAX
;
207 /* Fill the extent histogram with live data */
209 ret
= ioctl(fd
, FS_IOC_GETFSMAP
, fsmap
);
211 com_err(fs
->device_name
, errno
, "while calling fsmap");
216 /* No more extents to map, exit */
217 if (!fsmap
->fmh_entries
)
220 for (i
= 0, extent
= fsmap
->fmh_recs
;
221 i
< fsmap
->fmh_entries
;
223 if (!(extent
->fmr_flags
& FMR_OF_SPECIAL_OWNER
) ||
224 extent
->fmr_owner
!= FMR_OWN_FREE
)
226 update_chunk_stats(info
,
227 extent
->fmr_length
/ fs
->blocksize
);
230 p
= &fsmap
->fmh_recs
[fsmap
->fmh_entries
- 1];
231 if (p
->fmr_flags
& FMR_OF_LAST
)
233 fsmap_advance(fsmap
);
239 # define scan_online(fs, info) (0)
240 #endif /* HAVE_EXT2_IOCTLS */
242 static errcode_t
scan_offline(ext2_filsys fs
, struct chunk_info
*info
)
246 retval
= ext2fs_read_block_bitmap(fs
);
249 scan_block_bitmap(fs
, info
);
253 static errcode_t
dump_chunk_info(ext2_filsys fs
, struct chunk_info
*info
,
256 unsigned long total_chunks
;
257 const char *unitp
= "KMGTPEZY";
259 unsigned long start
= 0, end
;
262 fprintf(f
, "Total blocks: %llu\nFree blocks: %llu (%0.1f%%)\n",
263 ext2fs_blocks_count(fs
->super
),
264 ext2fs_free_blocks_count(fs
->super
),
265 (double)ext2fs_free_blocks_count(fs
->super
) * 100 /
266 ext2fs_blocks_count(fs
->super
));
268 if (info
->chunkbytes
) {
269 fprintf(f
, "\nChunksize: %lu bytes (%u blocks)\n",
270 info
->chunkbytes
, info
->blks_in_chunk
);
271 total_chunks
= (ext2fs_blocks_count(fs
->super
) +
272 info
->blks_in_chunk
) >>
273 (info
->chunkbits
- info
->blocksize_bits
);
274 fprintf(f
, "Total chunks: %lu\nFree chunks: %lu (%0.1f%%)\n",
275 total_chunks
, info
->free_chunks
,
276 (double)info
->free_chunks
* 100 / total_chunks
);
279 /* Display chunk information in KB */
280 if (info
->real_free_chunks
) {
281 unsigned int scale
= fs
->blocksize
>> 10;
282 info
->min
= info
->min
* scale
;
283 info
->max
= info
->max
* scale
;
284 info
->avg
= info
->avg
/ info
->real_free_chunks
* scale
;
289 fprintf(f
, "\nMin. free extent: %lu KB \nMax. free extent: %lu KB\n"
290 "Avg. free extent: %lu KB\n", info
->min
, info
->max
, info
->avg
);
291 fprintf(f
, "Num. free extent: %lu\n", info
->real_free_chunks
);
293 fprintf(f
, "\nHISTOGRAM OF FREE EXTENT SIZES:\n");
294 fprintf(f
, "%s : %12s %12s %7s\n", "Extent Size Range",
295 "Free extents", "Free Blocks", "Percent");
296 for (i
= 0; i
< MAX_HIST
; i
++) {
297 end
= 1 << (i
+ info
->blocksize_bits
- units
);
298 if (info
->histogram
.fc_chunks
[i
] != 0) {
301 sprintf(end_str
, "%5lu%c-", end
, *unitp
);
303 strcpy(end_str
, "max ");
304 fprintf(f
, "%5lu%c...%7s : %12lu %12lu %6.2f%%\n",
305 start
, *unitp
, end_str
,
306 info
->histogram
.fc_chunks
[i
],
307 info
->histogram
.fc_blocks
[i
],
308 (double)info
->histogram
.fc_blocks
[i
] * 100 /
309 ext2fs_free_blocks_count(fs
->super
));
312 if (start
== 1<<10) {
322 static void close_device(char *device_name
, ext2_filsys fs
)
324 int retval
= ext2fs_close_free(&fs
);
327 com_err(device_name
, retval
, "while closing the filesystem.\n");
330 static void collect_info(ext2_filsys fs
, struct chunk_info
*chunk_info
, FILE *f
)
332 unsigned int retval
= 0;
334 fprintf(f
, "Device: %s\n", fs
->device_name
);
335 fprintf(f
, "Blocksize: %u bytes\n", fs
->blocksize
);
337 init_chunk_info(fs
, chunk_info
);
338 if (!scan_online(fs
, chunk_info
)) {
339 init_chunk_info(fs
, chunk_info
);
340 retval
= scan_offline(fs
, chunk_info
);
343 com_err(fs
->device_name
, retval
, "while reading block bitmap");
344 close_device(fs
->device_name
, fs
);
348 retval
= dump_chunk_info(fs
, chunk_info
, f
);
350 com_err(fs
->device_name
, retval
, "while dumping chunk info");
351 close_device(fs
->device_name
, fs
);
357 static void open_device(char *device_name
, ext2_filsys
*fs
)
360 int flag
= EXT2_FLAG_FORCE
| EXT2_FLAG_64BITS
;
362 retval
= ext2fs_open(device_name
, flag
, 0, 0, unix_io_manager
, fs
);
364 com_err(device_name
, retval
, "while opening filesystem");
367 (*fs
)->default_bitmap_type
= EXT2FS_BMAP64_RBTREE
;
374 void do_freefrag(int argc
, char **argv
)
376 int main(int argc
, char *argv
[])
379 struct chunk_info chunk_info
;
380 ext2_filsys fs
= NULL
;
386 if (check_fs_open(argv
[0]))
392 add_error_table(&et_ext2_error_table
);
395 memset(&chunk_info
, 0, sizeof(chunk_info
));
397 while ((c
= getopt(argc
, argv
, "c:h")) != EOF
) {
400 chunk_info
.chunkbytes
= strtoull(optarg
, &end
, 0);
402 fprintf(stderr
, "%s: bad chunk size '%s'\n",
406 if (chunk_info
.chunkbytes
&
407 (chunk_info
.chunkbytes
- 1)) {
408 fprintf(stderr
, "%s: chunk size must be a "
409 "power of 2.\n", argv
[0]);
412 chunk_info
.chunkbytes
*= 1024;
422 if (optind
== argc
) {
423 fprintf(stderr
, "%s: missing device name.\n", progname
);
427 device_name
= argv
[optind
];
429 open_device(device_name
, &fs
);
434 if (chunk_info
.chunkbytes
&& (chunk_info
.chunkbytes
< fs
->blocksize
)) {
435 fprintf(stderr
, "%s: chunksize must be greater than or equal "
436 "to filesystem blocksize.\n", progname
);
439 collect_info(fs
, &chunk_info
, stdout
);
441 close_device(device_name
, fs
);