1 /* 2 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sbin/hammer/misc.c,v 1.5 2008/06/26 04:07:57 dillon Exp $ 35 */ 36 37 #include "hammer_util.h" 38 39 /* 40 * (taken from /usr/src/sys/vfs/hammer/hammer_btree.c) 41 * 42 * Compare two B-Tree elements, return -N, 0, or +N (e.g. similar to strcmp). 43 * 44 * Note that for this particular function a return value of -1, 0, or +1 45 * can denote a match if delete_tid is otherwise discounted. A delete_tid 46 * of zero is considered to be 'infinity' in comparisons. 47 * 48 * See also hammer_rec_rb_compare() and hammer_rec_cmp() in hammer_object.c. 49 */ 50 int 51 hammer_btree_cmp(hammer_base_elm_t key1, hammer_base_elm_t key2) 52 { 53 if (key1->localization < key2->localization) 54 return(-5); 55 if (key1->localization > key2->localization) 56 return(5); 57 58 if (key1->obj_id < key2->obj_id) 59 return(-4); 60 if (key1->obj_id > key2->obj_id) 61 return(4); 62 63 if (key1->rec_type < key2->rec_type) 64 return(-3); 65 if (key1->rec_type > key2->rec_type) 66 return(3); 67 68 if (key1->key < key2->key) 69 return(-2); 70 if (key1->key > key2->key) 71 return(2); 72 73 if (key1->create_tid == 0) { 74 if (key2->create_tid == 0) 75 return(0); 76 return(1); 77 } 78 if (key2->create_tid == 0) 79 return(-1); 80 if (key1->create_tid < key2->create_tid) 81 return(-1); 82 if (key1->create_tid > key2->create_tid) 83 return(1); 84 return(0); 85 } 86 87 void 88 hammer_key_beg_init(hammer_base_elm_t base) 89 { 90 bzero(base, sizeof(*base)); 91 92 base->localization = HAMMER_MIN_LOCALIZATION; 93 base->obj_id = HAMMER_MIN_OBJID; 94 base->key = HAMMER_MIN_KEY; 95 base->create_tid = 1; 96 base->rec_type = HAMMER_MIN_RECTYPE; 97 } 98 99 void 100 hammer_key_end_init(hammer_base_elm_t base) 101 { 102 bzero(base, sizeof(*base)); 103 104 base->localization = HAMMER_MAX_LOCALIZATION; 105 base->obj_id = HAMMER_MAX_OBJID; 106 base->key = HAMMER_MAX_KEY; 107 base->create_tid = HAMMER_MAX_TID; 108 base->rec_type = HAMMER_MAX_RECTYPE; 109 } 110 111 int 112 getyn(void) 113 { 114 char buf[256]; 115 int len; 116 117 if (fgets(buf, sizeof(buf), stdin) == NULL) 118 return(0); 119 len = strlen(buf); 120 while (len && (buf[len-1] == '\n' || buf[len-1] == '\r')) 121 --len; 122 buf[len] = 0; 123 if (strcmp(buf, "y") == 0 || 124 strcmp(buf, "yes") == 0 || 125 strcmp(buf, "Y") == 0 || 126 strcmp(buf, "YES") == 0) { 127 return(1); 128 } 129 return(0); 130 } 131 132 const char * 133 sizetostr(off_t size) 134 { 135 static char buf[32]; 136 137 if (size < 1024 / 2) { 138 snprintf(buf, sizeof(buf), "%6.2fB", (double)size); 139 } else if (size < 1024 * 1024 / 2) { 140 snprintf(buf, sizeof(buf), "%6.2fKB", 141 (double)size / 1024); 142 } else if (size < 1024 * 1024 * 1024LL / 2) { 143 snprintf(buf, sizeof(buf), "%6.2fMB", 144 (double)size / (1024 * 1024)); 145 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 146 snprintf(buf, sizeof(buf), "%6.2fGB", 147 (double)size / (1024 * 1024 * 1024LL)); 148 } else { 149 snprintf(buf, sizeof(buf), "%6.2fTB", 150 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 151 } 152 return(buf); 153 } 154 155 int 156 hammer_fs_to_vol(const char *fs, struct hammer_ioc_volume_list *p) 157 { 158 struct hammer_ioc_volume_list ioc; 159 int fd; 160 161 fd = open(fs, O_RDONLY); 162 if (fd < 0) { 163 perror("open"); 164 return(-1); 165 } 166 167 bzero(&ioc, sizeof(ioc)); 168 ioc.nvols = HAMMER_MAX_VOLUMES; 169 ioc.vols = malloc(ioc.nvols * sizeof(*ioc.vols)); 170 if (ioc.vols == NULL) { 171 perror("malloc"); 172 close(fd); 173 return(-1); 174 } 175 176 if (ioctl(fd, HAMMERIOC_LIST_VOLUMES, &ioc) < 0) { 177 perror("ioctl"); 178 close(fd); 179 free(ioc.vols); 180 return(-1); 181 } 182 183 bcopy(&ioc, p, sizeof(ioc)); 184 close(fd); 185 186 return(0); 187 } 188 189 int 190 hammer_fs_to_rootvol(const char *fs, char *buf, int len) 191 { 192 struct hammer_ioc_volume_list ioc; 193 int i; 194 195 if (hammer_fs_to_vol(fs, &ioc) == -1) 196 return(-1); 197 198 for (i = 0; i < ioc.nvols; i++) { 199 if (ioc.vols[i].vol_no == HAMMER_ROOT_VOLNO) { 200 strlcpy(buf, ioc.vols[i].device_name, len); 201 break; 202 } 203 } 204 assert(i != ioc.nvols); /* root volume must exist */ 205 206 free(ioc.vols); 207 return(0); 208 } 209 210 /* 211 * Functions and data structure for zone statistics 212 */ 213 /* 214 * Each layer1 needs ((2^19) / 64) = 8192 uint64_t. 215 */ 216 #define HAMMER_LAYER1_UINT64 8192 217 #define HAMMER_LAYER1_BYTES (HAMMER_LAYER1_UINT64 * sizeof(uint64_t)) 218 219 static int *l1_max = NULL; 220 static uint64_t **l1_bits = NULL; 221 222 static __inline 223 int 224 hammer_set_layer_bits(uint64_t *bits, int i) 225 { 226 int q, r; 227 228 q = i >> 6; 229 r = i & ((1 << 6) - 1); 230 231 bits += q; 232 if (!((*bits) & ((uint64_t)1 << r))) { 233 (*bits) |= ((uint64_t)1 << r); 234 return(1); 235 } 236 return(0); /* already seen this block */ 237 } 238 239 static 240 void 241 hammer_extend_layer1_bits(int vol, int newsiz, int oldsiz) 242 { 243 uint64_t *p; 244 245 assert(newsiz > oldsiz); 246 assert(newsiz > 0 && oldsiz >= 0); 247 248 p = l1_bits[vol]; 249 if (p == NULL) 250 p = malloc(HAMMER_LAYER1_BYTES * newsiz); 251 else 252 p = realloc(p, HAMMER_LAYER1_BYTES * newsiz); 253 if (p == NULL) 254 err(1, "alloc"); 255 l1_bits[vol] = p; 256 257 p += HAMMER_LAYER1_UINT64 * oldsiz; 258 bzero(p, HAMMER_LAYER1_BYTES * (newsiz - oldsiz)); 259 } 260 261 struct zone_stat* 262 hammer_init_zone_stat(void) 263 { 264 return(calloc(HAMMER_MAX_ZONES, sizeof(struct zone_stat))); 265 } 266 267 struct zone_stat* 268 hammer_init_zone_stat_bits(void) 269 { 270 int i; 271 272 l1_max = calloc(HAMMER_MAX_VOLUMES, sizeof(int)); 273 if (l1_max == NULL) 274 err(1, "calloc"); 275 276 l1_bits = calloc(HAMMER_MAX_VOLUMES, sizeof(uint64_t*)); 277 if (l1_bits == NULL) 278 err(1, "calloc"); 279 280 for (i = 0; i < HAMMER_MAX_VOLUMES; i++) { 281 l1_max[i] = -1; /* +1 needs to be 0 */ 282 l1_bits[i] = NULL; 283 } 284 return(hammer_init_zone_stat()); 285 } 286 287 void 288 hammer_cleanup_zone_stat(struct zone_stat *stats) 289 { 290 int i; 291 292 if (l1_bits) { 293 for (i = 0; i < HAMMER_MAX_VOLUMES; i++) { 294 free(l1_bits[i]); 295 l1_bits[i] = NULL; 296 } 297 } 298 299 free(l1_bits); 300 l1_bits = NULL; 301 302 free(l1_max); 303 l1_max = NULL; 304 305 free(stats); 306 } 307 308 static 309 void 310 _hammer_add_zone_stat(struct zone_stat *stats, int zone, int bytes, 311 int new_block, int new_item) 312 { 313 struct zone_stat *sp = stats + zone; 314 315 if (new_block) 316 sp->blocks++; 317 if (new_item) 318 sp->items++; 319 sp->used += bytes; 320 } 321 322 void 323 hammer_add_zone_stat(struct zone_stat *stats, hammer_off_t offset, int bytes) 324 { 325 int zone, vol, i, j, new_block; 326 uint64_t *p; 327 328 offset &= ~HAMMER_BIGBLOCK_MASK64; 329 zone = HAMMER_ZONE_DECODE(offset); 330 vol = HAMMER_VOL_DECODE(offset); 331 332 offset &= HAMMER_OFF_SHORT_MASK; /* cut off volume bits from layer1 */ 333 i = HAMMER_BLOCKMAP_LAYER1_INDEX(offset); 334 j = HAMMER_BLOCKMAP_LAYER2_INDEX(offset); 335 336 if (i > l1_max[vol]) { 337 assert(i < (HAMMER_BLOCKMAP_RADIX1 / HAMMER_MAX_VOLUMES)); 338 hammer_extend_layer1_bits(vol, i + 1, l1_max[vol] + 1); 339 l1_max[vol] = i; 340 } 341 342 p = l1_bits[vol] + i * HAMMER_LAYER1_UINT64; 343 new_block = hammer_set_layer_bits(p, j); 344 _hammer_add_zone_stat(stats, zone, bytes, new_block, 1); 345 } 346 347 /* 348 * If the same layer2 is used more than once the result will be wrong. 349 */ 350 void 351 hammer_add_zone_stat_layer2(struct zone_stat *stats, 352 hammer_blockmap_layer2_t layer2) 353 { 354 _hammer_add_zone_stat(stats, layer2->zone, 355 HAMMER_BIGBLOCK_SIZE - layer2->bytes_free, 1, 0); 356 } 357 358 static __inline 359 double 360 _calc_used_percentage(int64_t blocks, int64_t used) 361 { 362 double res; 363 364 if (blocks) 365 res = ((double)(used * 100)) / (blocks << HAMMER_BIGBLOCK_BITS); 366 else 367 res = 0; 368 return(res); 369 } 370 371 void 372 hammer_print_zone_stat(const struct zone_stat *stats) 373 { 374 int i; 375 int64_t total_blocks = 0; 376 int64_t total_items = 0; 377 int64_t total_used = 0; 378 const struct zone_stat *p = stats; 379 #define INDENT "" 380 381 printf("HAMMER zone statistics\n"); 382 printf(INDENT"zone # " 383 "blocks items used[B] used[%%]\n"); 384 for (i = 0; i < HAMMER_MAX_ZONES; i++) { 385 printf(INDENT"zone %-2d %-10s %-12ju %-18ju %-19ju %g\n", 386 i, zone_labels[i], p->blocks, p->items, p->used, 387 _calc_used_percentage(p->blocks, p->used)); 388 total_blocks += p->blocks; 389 total_items += p->items; 390 total_used += p->used; 391 p++; 392 } 393 394 /* 395 * Remember that zone0 is always 0% used and zone15 is 396 * always 100% used. 397 */ 398 printf(INDENT"--------------------------------------------------------------------------------\n"); 399 printf(INDENT"total %-12ju %-18ju %-19ju %g\n", 400 (uintmax_t)total_blocks, 401 (uintmax_t)total_items, 402 (uintmax_t)total_used, 403 _calc_used_percentage(total_blocks, total_used)); 404 } 405