]>
Commit | Line | Data |
---|---|---|
0b2681f4 TT |
1 | /* |
2 | * e2freefrag - report filesystem free-space fragmentation | |
3 | * | |
4 | * Copyright (C) 2009 Sun Microsystems, Inc. | |
5 | * | |
6 | * Author: Rupesh Thakare <rupesh@sun.com> | |
7 | * Andreas Dilger <adilger@sun.com> | |
8 | * | |
9 | * %Begin-Header% | |
10 | * This file may be redistributed under the terms of the GNU Public | |
11 | * License version 2. | |
12 | * %End-Header% | |
13 | */ | |
d1154eb4 | 14 | #include "config.h" |
0b2681f4 TT |
15 | #include <stdio.h> |
16 | #ifdef HAVE_UNISTD_H | |
17 | #include <unistd.h> | |
18 | #endif | |
19 | #ifdef HAVE_STDLIB_H | |
20 | #include <stdlib.h> | |
21 | #endif | |
22 | #ifdef HAVE_GETOPT_H | |
23 | #include <getopt.h> | |
24 | #else | |
25 | extern char *optarg; | |
26 | extern int optind; | |
27 | #endif | |
28 | ||
29 | #include "ext2fs/ext2_fs.h" | |
30 | #include "ext2fs/ext2fs.h" | |
31 | #include "e2freefrag.h" | |
32 | ||
3cebf9c1 | 33 | static void usage(const char *prog) |
0b2681f4 TT |
34 | { |
35 | fprintf(stderr, "usage: %s [-c chunksize in kb] [-h] " | |
36 | "device_name\n", prog); | |
5e96c572 | 37 | #ifndef DEBUGFS |
0b2681f4 | 38 | exit(1); |
5e96c572 | 39 | #endif |
0b2681f4 TT |
40 | } |
41 | ||
42 | static int ul_log2(unsigned long arg) | |
43 | { | |
44 | int l = 0; | |
45 | ||
46 | arg >>= 1; | |
47 | while (arg) { | |
48 | l++; | |
49 | arg >>= 1; | |
50 | } | |
51 | return l; | |
52 | } | |
53 | ||
3cebf9c1 | 54 | static void init_chunk_info(ext2_filsys fs, struct chunk_info *info) |
0b2681f4 TT |
55 | { |
56 | int i; | |
57 | ||
0b2681f4 | 58 | info->blocksize_bits = ul_log2((unsigned long)fs->blocksize); |
cba91c4b TT |
59 | if (info->chunkbytes) { |
60 | info->chunkbits = ul_log2(info->chunkbytes); | |
61 | info->blks_in_chunk = info->chunkbytes >> info->blocksize_bits; | |
62 | } else { | |
63 | info->chunkbits = ul_log2(DEFAULT_CHUNKSIZE); | |
64 | info->blks_in_chunk = DEFAULT_CHUNKSIZE >> info->blocksize_bits; | |
65 | } | |
0b2681f4 TT |
66 | |
67 | info->min = ~0UL; | |
68 | info->max = info->avg = 0; | |
69 | info->real_free_chunks = 0; | |
70 | ||
ad751f11 AD |
71 | for (i = 0; i < MAX_HIST; i++) { |
72 | info->histogram.fc_chunks[i] = 0; | |
73 | info->histogram.fc_blocks[i] = 0; | |
74 | } | |
0b2681f4 TT |
75 | } |
76 | ||
3cebf9c1 TT |
77 | static void update_chunk_stats(struct chunk_info *info, |
78 | unsigned long chunk_size) | |
1e48a456 | 79 | { |
3cebf9c1 | 80 | unsigned long idx; |
1e48a456 | 81 | |
3cebf9c1 TT |
82 | idx = ul_log2(chunk_size) + 1; |
83 | if (idx >= MAX_HIST) | |
84 | idx = MAX_HIST-1; | |
85 | info->histogram.fc_chunks[idx]++; | |
86 | info->histogram.fc_blocks[idx] += chunk_size; | |
1e48a456 TT |
87 | |
88 | if (chunk_size > info->max) | |
89 | info->max = chunk_size; | |
90 | if (chunk_size < info->min) | |
91 | info->min = chunk_size; | |
92 | info->avg += chunk_size; | |
93 | info->real_free_chunks++; | |
94 | } | |
95 | ||
3cebf9c1 | 96 | static void scan_block_bitmap(ext2_filsys fs, struct chunk_info *info) |
0b2681f4 | 97 | { |
4efbac6f | 98 | unsigned long long blocks_count = ext2fs_blocks_count(fs->super); |
0b2681f4 TT |
99 | unsigned long long chunks = (blocks_count + info->blks_in_chunk) >> |
100 | (info->chunkbits - info->blocksize_bits); | |
101 | unsigned long long chunk_num; | |
102 | unsigned long last_chunk_size = 0; | |
103 | unsigned long long chunk_start_blk = 0; | |
3e343b8d | 104 | int used; |
0b2681f4 TT |
105 | |
106 | for (chunk_num = 0; chunk_num < chunks; chunk_num++) { | |
107 | unsigned long long blk, num_blks; | |
108 | int chunk_free; | |
109 | ||
110 | /* Last chunk may be smaller */ | |
111 | if (chunk_start_blk + info->blks_in_chunk > blocks_count) | |
112 | num_blks = blocks_count - chunk_start_blk; | |
113 | else | |
114 | num_blks = info->blks_in_chunk; | |
115 | ||
116 | chunk_free = 0; | |
117 | ||
118 | /* Initialize starting block for first chunk correctly else | |
119 | * there is a segfault when blocksize = 1024 in which case | |
120 | * block_map->start = 1 */ | |
3e343b8d TT |
121 | for (blk = 0; blk < num_blks; blk++, chunk_start_blk++) { |
122 | if (chunk_num == 0 && blk == 0) { | |
123 | blk = fs->super->s_first_data_block; | |
124 | chunk_start_blk = blk; | |
125 | } | |
3c041a51 | 126 | used = ext2fs_fast_test_block_bitmap2(fs->block_map, |
554bc091 | 127 | chunk_start_blk >> fs->cluster_ratio_bits); |
0b2681f4 TT |
128 | if (!used) { |
129 | last_chunk_size++; | |
130 | chunk_free++; | |
131 | } | |
132 | ||
133 | if (used && last_chunk_size != 0) { | |
1e48a456 | 134 | update_chunk_stats(info, last_chunk_size); |
0b2681f4 TT |
135 | last_chunk_size = 0; |
136 | } | |
137 | } | |
138 | ||
139 | if (chunk_free == info->blks_in_chunk) | |
140 | info->free_chunks++; | |
141 | } | |
1e48a456 TT |
142 | if (last_chunk_size != 0) |
143 | update_chunk_stats(info, last_chunk_size); | |
0b2681f4 TT |
144 | } |
145 | ||
3cebf9c1 TT |
146 | static errcode_t get_chunk_info(ext2_filsys fs, struct chunk_info *info, |
147 | FILE *f) | |
0b2681f4 TT |
148 | { |
149 | unsigned long total_chunks; | |
3cebf9c1 | 150 | const char *unitp = "KMGTPEZY"; |
0b2681f4 | 151 | int units = 10; |
cf5301d7 | 152 | unsigned long start = 0, end; |
0b2681f4 TT |
153 | int i, retval = 0; |
154 | ||
155 | scan_block_bitmap(fs, info); | |
156 | ||
5e96c572 TT |
157 | fprintf(f, "Total blocks: %llu\nFree blocks: %u (%0.1f%%)\n", |
158 | ext2fs_blocks_count(fs->super), fs->super->s_free_blocks_count, | |
159 | (double)fs->super->s_free_blocks_count * 100 / | |
160 | ext2fs_blocks_count(fs->super)); | |
0b2681f4 | 161 | |
cba91c4b | 162 | if (info->chunkbytes) { |
5e96c572 TT |
163 | fprintf(f, "\nChunksize: %lu bytes (%u blocks)\n", |
164 | info->chunkbytes, info->blks_in_chunk); | |
4efbac6f | 165 | total_chunks = (ext2fs_blocks_count(fs->super) + |
cba91c4b TT |
166 | info->blks_in_chunk) >> |
167 | (info->chunkbits - info->blocksize_bits); | |
5e96c572 TT |
168 | fprintf(f, "Total chunks: %lu\nFree chunks: %lu (%0.1f%%)\n", |
169 | total_chunks, info->free_chunks, | |
170 | (double)info->free_chunks * 100 / total_chunks); | |
cba91c4b | 171 | } |
0b2681f4 TT |
172 | |
173 | /* Display chunk information in KB */ | |
174 | if (info->real_free_chunks) { | |
175 | info->min = (info->min * fs->blocksize) >> 10; | |
176 | info->max = (info->max * fs->blocksize) >> 10; | |
177 | info->avg = (info->avg / info->real_free_chunks * | |
178 | fs->blocksize) >> 10; | |
179 | } else { | |
180 | info->min = 0; | |
181 | } | |
182 | ||
5e96c572 TT |
183 | fprintf(f, "\nMin. free extent: %lu KB \nMax. free extent: %lu KB\n" |
184 | "Avg. free extent: %lu KB\n", info->min, info->max, info->avg); | |
185 | fprintf(f, "Num. free extent: %lu\n", info->real_free_chunks); | |
0b2681f4 | 186 | |
5e96c572 TT |
187 | fprintf(f, "\nHISTOGRAM OF FREE EXTENT SIZES:\n"); |
188 | fprintf(f, "%s : %12s %12s %7s\n", "Extent Size Range", | |
189 | "Free extents", "Free Blocks", "Percent"); | |
0b2681f4 TT |
190 | for (i = 0; i < MAX_HIST; i++) { |
191 | end = 1 << (i + info->blocksize_bits - units); | |
1e48a456 TT |
192 | if (info->histogram.fc_chunks[i] != 0) { |
193 | char end_str[32]; | |
194 | ||
195 | sprintf(end_str, "%5lu%c-", end, *unitp); | |
196 | if (i == MAX_HIST-1) | |
197 | strcpy(end_str, "max "); | |
5e96c572 TT |
198 | fprintf(f, "%5lu%c...%7s : %12lu %12lu %6.2f%%\n", |
199 | start, *unitp, end_str, | |
200 | info->histogram.fc_chunks[i], | |
201 | info->histogram.fc_blocks[i], | |
202 | (double)info->histogram.fc_blocks[i] * 100 / | |
203 | fs->super->s_free_blocks_count); | |
1e48a456 | 204 | } |
0b2681f4 TT |
205 | start = end; |
206 | if (start == 1<<10) { | |
207 | start = 1; | |
208 | units += 10; | |
209 | unitp++; | |
210 | } | |
211 | } | |
212 | ||
213 | return retval; | |
214 | } | |
215 | ||
3cebf9c1 | 216 | static void close_device(char *device_name, ext2_filsys fs) |
0b2681f4 TT |
217 | { |
218 | int retval = ext2fs_close(fs); | |
219 | ||
220 | if (retval) | |
221 | com_err(device_name, retval, "while closing the filesystem.\n"); | |
222 | } | |
223 | ||
3cebf9c1 | 224 | static void collect_info(ext2_filsys fs, struct chunk_info *chunk_info, FILE *f) |
0b2681f4 | 225 | { |
cf5301d7 | 226 | unsigned int retval = 0; |
0b2681f4 | 227 | |
5e96c572 TT |
228 | fprintf(f, "Device: %s\n", fs->device_name); |
229 | fprintf(f, "Blocksize: %u bytes\n", fs->blocksize); | |
0b2681f4 TT |
230 | |
231 | retval = ext2fs_read_block_bitmap(fs); | |
232 | if (retval) { | |
233 | com_err(fs->device_name, retval, "while reading block bitmap"); | |
234 | close_device(fs->device_name, fs); | |
235 | exit(1); | |
236 | } | |
237 | ||
238 | init_chunk_info(fs, chunk_info); | |
239 | ||
5e96c572 | 240 | retval = get_chunk_info(fs, chunk_info, f); |
0b2681f4 TT |
241 | if (retval) { |
242 | com_err(fs->device_name, retval, "while collecting chunk info"); | |
243 | close_device(fs->device_name, fs); | |
244 | exit(1); | |
245 | } | |
246 | } | |
247 | ||
3cebf9c1 TT |
248 | #ifndef DEBUGFS |
249 | static void open_device(char *device_name, ext2_filsys *fs) | |
0b2681f4 TT |
250 | { |
251 | int retval; | |
ea2d3788 | 252 | int flag = EXT2_FLAG_FORCE | EXT2_FLAG_64BITS; |
0b2681f4 TT |
253 | |
254 | retval = ext2fs_open(device_name, flag, 0, 0, unix_io_manager, fs); | |
255 | if (retval) { | |
256 | com_err(device_name, retval, "while opening filesystem"); | |
257 | exit(1); | |
258 | } | |
ea2d3788 | 259 | (*fs)->default_bitmap_type = EXT2FS_BMAP64_RBTREE; |
0b2681f4 | 260 | } |
3cebf9c1 | 261 | #endif |
0b2681f4 | 262 | |
5e96c572 | 263 | #ifdef DEBUGFS |
5e96c572 TT |
264 | #include "debugfs.h" |
265 | ||
266 | void do_freefrag(int argc, char **argv) | |
267 | #else | |
0b2681f4 | 268 | int main(int argc, char *argv[]) |
5e96c572 | 269 | #endif |
0b2681f4 | 270 | { |
3cebf9c1 | 271 | struct chunk_info chunk_info; |
0b2681f4 | 272 | ext2_filsys fs = NULL; |
0b2681f4 | 273 | char *progname; |
b887f08f MF |
274 | char *end; |
275 | int c; | |
0b2681f4 | 276 | |
5e96c572 TT |
277 | #ifdef DEBUGFS |
278 | if (check_fs_open(argv[0])) | |
279 | return; | |
280 | #else | |
3cebf9c1 TT |
281 | char *device_name; |
282 | ||
137a7dc0 | 283 | add_error_table(&et_ext2_error_table); |
5e96c572 | 284 | #endif |
0b2681f4 | 285 | progname = argv[0]; |
3cebf9c1 | 286 | memset(&chunk_info, 0, sizeof(chunk_info)); |
0b2681f4 TT |
287 | |
288 | while ((c = getopt(argc, argv, "c:h")) != EOF) { | |
289 | switch (c) { | |
290 | case 'c': | |
291 | chunk_info.chunkbytes = strtoull(optarg, &end, 0); | |
292 | if (*end != '\0') { | |
293 | fprintf(stderr, "%s: bad chunk size '%s'\n", | |
294 | progname, optarg); | |
295 | usage(progname); | |
296 | } | |
297 | if (chunk_info.chunkbytes & | |
298 | (chunk_info.chunkbytes - 1)) { | |
299 | fprintf(stderr, "%s: chunk size must be a " | |
aff2cf8e | 300 | "power of 2.\n", argv[0]); |
0b2681f4 TT |
301 | usage(progname); |
302 | } | |
303 | chunk_info.chunkbytes *= 1024; | |
304 | break; | |
0b2681f4 | 305 | case 'h': |
4ebbc0a3 | 306 | default: |
0b2681f4 TT |
307 | usage(progname); |
308 | break; | |
309 | } | |
310 | } | |
311 | ||
5e96c572 | 312 | #ifndef DEBUGFS |
0b2681f4 TT |
313 | if (optind == argc) { |
314 | fprintf(stderr, "%s: missing device name.\n", progname); | |
315 | usage(progname); | |
316 | } | |
317 | ||
318 | device_name = argv[optind]; | |
319 | ||
320 | open_device(device_name, &fs); | |
5e96c572 TT |
321 | #else |
322 | fs = current_fs; | |
323 | #endif | |
0b2681f4 | 324 | |
cba91c4b | 325 | if (chunk_info.chunkbytes && (chunk_info.chunkbytes < fs->blocksize)) { |
0b2681f4 TT |
326 | fprintf(stderr, "%s: chunksize must be greater than or equal " |
327 | "to filesystem blocksize.\n", progname); | |
328 | exit(1); | |
329 | } | |
5e96c572 TT |
330 | collect_info(fs, &chunk_info, stdout); |
331 | #ifndef DEBUGFS | |
0b2681f4 TT |
332 | close_device(device_name, fs); |
333 | ||
3cebf9c1 | 334 | return 0; |
5e96c572 | 335 | #endif |
0b2681f4 | 336 | } |