1 /* 2 * Copyright (c) 2007 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 35 #include <sys/diskslice.h> 36 #include <sys/diskmbr.h> 37 38 #include "hammer_util.h" 39 40 static void check_volume(struct volume_info *vol); 41 static void get_buffer_readahead(struct buffer_info *base); 42 static __inline int readhammervol(struct volume_info *vol); 43 static __inline int readhammerbuf(struct buffer_info *buf); 44 static __inline int writehammervol(struct volume_info *vol); 45 static __inline int writehammerbuf(struct buffer_info *buf); 46 47 uuid_t Hammer_FSType; 48 uuid_t Hammer_FSId; 49 int UseReadBehind = -4; 50 int UseReadAhead = 4; 51 int DebugOpt; 52 53 TAILQ_HEAD(volume_list, volume_info); 54 static struct volume_list VolList = TAILQ_HEAD_INITIALIZER(VolList); 55 static int valid_hammer_volumes; 56 57 static __inline 58 int 59 buffer_hash(hammer_off_t buf_offset) 60 { 61 int hi; 62 63 hi = (int)(buf_offset / HAMMER_BUFSIZE) & HAMMER_BUFLISTMASK; 64 return(hi); 65 } 66 67 static struct buffer_info* 68 find_buffer(struct volume_info *volume, hammer_off_t buf_offset) 69 { 70 int hi; 71 struct buffer_info *buf; 72 73 hi = buffer_hash(buf_offset); 74 TAILQ_FOREACH(buf, &volume->buffer_lists[hi], entry) 75 if (buf->buf_offset == buf_offset) 76 return(buf); 77 return(NULL); 78 } 79 80 static 81 struct volume_info * 82 __alloc_volume(const char *volname, int oflags) 83 { 84 struct volume_info *vol; 85 int i; 86 87 vol = malloc(sizeof(*vol)); 88 if (vol == NULL) 89 err(1, "alloc_volume"); 90 bzero(vol, sizeof(*vol)); 91 92 vol->vol_no = -1; 93 vol->rdonly = (oflags == O_RDONLY); 94 vol->name = strdup(volname); 95 vol->fd = open(vol->name, oflags); 96 if (vol->fd < 0) 97 err(1, "alloc_volume: Failed to open %s", vol->name); 98 check_volume(vol); 99 100 vol->ondisk = malloc(HAMMER_BUFSIZE); 101 if (vol->ondisk == NULL) 102 err(1, "alloc_volume"); 103 bzero(vol->ondisk, HAMMER_BUFSIZE); 104 105 for (i = 0; i < HAMMER_BUFLISTS; ++i) 106 TAILQ_INIT(&vol->buffer_lists[i]); 107 108 return(vol); 109 } 110 111 static void 112 __add_volume(struct volume_info *vol) 113 { 114 struct volume_info *scan; 115 struct stat st1, st2; 116 117 if (fstat(vol->fd, &st1) != 0) 118 errx(1, "add_volume: %s: Failed to stat", vol->name); 119 120 TAILQ_FOREACH(scan, &VolList, entry) { 121 if (scan->vol_no == vol->vol_no) { 122 errx(1, "add_volume: %s: Duplicate volume number %d " 123 "against %s", 124 vol->name, vol->vol_no, scan->name); 125 } 126 if (fstat(scan->fd, &st2) != 0) { 127 errx(1, "add_volume: %s: Failed to stat %s", 128 vol->name, scan->name); 129 } 130 if ((st1.st_ino == st2.st_ino) && (st1.st_dev == st2.st_dev)) { 131 errx(1, "add_volume: %s: Specified more than once", 132 vol->name); 133 } 134 } 135 136 TAILQ_INSERT_TAIL(&VolList, vol, entry); 137 } 138 139 static void 140 __verify_volume(struct volume_info *vol) 141 { 142 hammer_volume_ondisk_t ondisk = vol->ondisk; 143 144 if (ondisk->vol_signature != HAMMER_FSBUF_VOLUME) { 145 errx(1, "verify_volume: Invalid volume signature %016jx", 146 ondisk->vol_signature); 147 } 148 if (ondisk->vol_rootvol != HAMMER_ROOT_VOLNO) { 149 errx(1, "verify_volume: Invalid root volume# %d", 150 ondisk->vol_rootvol); 151 } 152 if (bcmp(&Hammer_FSType, &ondisk->vol_fstype, sizeof(Hammer_FSType))) { 153 errx(1, "verify_volume: %s: Header does not indicate " 154 "that this is a HAMMER volume", vol->name); 155 } 156 if (bcmp(&Hammer_FSId, &ondisk->vol_fsid, sizeof(Hammer_FSId))) { 157 errx(1, "verify_volume: %s: FSId does not match other volumes!", 158 vol->name); 159 } 160 } 161 162 /* 163 * Initialize a volume structure and ondisk vol_no field. 164 */ 165 struct volume_info * 166 init_volume(const char *filename, int oflags, int32_t vol_no) 167 { 168 struct volume_info *vol; 169 170 vol = __alloc_volume(filename, oflags); 171 vol->vol_no = vol->ondisk->vol_no = vol_no; 172 173 __add_volume(vol); 174 175 return(vol); 176 } 177 178 /* 179 * Initialize a volume structure and read ondisk volume header. 180 */ 181 struct volume_info* 182 load_volume(const char *filename, int oflags, int verify) 183 { 184 struct volume_info *vol; 185 int n; 186 187 vol = __alloc_volume(filename, oflags); 188 189 n = readhammervol(vol); 190 if (n == -1) { 191 err(1, "load_volume: %s: Read failed at offset 0", vol->name); 192 } 193 vol->vol_no = vol->ondisk->vol_no; 194 195 if (valid_hammer_volumes++ == 0) 196 Hammer_FSId = vol->ondisk->vol_fsid; 197 if (verify) 198 __verify_volume(vol); 199 200 __add_volume(vol); 201 202 return(vol); 203 } 204 205 /* 206 * Check basic volume characteristics. 207 */ 208 static void 209 check_volume(struct volume_info *vol) 210 { 211 struct partinfo pinfo; 212 struct stat st; 213 214 /* 215 * Get basic information about the volume 216 */ 217 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) { 218 /* 219 * Allow the formatting of regular files as HAMMER volumes 220 */ 221 if (fstat(vol->fd, &st) < 0) 222 err(1, "Unable to stat %s", vol->name); 223 vol->size = st.st_size; 224 vol->type = "REGFILE"; 225 } else { 226 /* 227 * When formatting a block device as a HAMMER volume the 228 * sector size must be compatible. HAMMER uses 16384 byte 229 * filesystem buffers. 230 */ 231 if (pinfo.reserved_blocks) { 232 errx(1, "HAMMER cannot be placed in a partition " 233 "which overlaps the disklabel or MBR"); 234 } 235 if (pinfo.media_blksize > HAMMER_BUFSIZE || 236 HAMMER_BUFSIZE % pinfo.media_blksize) { 237 errx(1, "A media sector size of %d is not supported", 238 pinfo.media_blksize); 239 } 240 241 vol->size = pinfo.media_size; 242 vol->device_offset = pinfo.media_offset; 243 vol->type = "DEVICE"; 244 } 245 } 246 247 void 248 assert_volume_offset(struct volume_info *vol) 249 { 250 assert(hammer_is_zone_raw_buffer(vol->vol_free_off)); 251 assert(hammer_is_zone_raw_buffer(vol->vol_free_end)); 252 if (vol->vol_free_off >= vol->vol_free_end) 253 errx(1, "Ran out of room, filesystem too small"); 254 } 255 256 struct volume_info * 257 get_volume(int32_t vol_no) 258 { 259 struct volume_info *vol; 260 261 TAILQ_FOREACH(vol, &VolList, entry) { 262 if (vol->vol_no == vol_no) 263 break; 264 } 265 266 return(vol); 267 } 268 269 struct volume_info * 270 get_root_volume(void) 271 { 272 return(get_volume(HAMMER_ROOT_VOLNO)); 273 } 274 275 /* 276 * Acquire the specified buffer. isnew is -1 only when called 277 * via get_buffer_readahead() to prevent another readahead. 278 */ 279 static struct buffer_info * 280 get_buffer(hammer_off_t buf_offset, int isnew) 281 { 282 struct buffer_info *buf; 283 struct volume_info *volume; 284 int vol_no; 285 int zone; 286 int hi; 287 int dora = 0; 288 int error = 0; 289 290 zone = HAMMER_ZONE_DECODE(buf_offset); 291 if (zone > HAMMER_ZONE_RAW_BUFFER_INDEX) 292 buf_offset = blockmap_lookup(buf_offset, NULL, NULL, &error); 293 if (error || buf_offset == HAMMER_OFF_BAD) 294 return(NULL); 295 assert(hammer_is_zone_raw_buffer(buf_offset)); 296 297 vol_no = HAMMER_VOL_DECODE(buf_offset); 298 volume = get_volume(vol_no); 299 assert(volume != NULL); 300 301 buf_offset &= ~HAMMER_BUFMASK64; 302 buf = find_buffer(volume, buf_offset); 303 304 if (buf == NULL) { 305 buf = malloc(sizeof(*buf)); 306 bzero(buf, sizeof(*buf)); 307 buf->buf_offset = buf_offset; 308 buf->raw_offset = hammer_xlate_to_phys(volume->ondisk, 309 buf_offset); 310 buf->volume = volume; 311 buf->ondisk = malloc(HAMMER_BUFSIZE); 312 if (isnew <= 0) { 313 if (readhammerbuf(buf) == -1) { 314 err(1, "get_buffer: %s:%016jx " 315 "Read failed at offset %016jx", 316 volume->name, 317 (intmax_t)buf->buf_offset, 318 (intmax_t)buf->raw_offset); 319 } 320 } 321 322 hi = buffer_hash(buf_offset); 323 TAILQ_INSERT_TAIL(&volume->buffer_lists[hi], buf, entry); 324 hammer_cache_add(&buf->cache); 325 dora = (isnew == 0); 326 } else { 327 assert(buf->ondisk != NULL); 328 assert(isnew != -1); 329 hammer_cache_used(&buf->cache); 330 } 331 332 ++buf->cache.refs; 333 hammer_cache_flush(); 334 335 if (isnew > 0) { 336 assert(buf->cache.modified == 0); 337 bzero(buf->ondisk, HAMMER_BUFSIZE); 338 buf->cache.modified = 1; 339 } 340 if (dora) 341 get_buffer_readahead(buf); 342 return(buf); 343 } 344 345 static void 346 get_buffer_readahead(struct buffer_info *base) 347 { 348 struct buffer_info *buf; 349 struct volume_info *vol; 350 hammer_off_t buf_offset; 351 int64_t raw_offset; 352 int ri = UseReadBehind; 353 int re = UseReadAhead; 354 355 raw_offset = base->raw_offset + ri * HAMMER_BUFSIZE; 356 vol = base->volume; 357 358 while (ri < re) { 359 if (raw_offset >= vol->ondisk->vol_buf_end) 360 break; 361 if (raw_offset < vol->ondisk->vol_buf_beg || ri == 0) { 362 ++ri; 363 raw_offset += HAMMER_BUFSIZE; 364 continue; 365 } 366 buf_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 367 raw_offset - vol->ondisk->vol_buf_beg); 368 buf = find_buffer(vol, buf_offset); 369 if (buf == NULL) { 370 buf = get_buffer(buf_offset, -1); 371 rel_buffer(buf); 372 } 373 ++ri; 374 raw_offset += HAMMER_BUFSIZE; 375 } 376 } 377 378 void 379 rel_buffer(struct buffer_info *buffer) 380 { 381 struct volume_info *volume; 382 int hi; 383 384 if (buffer == NULL) 385 return; 386 assert(buffer->cache.refs > 0); 387 if (--buffer->cache.refs == 0) { 388 if (buffer->cache.delete) { 389 hi = buffer_hash(buffer->buf_offset); 390 volume = buffer->volume; 391 if (buffer->cache.modified) 392 flush_buffer(buffer); 393 TAILQ_REMOVE(&volume->buffer_lists[hi], buffer, entry); 394 hammer_cache_del(&buffer->cache); 395 free(buffer->ondisk); 396 free(buffer); 397 } 398 } 399 } 400 401 /* 402 * Retrieve a pointer to a buffer data given a buffer offset. The underlying 403 * bufferp is freed if isnew or the offset is out of range of the cached data. 404 * If bufferp is freed a referenced buffer is loaded into it. 405 */ 406 void * 407 get_buffer_data(hammer_off_t buf_offset, struct buffer_info **bufferp, 408 int isnew) 409 { 410 if (*bufferp != NULL) { 411 if (isnew > 0 || 412 (((*bufferp)->buf_offset ^ buf_offset) & ~HAMMER_BUFMASK64)) { 413 rel_buffer(*bufferp); 414 *bufferp = NULL; 415 } 416 } 417 418 if (*bufferp == NULL) { 419 *bufferp = get_buffer(buf_offset, isnew); 420 if (*bufferp == NULL) 421 return(NULL); 422 } 423 424 return(((char *)(*bufferp)->ondisk) + 425 ((int32_t)buf_offset & HAMMER_BUFMASK)); 426 } 427 428 /* 429 * Allocate HAMMER elements - B-Tree nodes 430 */ 431 hammer_node_ondisk_t 432 alloc_btree_node(hammer_off_t *offp, struct buffer_info **data_bufferp) 433 { 434 hammer_node_ondisk_t node; 435 436 node = alloc_blockmap(HAMMER_ZONE_BTREE_INDEX, sizeof(*node), 437 offp, data_bufferp); 438 bzero(node, sizeof(*node)); 439 return(node); 440 } 441 442 /* 443 * Allocate HAMMER elements - meta data (inode, direntry, PFS, etc) 444 */ 445 void * 446 alloc_meta_element(hammer_off_t *offp, int32_t data_len, 447 struct buffer_info **data_bufferp) 448 { 449 void *data; 450 451 data = alloc_blockmap(HAMMER_ZONE_META_INDEX, data_len, 452 offp, data_bufferp); 453 bzero(data, data_len); 454 return(data); 455 } 456 457 /* 458 * Format a new blockmap. This is mostly a degenerate case because 459 * all allocations are now actually done from the freemap. 460 */ 461 void 462 format_blockmap(struct volume_info *root_vol, int zone, hammer_off_t offset) 463 { 464 hammer_blockmap_t blockmap; 465 hammer_off_t zone_base; 466 467 /* Only root volume needs formatting */ 468 assert(root_vol->vol_no == HAMMER_ROOT_VOLNO); 469 470 assert(hammer_is_zone2_mapped_index(zone)); 471 472 blockmap = &root_vol->ondisk->vol0_blockmap[zone]; 473 zone_base = HAMMER_ZONE_ENCODE(zone, offset); 474 475 bzero(blockmap, sizeof(*blockmap)); 476 blockmap->phys_offset = 0; 477 blockmap->first_offset = zone_base; 478 blockmap->next_offset = zone_base; 479 blockmap->alloc_offset = HAMMER_ENCODE(zone, 255, -1); 480 hammer_crc_set_blockmap(blockmap); 481 } 482 483 /* 484 * Format a new freemap. Set all layer1 entries to UNAVAIL. The initialize 485 * code will load each volume's freemap. 486 */ 487 void 488 format_freemap(struct volume_info *root_vol) 489 { 490 struct buffer_info *buffer = NULL; 491 hammer_off_t layer1_offset; 492 hammer_blockmap_t blockmap; 493 hammer_blockmap_layer1_t layer1; 494 int i, isnew; 495 496 /* Only root volume needs formatting */ 497 assert(root_vol->vol_no == HAMMER_ROOT_VOLNO); 498 499 layer1_offset = bootstrap_bigblock(root_vol); 500 for (i = 0; i < HAMMER_BIGBLOCK_SIZE; i += sizeof(*layer1)) { 501 isnew = ((i % HAMMER_BUFSIZE) == 0); 502 layer1 = get_buffer_data(layer1_offset + i, &buffer, isnew); 503 bzero(layer1, sizeof(*layer1)); 504 layer1->phys_offset = HAMMER_BLOCKMAP_UNAVAIL; 505 layer1->blocks_free = 0; 506 hammer_crc_set_layer1(layer1); 507 } 508 assert(i == HAMMER_BIGBLOCK_SIZE); 509 rel_buffer(buffer); 510 511 blockmap = &root_vol->ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 512 bzero(blockmap, sizeof(*blockmap)); 513 blockmap->phys_offset = layer1_offset; 514 blockmap->first_offset = 0; 515 blockmap->next_offset = HAMMER_ENCODE_RAW_BUFFER(0, 0); 516 blockmap->alloc_offset = HAMMER_ENCODE_RAW_BUFFER(255, -1); 517 hammer_crc_set_blockmap(blockmap); 518 } 519 520 /* 521 * Load the volume's remaining free space into the freemap. 522 * 523 * Returns the number of big-blocks available. 524 */ 525 int64_t 526 initialize_freemap(struct volume_info *vol) 527 { 528 struct volume_info *root_vol; 529 struct buffer_info *buffer1 = NULL; 530 struct buffer_info *buffer2 = NULL; 531 hammer_blockmap_layer1_t layer1; 532 hammer_blockmap_layer2_t layer2; 533 hammer_off_t layer1_offset; 534 hammer_off_t layer2_offset; 535 hammer_off_t phys_offset; 536 hammer_off_t block_offset; 537 hammer_off_t aligned_vol_free_end; 538 hammer_blockmap_t freemap; 539 int64_t count = 0; 540 int64_t layer1_count = 0; 541 542 root_vol = get_root_volume(); 543 544 assert_volume_offset(vol); 545 aligned_vol_free_end = HAMMER_BLOCKMAP_LAYER2_DOALIGN(vol->vol_free_end); 546 547 printf("initialize freemap volume %d\n", vol->vol_no); 548 549 /* 550 * Initialize the freemap. First preallocate the big-blocks required 551 * to implement layer2. This preallocation is a bootstrap allocation 552 * using blocks from the target volume. 553 */ 554 freemap = &root_vol->ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 555 556 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 557 phys_offset < aligned_vol_free_end; 558 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 559 layer1_offset = freemap->phys_offset + 560 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 561 layer1 = get_buffer_data(layer1_offset, &buffer1, 0); 562 if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) { 563 layer1->phys_offset = bootstrap_bigblock(vol); 564 layer1->blocks_free = 0; 565 buffer1->cache.modified = 1; 566 hammer_crc_set_layer1(layer1); 567 } 568 } 569 570 /* 571 * Now fill everything in. 572 */ 573 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 574 phys_offset < aligned_vol_free_end; 575 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 576 layer1_count = 0; 577 layer1_offset = freemap->phys_offset + 578 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 579 layer1 = get_buffer_data(layer1_offset, &buffer1, 0); 580 assert(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); 581 582 for (block_offset = 0; 583 block_offset < HAMMER_BLOCKMAP_LAYER2; 584 block_offset += HAMMER_BIGBLOCK_SIZE) { 585 layer2_offset = layer1->phys_offset + 586 HAMMER_BLOCKMAP_LAYER2_OFFSET(block_offset); 587 layer2 = get_buffer_data(layer2_offset, &buffer2, 0); 588 bzero(layer2, sizeof(*layer2)); 589 590 if (phys_offset + block_offset < vol->vol_free_off) { 591 /* 592 * Big-blocks already allocated as part 593 * of the freemap bootstrap. 594 */ 595 layer2->zone = HAMMER_ZONE_FREEMAP_INDEX; 596 layer2->append_off = HAMMER_BIGBLOCK_SIZE; 597 layer2->bytes_free = 0; 598 } else if (phys_offset + block_offset < vol->vol_free_end) { 599 layer2->zone = 0; 600 layer2->append_off = 0; 601 layer2->bytes_free = HAMMER_BIGBLOCK_SIZE; 602 ++count; 603 ++layer1_count; 604 } else { 605 layer2->zone = HAMMER_ZONE_UNAVAIL_INDEX; 606 layer2->append_off = HAMMER_BIGBLOCK_SIZE; 607 layer2->bytes_free = 0; 608 } 609 hammer_crc_set_layer2(layer2); 610 buffer2->cache.modified = 1; 611 } 612 613 layer1->blocks_free += layer1_count; 614 hammer_crc_set_layer1(layer1); 615 buffer1->cache.modified = 1; 616 } 617 618 rel_buffer(buffer1); 619 rel_buffer(buffer2); 620 return(count); 621 } 622 623 /* 624 * Returns the number of big-blocks available for filesystem data and undos 625 * without formatting. 626 */ 627 int64_t 628 count_freemap(struct volume_info *vol) 629 { 630 hammer_off_t phys_offset; 631 hammer_off_t vol_free_off; 632 hammer_off_t aligned_vol_free_end; 633 int64_t count = 0; 634 635 vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 636 637 assert_volume_offset(vol); 638 aligned_vol_free_end = HAMMER_BLOCKMAP_LAYER2_DOALIGN(vol->vol_free_end); 639 640 if (vol->vol_no == HAMMER_ROOT_VOLNO) 641 vol_free_off += HAMMER_BIGBLOCK_SIZE; 642 643 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 644 phys_offset < aligned_vol_free_end; 645 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 646 vol_free_off += HAMMER_BIGBLOCK_SIZE; 647 } 648 649 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 650 phys_offset < aligned_vol_free_end; 651 phys_offset += HAMMER_BIGBLOCK_SIZE) { 652 if (phys_offset < vol_free_off) { 653 ; 654 } else if (phys_offset < vol->vol_free_end) { 655 ++count; 656 } 657 } 658 659 return(count); 660 } 661 662 /* 663 * Format the undomap for the root volume. 664 */ 665 void 666 format_undomap(struct volume_info *root_vol, int64_t *undo_buffer_size) 667 { 668 hammer_off_t undo_limit; 669 hammer_blockmap_t blockmap; 670 hammer_volume_ondisk_t ondisk; 671 struct buffer_info *buffer = NULL; 672 hammer_off_t scan; 673 int n; 674 int limit_index; 675 uint32_t seqno; 676 677 /* Only root volume needs formatting */ 678 assert(root_vol->vol_no == HAMMER_ROOT_VOLNO); 679 ondisk = root_vol->ondisk; 680 681 /* 682 * Size the undo buffer in multiples of HAMMER_BIGBLOCK_SIZE, 683 * up to HAMMER_MAX_UNDO_BIGBLOCKS big-blocks. 684 * Size to approximately 0.1% of the disk. 685 * 686 * The minimum UNDO fifo size is 512MB, or approximately 1% of 687 * the recommended 50G disk. 688 * 689 * Changing this minimum is rather dangerous as complex filesystem 690 * operations can cause the UNDO FIFO to fill up otherwise. 691 */ 692 undo_limit = *undo_buffer_size; 693 if (undo_limit == 0) { 694 undo_limit = HAMMER_VOL_BUF_SIZE(ondisk) / 1000; 695 if (undo_limit < HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS) 696 undo_limit = HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS; 697 } 698 undo_limit = HAMMER_BIGBLOCK_DOALIGN(undo_limit); 699 if (undo_limit < HAMMER_BIGBLOCK_SIZE) 700 undo_limit = HAMMER_BIGBLOCK_SIZE; 701 if (undo_limit > HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS) 702 undo_limit = HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS; 703 *undo_buffer_size = undo_limit; 704 705 blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX]; 706 bzero(blockmap, sizeof(*blockmap)); 707 blockmap->phys_offset = HAMMER_BLOCKMAP_UNAVAIL; 708 blockmap->first_offset = HAMMER_ENCODE_UNDO(0); 709 blockmap->next_offset = blockmap->first_offset; 710 blockmap->alloc_offset = HAMMER_ENCODE_UNDO(undo_limit); 711 hammer_crc_set_blockmap(blockmap); 712 713 limit_index = undo_limit / HAMMER_BIGBLOCK_SIZE; 714 assert(limit_index <= HAMMER_MAX_UNDO_BIGBLOCKS); 715 716 for (n = 0; n < limit_index; ++n) { 717 ondisk->vol0_undo_array[n] = alloc_undo_bigblock(root_vol); 718 } 719 while (n < HAMMER_MAX_UNDO_BIGBLOCKS) { 720 ondisk->vol0_undo_array[n++] = HAMMER_BLOCKMAP_UNAVAIL; 721 } 722 723 /* 724 * Pre-initialize the UNDO blocks (HAMMER version 4+) 725 */ 726 printf("initializing the undo map (%jd MB)\n", 727 (intmax_t)HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset) / 728 (1024 * 1024)); 729 730 scan = blockmap->first_offset; 731 seqno = 0; 732 733 while (scan < blockmap->alloc_offset) { 734 hammer_fifo_head_t head; 735 hammer_fifo_tail_t tail; 736 int isnew; 737 int bytes = HAMMER_UNDO_ALIGN; 738 739 isnew = ((scan & HAMMER_BUFMASK64) == 0); 740 head = get_buffer_data(scan, &buffer, isnew); 741 buffer->cache.modified = 1; 742 tail = (void *)((char *)head + bytes - sizeof(*tail)); 743 744 bzero(head, bytes); 745 head->hdr_signature = HAMMER_HEAD_SIGNATURE; 746 head->hdr_type = HAMMER_HEAD_TYPE_DUMMY; 747 head->hdr_size = bytes; 748 head->hdr_seq = seqno++; 749 750 tail->tail_signature = HAMMER_TAIL_SIGNATURE; 751 tail->tail_type = HAMMER_HEAD_TYPE_DUMMY; 752 tail->tail_size = bytes; 753 754 hammer_crc_set_fifo_head(head, bytes); 755 756 scan += bytes; 757 } 758 rel_buffer(buffer); 759 } 760 761 const char *zone_labels[] = { 762 "", /* 0 */ 763 "raw_volume", /* 1 */ 764 "raw_buffer", /* 2 */ 765 "undo", /* 3 */ 766 "freemap", /* 4 */ 767 "", /* 5 */ 768 "", /* 6 */ 769 "", /* 7 */ 770 "btree", /* 8 */ 771 "meta", /* 9 */ 772 "large_data", /* 10 */ 773 "small_data", /* 11 */ 774 "", /* 12 */ 775 "", /* 13 */ 776 "", /* 14 */ 777 "unavail", /* 15 */ 778 }; 779 780 void 781 print_blockmap(const struct volume_info *vol) 782 { 783 hammer_blockmap_t blockmap; 784 hammer_volume_ondisk_t ondisk; 785 int64_t size, used; 786 int i; 787 #define INDENT "" 788 789 ondisk = vol->ondisk; 790 printf(INDENT"vol_label\t%s\n", ondisk->vol_label); 791 printf(INDENT"vol_count\t%d\n", ondisk->vol_count); 792 printf(INDENT"vol_bot_beg\t%s\n", sizetostr(ondisk->vol_bot_beg)); 793 printf(INDENT"vol_mem_beg\t%s\n", sizetostr(ondisk->vol_mem_beg)); 794 printf(INDENT"vol_buf_beg\t%s\n", sizetostr(ondisk->vol_buf_beg)); 795 printf(INDENT"vol_buf_end\t%s\n", sizetostr(ondisk->vol_buf_end)); 796 printf(INDENT"vol0_next_tid\t%016jx\n", 797 (uintmax_t)ondisk->vol0_next_tid); 798 799 blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX]; 800 size = HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset); 801 if (blockmap->first_offset <= blockmap->next_offset) 802 used = blockmap->next_offset - blockmap->first_offset; 803 else 804 used = blockmap->alloc_offset - blockmap->first_offset + 805 HAMMER_OFF_LONG_ENCODE(blockmap->next_offset); 806 printf(INDENT"undo_size\t%s\n", sizetostr(size)); 807 printf(INDENT"undo_used\t%s\n", sizetostr(used)); 808 809 printf(INDENT"zone # " 810 "phys first next alloc\n"); 811 for (i = 0; i < HAMMER_MAX_ZONES; i++) { 812 blockmap = &ondisk->vol0_blockmap[i]; 813 printf(INDENT"zone %-2d %-10s %016jx %016jx %016jx %016jx\n", 814 i, zone_labels[i], 815 (uintmax_t)blockmap->phys_offset, 816 (uintmax_t)blockmap->first_offset, 817 (uintmax_t)blockmap->next_offset, 818 (uintmax_t)blockmap->alloc_offset); 819 } 820 } 821 822 /* 823 * Flush various tracking structures to disk 824 */ 825 void 826 flush_all_volumes(void) 827 { 828 struct volume_info *vol; 829 830 TAILQ_FOREACH(vol, &VolList, entry) 831 flush_volume(vol); 832 } 833 834 void 835 flush_volume(struct volume_info *volume) 836 { 837 struct buffer_info *buffer; 838 int i; 839 840 for (i = 0; i < HAMMER_BUFLISTS; ++i) { 841 TAILQ_FOREACH(buffer, &volume->buffer_lists[i], entry) 842 flush_buffer(buffer); 843 } 844 if (writehammervol(volume) == -1) 845 err(1, "Write volume %d (%s)", volume->vol_no, volume->name); 846 } 847 848 void 849 flush_buffer(struct buffer_info *buffer) 850 { 851 struct volume_info *vol; 852 853 vol = buffer->volume; 854 if (writehammerbuf(buffer) == -1) 855 err(1, "Write volume %d (%s)", vol->vol_no, vol->name); 856 buffer->cache.modified = 0; 857 } 858 859 /* 860 * Core I/O operations 861 */ 862 static int 863 __read(struct volume_info *vol, void *data, int64_t offset, int size) 864 { 865 ssize_t n; 866 867 n = pread(vol->fd, data, size, offset); 868 if (n != size) 869 return(-1); 870 return(0); 871 } 872 873 static __inline int 874 readhammervol(struct volume_info *vol) 875 { 876 return(__read(vol, vol->ondisk, 0, HAMMER_BUFSIZE)); 877 } 878 879 static __inline int 880 readhammerbuf(struct buffer_info *buf) 881 { 882 return(__read(buf->volume, buf->ondisk, buf->raw_offset, HAMMER_BUFSIZE)); 883 } 884 885 static int 886 __write(struct volume_info *vol, const void *data, int64_t offset, int size) 887 { 888 ssize_t n; 889 890 if (vol->rdonly) 891 return(0); 892 893 n = pwrite(vol->fd, data, size, offset); 894 if (n != size) 895 return(-1); 896 return(0); 897 } 898 899 static __inline int 900 writehammervol(struct volume_info *vol) 901 { 902 return(__write(vol, vol->ondisk, 0, HAMMER_BUFSIZE)); 903 } 904 905 static __inline int 906 writehammerbuf(struct buffer_info *buf) 907 { 908 return(__write(buf->volume, buf->ondisk, buf->raw_offset, HAMMER_BUFSIZE)); 909 } 910 911 int64_t init_boot_area_size(int64_t value, off_t avg_vol_size) 912 { 913 if (value == 0) { 914 value = HAMMER_BOOT_NOMBYTES; 915 while (value > avg_vol_size / HAMMER_MAX_VOLUMES) 916 value >>= 1; 917 } 918 919 if (value < HAMMER_BOOT_MINBYTES) { 920 value = HAMMER_BOOT_MINBYTES; 921 } else if (value > HAMMER_BOOT_MAXBYTES) { 922 value = HAMMER_BOOT_MAXBYTES; 923 } 924 925 return(value); 926 } 927 928 int64_t init_memory_log_size(int64_t value, off_t avg_vol_size) 929 { 930 if (value == 0) { 931 value = HAMMER_MEM_NOMBYTES; 932 while (value > avg_vol_size / HAMMER_MAX_VOLUMES) 933 value >>= 1; 934 } 935 936 if (value < HAMMER_MEM_MINBYTES) { 937 value = HAMMER_MEM_MINBYTES; 938 } else if (value > HAMMER_MEM_MAXBYTES) { 939 value = HAMMER_MEM_MAXBYTES; 940 } 941 942 return(value); 943 } 944