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