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