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 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 } 253 254 struct volume_info * 255 get_volume(int32_t vol_no) 256 { 257 struct volume_info *vol; 258 259 TAILQ_FOREACH(vol, &VolList, entry) { 260 if (vol->vol_no == vol_no) 261 break; 262 } 263 264 return(vol); 265 } 266 267 struct volume_info * 268 get_root_volume(void) 269 { 270 return(get_volume(HAMMER_ROOT_VOLNO)); 271 } 272 273 /* 274 * Acquire the specified buffer. isnew is -1 only when called 275 * via get_buffer_readahead() to prevent another readahead. 276 */ 277 static struct buffer_info * 278 get_buffer(hammer_off_t buf_offset, int isnew) 279 { 280 struct buffer_info *buf; 281 struct volume_info *volume; 282 int vol_no; 283 int zone; 284 int hi; 285 int dora = 0; 286 int error = 0; 287 288 zone = HAMMER_ZONE_DECODE(buf_offset); 289 if (zone > HAMMER_ZONE_RAW_BUFFER_INDEX) 290 buf_offset = blockmap_lookup(buf_offset, NULL, NULL, &error); 291 if (error || buf_offset == HAMMER_OFF_BAD) 292 return(NULL); 293 assert(hammer_is_zone_raw_buffer(buf_offset)); 294 295 vol_no = HAMMER_VOL_DECODE(buf_offset); 296 volume = get_volume(vol_no); 297 assert(volume != NULL); 298 299 buf_offset &= ~HAMMER_BUFMASK64; 300 buf = find_buffer(volume, buf_offset); 301 302 if (buf == NULL) { 303 buf = malloc(sizeof(*buf)); 304 bzero(buf, sizeof(*buf)); 305 buf->buf_offset = buf_offset; 306 buf->raw_offset = hammer_xlate_to_phys(volume->ondisk, 307 buf_offset); 308 buf->volume = volume; 309 buf->ondisk = malloc(HAMMER_BUFSIZE); 310 if (isnew <= 0) { 311 if (readhammerbuf(buf) == -1) { 312 err(1, "get_buffer: %s:%016jx " 313 "Read failed at offset %016jx", 314 volume->name, 315 (intmax_t)buf->buf_offset, 316 (intmax_t)buf->raw_offset); 317 } 318 } 319 320 hi = buffer_hash(buf_offset); 321 TAILQ_INSERT_TAIL(&volume->buffer_lists[hi], buf, entry); 322 hammer_cache_add(&buf->cache); 323 dora = (isnew == 0); 324 } else { 325 assert(buf->ondisk != NULL); 326 assert(isnew != -1); 327 hammer_cache_used(&buf->cache); 328 } 329 330 ++buf->cache.refs; 331 hammer_cache_flush(); 332 333 if (isnew > 0) { 334 assert(buf->cache.modified == 0); 335 bzero(buf->ondisk, HAMMER_BUFSIZE); 336 buf->cache.modified = 1; 337 } 338 if (dora) 339 get_buffer_readahead(buf); 340 return(buf); 341 } 342 343 static void 344 get_buffer_readahead(struct buffer_info *base) 345 { 346 struct buffer_info *buf; 347 struct volume_info *vol; 348 hammer_off_t buf_offset; 349 int64_t raw_offset; 350 int ri = UseReadBehind; 351 int re = UseReadAhead; 352 353 raw_offset = base->raw_offset + ri * HAMMER_BUFSIZE; 354 vol = base->volume; 355 356 while (ri < re) { 357 if (raw_offset >= vol->ondisk->vol_buf_end) 358 break; 359 if (raw_offset < vol->ondisk->vol_buf_beg || ri == 0) { 360 ++ri; 361 raw_offset += HAMMER_BUFSIZE; 362 continue; 363 } 364 buf_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 365 raw_offset - vol->ondisk->vol_buf_beg); 366 buf = find_buffer(vol, buf_offset); 367 if (buf == NULL) { 368 buf = get_buffer(buf_offset, -1); 369 rel_buffer(buf); 370 } 371 ++ri; 372 raw_offset += HAMMER_BUFSIZE; 373 } 374 } 375 376 void 377 rel_buffer(struct buffer_info *buffer) 378 { 379 struct volume_info *volume; 380 int hi; 381 382 if (buffer == NULL) 383 return; 384 assert(buffer->cache.refs > 0); 385 if (--buffer->cache.refs == 0) { 386 if (buffer->cache.delete) { 387 hi = buffer_hash(buffer->buf_offset); 388 volume = buffer->volume; 389 if (buffer->cache.modified) 390 flush_buffer(buffer); 391 TAILQ_REMOVE(&volume->buffer_lists[hi], buffer, entry); 392 hammer_cache_del(&buffer->cache); 393 free(buffer->ondisk); 394 free(buffer); 395 } 396 } 397 } 398 399 /* 400 * Retrieve a pointer to a buffer data given a buffer offset. The underlying 401 * bufferp is freed if isnew or the offset is out of range of the cached data. 402 * If bufferp is freed a referenced buffer is loaded into it. 403 */ 404 void * 405 get_buffer_data(hammer_off_t buf_offset, struct buffer_info **bufferp, 406 int isnew) 407 { 408 if (*bufferp != NULL) { 409 if (isnew > 0 || 410 (((*bufferp)->buf_offset ^ buf_offset) & ~HAMMER_BUFMASK64)) { 411 rel_buffer(*bufferp); 412 *bufferp = NULL; 413 } 414 } 415 416 if (*bufferp == NULL) { 417 *bufferp = get_buffer(buf_offset, isnew); 418 if (*bufferp == NULL) 419 return(NULL); 420 } 421 422 return(((char *)(*bufferp)->ondisk) + 423 ((int32_t)buf_offset & HAMMER_BUFMASK)); 424 } 425 426 /* 427 * Allocate HAMMER elements - B-Tree nodes 428 */ 429 hammer_node_ondisk_t 430 alloc_btree_node(hammer_off_t *offp, struct buffer_info **data_bufferp) 431 { 432 hammer_node_ondisk_t node; 433 434 node = alloc_blockmap(HAMMER_ZONE_BTREE_INDEX, sizeof(*node), 435 offp, data_bufferp); 436 bzero(node, sizeof(*node)); 437 return(node); 438 } 439 440 /* 441 * Allocate HAMMER elements - meta data (inode, direntry, PFS, etc) 442 */ 443 void * 444 alloc_meta_element(hammer_off_t *offp, int32_t data_len, 445 struct buffer_info **data_bufferp) 446 { 447 void *data; 448 449 data = alloc_blockmap(HAMMER_ZONE_META_INDEX, data_len, 450 offp, data_bufferp); 451 bzero(data, data_len); 452 return(data); 453 } 454 455 /* 456 * Format a new blockmap. This is mostly a degenerate case because 457 * all allocations are now actually done from the freemap. 458 */ 459 void 460 format_blockmap(struct volume_info *root_vol, int zone, hammer_off_t offset) 461 { 462 hammer_blockmap_t blockmap; 463 hammer_off_t zone_base; 464 465 /* Only root volume needs formatting */ 466 assert(root_vol->vol_no == HAMMER_ROOT_VOLNO); 467 468 assert(hammer_is_zone2_mapped_index(zone)); 469 470 blockmap = &root_vol->ondisk->vol0_blockmap[zone]; 471 zone_base = HAMMER_ZONE_ENCODE(zone, offset); 472 473 bzero(blockmap, sizeof(*blockmap)); 474 blockmap->phys_offset = 0; 475 blockmap->first_offset = zone_base; 476 blockmap->next_offset = zone_base; 477 blockmap->alloc_offset = HAMMER_ENCODE(zone, 255, -1); 478 hammer_crc_set_blockmap(blockmap); 479 } 480 481 /* 482 * Format a new freemap. Set all layer1 entries to UNAVAIL. The initialize 483 * code will load each volume's freemap. 484 */ 485 void 486 format_freemap(struct volume_info *root_vol) 487 { 488 struct buffer_info *buffer = NULL; 489 hammer_off_t layer1_offset; 490 hammer_blockmap_t blockmap; 491 hammer_blockmap_layer1_t layer1; 492 int i, isnew; 493 494 /* Only root volume needs formatting */ 495 assert(root_vol->vol_no == HAMMER_ROOT_VOLNO); 496 497 layer1_offset = alloc_bigblock(root_vol, HAMMER_ZONE_FREEMAP_INDEX); 498 for (i = 0; i < HAMMER_BIGBLOCK_SIZE; i += sizeof(*layer1)) { 499 isnew = ((i % HAMMER_BUFSIZE) == 0); 500 layer1 = get_buffer_data(layer1_offset + i, &buffer, isnew); 501 bzero(layer1, sizeof(*layer1)); 502 layer1->phys_offset = HAMMER_BLOCKMAP_UNAVAIL; 503 layer1->blocks_free = 0; 504 hammer_crc_set_layer1(layer1); 505 } 506 assert(i == HAMMER_BIGBLOCK_SIZE); 507 rel_buffer(buffer); 508 509 blockmap = &root_vol->ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 510 bzero(blockmap, sizeof(*blockmap)); 511 blockmap->phys_offset = layer1_offset; 512 blockmap->first_offset = 0; 513 blockmap->next_offset = HAMMER_ENCODE_RAW_BUFFER(0, 0); 514 blockmap->alloc_offset = HAMMER_ENCODE_RAW_BUFFER(255, -1); 515 hammer_crc_set_blockmap(blockmap); 516 } 517 518 /* 519 * Load the volume's remaining free space into the freemap. 520 * 521 * Returns the number of big-blocks available. 522 */ 523 int64_t 524 initialize_freemap(struct volume_info *vol) 525 { 526 struct volume_info *root_vol; 527 struct buffer_info *buffer1 = NULL; 528 struct buffer_info *buffer2 = NULL; 529 hammer_blockmap_layer1_t layer1; 530 hammer_blockmap_layer2_t layer2; 531 hammer_off_t layer1_offset; 532 hammer_off_t layer2_offset; 533 hammer_off_t phys_offset; 534 hammer_off_t block_offset; 535 hammer_off_t aligned_vol_free_end; 536 hammer_blockmap_t freemap; 537 int64_t count = 0; 538 int64_t layer1_count = 0; 539 540 root_vol = get_root_volume(); 541 542 assert_volume_offset(vol); 543 aligned_vol_free_end = (vol->vol_free_end + HAMMER_BLOCKMAP_LAYER2_MASK) 544 & ~HAMMER_BLOCKMAP_LAYER2_MASK; 545 546 printf("initialize freemap volume %d\n", vol->vol_no); 547 548 /* 549 * Initialize the freemap. First preallocate the big-blocks required 550 * to implement layer2. This preallocation is a bootstrap allocation 551 * using blocks from the target volume. 552 */ 553 freemap = &root_vol->ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 554 555 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 556 phys_offset < aligned_vol_free_end; 557 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 558 layer1_offset = freemap->phys_offset + 559 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 560 layer1 = get_buffer_data(layer1_offset, &buffer1, 0); 561 if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) { 562 layer1->phys_offset = alloc_bigblock(vol, 563 HAMMER_ZONE_FREEMAP_INDEX); 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 = (vol->vol_free_end + HAMMER_BLOCKMAP_LAYER2_MASK) 639 & ~HAMMER_BLOCKMAP_LAYER2_MASK; 640 641 if (vol->vol_no == HAMMER_ROOT_VOLNO) 642 vol_free_off += HAMMER_BIGBLOCK_SIZE; 643 644 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 645 phys_offset < aligned_vol_free_end; 646 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 647 vol_free_off += HAMMER_BIGBLOCK_SIZE; 648 } 649 650 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 651 phys_offset < aligned_vol_free_end; 652 phys_offset += HAMMER_BIGBLOCK_SIZE) { 653 if (phys_offset < vol_free_off) { 654 ; 655 } else if (phys_offset < vol->vol_free_end) { 656 ++count; 657 } 658 } 659 660 return(count); 661 } 662 663 /* 664 * Format the undomap for the root volume. 665 */ 666 void 667 format_undomap(struct volume_info *root_vol, int64_t *undo_buffer_size) 668 { 669 const int undo_zone = HAMMER_ZONE_UNDO_INDEX; 670 hammer_off_t undo_limit; 671 hammer_blockmap_t blockmap; 672 hammer_volume_ondisk_t ondisk; 673 struct buffer_info *buffer = NULL; 674 hammer_off_t scan; 675 int n; 676 int limit_index; 677 uint32_t seqno; 678 679 /* Only root volume needs formatting */ 680 assert(root_vol->vol_no == HAMMER_ROOT_VOLNO); 681 ondisk = root_vol->ondisk; 682 683 /* 684 * Size the undo buffer in multiples of HAMMER_BIGBLOCK_SIZE, 685 * up to HAMMER_UNDO_LAYER2 big-blocks. Size to approximately 686 * 0.1% of the disk. 687 * 688 * The minimum UNDO fifo size is 500MB, or approximately 1% of 689 * the recommended 50G disk. 690 * 691 * Changing this minimum is rather dangerous as complex filesystem 692 * operations can cause the UNDO FIFO to fill up otherwise. 693 */ 694 undo_limit = *undo_buffer_size; 695 if (undo_limit == 0) { 696 undo_limit = HAMMER_VOL_BUF_SIZE(ondisk) / 1000; 697 if (undo_limit < 500*1024*1024) 698 undo_limit = 500*1024*1024; 699 } 700 undo_limit = (undo_limit + HAMMER_BIGBLOCK_MASK64) & 701 ~HAMMER_BIGBLOCK_MASK64; 702 if (undo_limit < HAMMER_BIGBLOCK_SIZE) 703 undo_limit = HAMMER_BIGBLOCK_SIZE; 704 if (undo_limit > HAMMER_BIGBLOCK_SIZE * HAMMER_UNDO_LAYER2) 705 undo_limit = HAMMER_BIGBLOCK_SIZE * HAMMER_UNDO_LAYER2; 706 *undo_buffer_size = undo_limit; 707 708 blockmap = &ondisk->vol0_blockmap[undo_zone]; 709 bzero(blockmap, sizeof(*blockmap)); 710 blockmap->phys_offset = HAMMER_BLOCKMAP_UNAVAIL; 711 blockmap->first_offset = HAMMER_ZONE_ENCODE(undo_zone, 0); 712 blockmap->next_offset = blockmap->first_offset; 713 blockmap->alloc_offset = HAMMER_ZONE_ENCODE(undo_zone, undo_limit); 714 hammer_crc_set_blockmap(blockmap); 715 716 limit_index = undo_limit / HAMMER_BIGBLOCK_SIZE; 717 assert(limit_index <= HAMMER_UNDO_LAYER2); 718 719 for (n = 0; n < limit_index; ++n) { 720 ondisk->vol0_undo_array[n] = alloc_bigblock(root_vol, 721 HAMMER_ZONE_UNDO_INDEX); 722 } 723 while (n < HAMMER_UNDO_LAYER2) { 724 ondisk->vol0_undo_array[n++] = HAMMER_BLOCKMAP_UNAVAIL; 725 } 726 727 /* 728 * Pre-initialize the UNDO blocks (HAMMER version 4+) 729 */ 730 printf("initializing the undo map (%jd MB)\n", 731 (intmax_t)(blockmap->alloc_offset & HAMMER_OFF_LONG_MASK) / 732 (1024 * 1024)); 733 734 scan = blockmap->first_offset; 735 seqno = 0; 736 737 while (scan < blockmap->alloc_offset) { 738 hammer_fifo_head_t head; 739 hammer_fifo_tail_t tail; 740 int isnew; 741 int bytes = HAMMER_UNDO_ALIGN; 742 743 isnew = ((scan & HAMMER_BUFMASK64) == 0); 744 head = get_buffer_data(scan, &buffer, isnew); 745 buffer->cache.modified = 1; 746 tail = (void *)((char *)head + bytes - sizeof(*tail)); 747 748 bzero(head, bytes); 749 head->hdr_signature = HAMMER_HEAD_SIGNATURE; 750 head->hdr_type = HAMMER_HEAD_TYPE_DUMMY; 751 head->hdr_size = bytes; 752 head->hdr_seq = seqno++; 753 754 tail->tail_signature = HAMMER_TAIL_SIGNATURE; 755 tail->tail_type = HAMMER_HEAD_TYPE_DUMMY; 756 tail->tail_size = bytes; 757 758 hammer_crc_set_fifo_head(head, bytes); 759 760 scan += bytes; 761 } 762 rel_buffer(buffer); 763 } 764 765 const char *zone_labels[] = { 766 "", /* 0 */ 767 "raw_volume", /* 1 */ 768 "raw_buffer", /* 2 */ 769 "undo", /* 3 */ 770 "freemap", /* 4 */ 771 "", /* 5 */ 772 "", /* 6 */ 773 "", /* 7 */ 774 "btree", /* 8 */ 775 "meta", /* 9 */ 776 "large_data", /* 10 */ 777 "small_data", /* 11 */ 778 "", /* 12 */ 779 "", /* 13 */ 780 "", /* 14 */ 781 "unavail", /* 15 */ 782 }; 783 784 void 785 print_blockmap(const struct volume_info *root_vol) 786 { 787 hammer_blockmap_t blockmap; 788 hammer_volume_ondisk_t ondisk; 789 int64_t size, used; 790 int i; 791 #define INDENT "" 792 793 ondisk = root_vol->ondisk; 794 printf(INDENT"vol_label\t%s\n", ondisk->vol_label); 795 printf(INDENT"vol_count\t%d\n", ondisk->vol_count); 796 printf(INDENT"vol_bot_beg\t%s\n", sizetostr(ondisk->vol_bot_beg)); 797 printf(INDENT"vol_mem_beg\t%s\n", sizetostr(ondisk->vol_mem_beg)); 798 printf(INDENT"vol_buf_beg\t%s\n", sizetostr(ondisk->vol_buf_beg)); 799 printf(INDENT"vol_buf_end\t%s\n", sizetostr(ondisk->vol_buf_end)); 800 printf(INDENT"vol0_next_tid\t%016jx\n", 801 (uintmax_t)ondisk->vol0_next_tid); 802 803 blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX]; 804 size = blockmap->alloc_offset & HAMMER_OFF_LONG_MASK; 805 if (blockmap->first_offset <= blockmap->next_offset) 806 used = blockmap->next_offset - blockmap->first_offset; 807 else 808 used = blockmap->alloc_offset - blockmap->first_offset + 809 (blockmap->next_offset & HAMMER_OFF_LONG_MASK); 810 printf(INDENT"undo_size\t%s\n", sizetostr(size)); 811 printf(INDENT"undo_used\t%s\n", sizetostr(used)); 812 813 printf(INDENT"zone # " 814 "phys first next alloc\n"); 815 for (i = 0; i < HAMMER_MAX_ZONES; i++) { 816 blockmap = &ondisk->vol0_blockmap[i]; 817 printf(INDENT"zone %-2d %-10s %016jx %016jx %016jx %016jx\n", 818 i, zone_labels[i], 819 (uintmax_t)blockmap->phys_offset, 820 (uintmax_t)blockmap->first_offset, 821 (uintmax_t)blockmap->next_offset, 822 (uintmax_t)blockmap->alloc_offset); 823 } 824 } 825 826 /* 827 * Flush various tracking structures to disk 828 */ 829 void 830 flush_all_volumes(void) 831 { 832 struct volume_info *vol; 833 834 TAILQ_FOREACH(vol, &VolList, entry) 835 flush_volume(vol); 836 } 837 838 void 839 flush_volume(struct volume_info *volume) 840 { 841 struct buffer_info *buffer; 842 int i; 843 844 for (i = 0; i < HAMMER_BUFLISTS; ++i) { 845 TAILQ_FOREACH(buffer, &volume->buffer_lists[i], entry) 846 flush_buffer(buffer); 847 } 848 if (writehammervol(volume) == -1) 849 err(1, "Write volume %d (%s)", volume->vol_no, volume->name); 850 } 851 852 void 853 flush_buffer(struct buffer_info *buffer) 854 { 855 struct volume_info *vol; 856 857 vol = buffer->volume; 858 if (writehammerbuf(buffer) == -1) 859 err(1, "Write volume %d (%s)", vol->vol_no, vol->name); 860 buffer->cache.modified = 0; 861 } 862 863 /* 864 * Core I/O operations 865 */ 866 static int 867 __read(struct volume_info *vol, void *data, int64_t offset, int size) 868 { 869 ssize_t n; 870 871 n = pread(vol->fd, data, size, offset); 872 if (n != size) 873 return(-1); 874 return(0); 875 } 876 877 static __inline int 878 readhammervol(struct volume_info *vol) 879 { 880 return(__read(vol, vol->ondisk, 0, HAMMER_BUFSIZE)); 881 } 882 883 static __inline int 884 readhammerbuf(struct buffer_info *buf) 885 { 886 return(__read(buf->volume, buf->ondisk, buf->raw_offset, HAMMER_BUFSIZE)); 887 } 888 889 static int 890 __write(struct volume_info *vol, const void *data, int64_t offset, int size) 891 { 892 ssize_t n; 893 894 if (vol->rdonly) 895 return(0); 896 897 n = pwrite(vol->fd, data, size, offset); 898 if (n != size) 899 return(-1); 900 return(0); 901 } 902 903 static __inline int 904 writehammervol(struct volume_info *vol) 905 { 906 return(__write(vol, vol->ondisk, 0, HAMMER_BUFSIZE)); 907 } 908 909 static __inline int 910 writehammerbuf(struct buffer_info *buf) 911 { 912 return(__write(buf->volume, buf->ondisk, buf->raw_offset, HAMMER_BUFSIZE)); 913 } 914 915 int64_t init_boot_area_size(int64_t value, off_t avg_vol_size) 916 { 917 if (value == 0) { 918 value = HAMMER_BOOT_NOMBYTES; 919 while (value > avg_vol_size / HAMMER_MAX_VOLUMES) 920 value >>= 1; 921 if (value < HAMMER_BOOT_MINBYTES) 922 value = 0; 923 } else if (value < HAMMER_BOOT_MINBYTES) { 924 value = HAMMER_BOOT_MINBYTES; 925 } 926 927 return(value); 928 } 929 930 int64_t init_mem_area_size(int64_t value, off_t avg_vol_size) 931 { 932 if (value == 0) { 933 value = HAMMER_MEM_NOMBYTES; 934 while (value > avg_vol_size / HAMMER_MAX_VOLUMES) 935 value >>= 1; 936 if (value < HAMMER_MEM_MINBYTES) 937 value = 0; 938 } else if (value < HAMMER_MEM_MINBYTES) { 939 value = HAMMER_MEM_MINBYTES; 940 } 941 942 return(value); 943 } 944