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