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/types.h> 36 #include <assert.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <stdarg.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <stddef.h> 43 #include <err.h> 44 #include <fcntl.h> 45 #include "hammer_util.h" 46 47 static void *alloc_blockmap(int zone, int bytes, hammer_off_t *result_offp, 48 struct buffer_info **bufferp); 49 static hammer_off_t alloc_bigblock(struct volume_info *volume, int zone); 50 static void get_buffer_readahead(struct buffer_info *base); 51 static __inline void *get_ondisk(hammer_off_t buf_offset, 52 struct buffer_info **bufferp, int isnew); 53 #if 0 54 static void init_fifo_head(hammer_fifo_head_t head, u_int16_t hdr_type); 55 static hammer_off_t hammer_alloc_fifo(int32_t base_bytes, int32_t ext_bytes, 56 struct buffer_info **bufp, u_int16_t hdr_type); 57 static void readhammerbuf(struct volume_info *vol, void *data, 58 int64_t offset); 59 #endif 60 static void writehammerbuf(struct volume_info *vol, const void *data, 61 int64_t offset); 62 63 int DebugOpt; 64 65 uuid_t Hammer_FSType; 66 uuid_t Hammer_FSId; 67 int64_t BootAreaSize; 68 int64_t MemAreaSize; 69 int64_t UndoBufferSize; 70 int UsingSuperClusters; 71 int NumVolumes; 72 int RootVolNo = -1; 73 int UseReadBehind = -4; 74 int UseReadAhead = 4; 75 int AssertOnFailure = 1; 76 struct volume_list VolList = TAILQ_HEAD_INITIALIZER(VolList); 77 78 static __inline 79 int 80 buffer_hash(hammer_off_t buf_offset) 81 { 82 int hi; 83 84 hi = (int)(buf_offset / HAMMER_BUFSIZE) & HAMMER_BUFLISTMASK; 85 return(hi); 86 } 87 88 /* 89 * Lookup the requested information structure and related on-disk buffer. 90 * Missing structures are created. 91 */ 92 struct volume_info * 93 setup_volume(int32_t vol_no, const char *filename, int isnew, int oflags) 94 { 95 struct volume_info *vol; 96 struct volume_info *scan; 97 struct hammer_volume_ondisk *ondisk; 98 int i, n; 99 100 /* 101 * Allocate the volume structure 102 */ 103 vol = malloc(sizeof(*vol)); 104 bzero(vol, sizeof(*vol)); 105 for (i = 0; i < HAMMER_BUFLISTS; ++i) 106 TAILQ_INIT(&vol->buffer_lists[i]); 107 vol->name = strdup(filename); 108 vol->fd = open(filename, oflags); 109 if (vol->fd < 0) { 110 free(vol->name); 111 free(vol); 112 err(1, "setup_volume: %s: Open failed", filename); 113 } 114 115 /* 116 * Read or initialize the volume header 117 */ 118 vol->ondisk = ondisk = malloc(HAMMER_BUFSIZE); 119 if (isnew > 0) { 120 bzero(ondisk, HAMMER_BUFSIZE); 121 } else { 122 n = pread(vol->fd, ondisk, HAMMER_BUFSIZE, 0); 123 if (n != HAMMER_BUFSIZE) { 124 err(1, "setup_volume: %s: Read failed at offset 0", 125 filename); 126 } 127 vol_no = ondisk->vol_no; 128 if (RootVolNo < 0) { 129 RootVolNo = ondisk->vol_rootvol; 130 } else if (RootVolNo != (int)ondisk->vol_rootvol) { 131 errx(1, "setup_volume: %s: root volume disagreement: " 132 "%d vs %d", 133 vol->name, RootVolNo, ondisk->vol_rootvol); 134 } 135 136 if (bcmp(&Hammer_FSType, &ondisk->vol_fstype, sizeof(Hammer_FSType)) != 0) { 137 errx(1, "setup_volume: %s: Header does not indicate " 138 "that this is a hammer volume", vol->name); 139 } 140 if (TAILQ_EMPTY(&VolList)) { 141 Hammer_FSId = vol->ondisk->vol_fsid; 142 } else if (bcmp(&Hammer_FSId, &ondisk->vol_fsid, sizeof(Hammer_FSId)) != 0) { 143 errx(1, "setup_volume: %s: FSId does match other " 144 "volumes!", vol->name); 145 } 146 } 147 vol->vol_no = vol_no; 148 149 if (isnew > 0) { 150 /*init_fifo_head(&ondisk->head, HAMMER_HEAD_TYPE_VOL);*/ 151 vol->cache.modified = 1; 152 } 153 154 /* 155 * Link the volume structure in 156 */ 157 TAILQ_FOREACH(scan, &VolList, entry) { 158 if (scan->vol_no == vol_no) { 159 errx(1, "setup_volume %s: Duplicate volume number %d " 160 "against %s", filename, vol_no, scan->name); 161 } 162 } 163 TAILQ_INSERT_TAIL(&VolList, vol, entry); 164 return(vol); 165 } 166 167 struct volume_info * 168 test_volume(int32_t vol_no) 169 { 170 struct volume_info *vol; 171 172 TAILQ_FOREACH(vol, &VolList, entry) { 173 if (vol->vol_no == vol_no) 174 break; 175 } 176 if (vol == NULL) 177 return(NULL); 178 ++vol->cache.refs; 179 /* not added to or removed from hammer cache */ 180 return(vol); 181 } 182 183 struct volume_info * 184 get_volume(int32_t vol_no) 185 { 186 struct volume_info *vol; 187 188 TAILQ_FOREACH(vol, &VolList, entry) { 189 if (vol->vol_no == vol_no) 190 break; 191 } 192 if (vol == NULL) 193 errx(1, "get_volume: Volume %d does not exist!", vol_no); 194 ++vol->cache.refs; 195 /* not added to or removed from hammer cache */ 196 return(vol); 197 } 198 199 void 200 rel_volume(struct volume_info *volume) 201 { 202 /* not added to or removed from hammer cache */ 203 --volume->cache.refs; 204 } 205 206 /* 207 * Acquire the specified buffer. 208 */ 209 struct buffer_info * 210 get_buffer(hammer_off_t buf_offset, int isnew) 211 { 212 void *ondisk; 213 struct buffer_info *buf; 214 struct volume_info *volume; 215 hammer_off_t orig_offset = buf_offset; 216 int vol_no; 217 int zone; 218 int hi, n; 219 int dora = 0; 220 221 zone = HAMMER_ZONE_DECODE(buf_offset); 222 if (zone > HAMMER_ZONE_RAW_BUFFER_INDEX) { 223 buf_offset = blockmap_lookup(buf_offset, NULL, NULL, NULL); 224 } 225 if (buf_offset == HAMMER_OFF_BAD) 226 return(NULL); 227 228 if (AssertOnFailure) { 229 assert((buf_offset & HAMMER_OFF_ZONE_MASK) == 230 HAMMER_ZONE_RAW_BUFFER); 231 } 232 vol_no = HAMMER_VOL_DECODE(buf_offset); 233 volume = test_volume(vol_no); 234 if (volume == NULL) { 235 if (AssertOnFailure) 236 errx(1, "get_buffer: Volume %d not found!", vol_no); 237 return(NULL); 238 } 239 240 buf_offset &= ~HAMMER_BUFMASK64; 241 242 hi = buffer_hash(buf_offset); 243 244 TAILQ_FOREACH(buf, &volume->buffer_lists[hi], entry) { 245 if (buf->buf_offset == buf_offset) 246 break; 247 } 248 if (buf == NULL) { 249 buf = malloc(sizeof(*buf)); 250 bzero(buf, sizeof(*buf)); 251 if (DebugOpt) { 252 fprintf(stderr, "get_buffer %016llx %016llx\n", 253 (long long)orig_offset, (long long)buf_offset); 254 } 255 buf->buf_offset = buf_offset; 256 buf->raw_offset = volume->ondisk->vol_buf_beg + 257 (buf_offset & HAMMER_OFF_SHORT_MASK); 258 buf->volume = volume; 259 TAILQ_INSERT_TAIL(&volume->buffer_lists[hi], buf, entry); 260 ++volume->cache.refs; 261 buf->cache.u.buffer = buf; 262 hammer_cache_add(&buf->cache, ISBUFFER); 263 dora = (isnew == 0); 264 if (isnew < 0) 265 buf->flags |= HAMMER_BUFINFO_READAHEAD; 266 } else { 267 if (isnew >= 0) { 268 buf->flags &= ~HAMMER_BUFINFO_READAHEAD; 269 hammer_cache_used(&buf->cache); 270 } 271 ++buf->use_count; 272 } 273 ++buf->cache.refs; 274 hammer_cache_flush(); 275 if ((ondisk = buf->ondisk) == NULL) { 276 buf->ondisk = ondisk = malloc(HAMMER_BUFSIZE); 277 if (isnew <= 0) { 278 n = pread(volume->fd, ondisk, HAMMER_BUFSIZE, 279 buf->raw_offset); 280 if (n != HAMMER_BUFSIZE) { 281 if (AssertOnFailure) 282 err(1, "get_buffer: %s:%016llx " 283 "Read failed at offset %016llx", 284 volume->name, 285 (long long)buf->buf_offset, 286 (long long)buf->raw_offset); 287 bzero(ondisk, HAMMER_BUFSIZE); 288 } 289 } 290 } 291 if (isnew > 0) { 292 bzero(ondisk, HAMMER_BUFSIZE); 293 buf->cache.modified = 1; 294 } 295 if (dora) 296 get_buffer_readahead(buf); 297 return(buf); 298 } 299 300 static void 301 get_buffer_readahead(struct buffer_info *base) 302 { 303 struct buffer_info *buf; 304 struct volume_info *vol; 305 hammer_off_t buf_offset; 306 int64_t raw_offset; 307 int ri = UseReadBehind; 308 int re = UseReadAhead; 309 int hi; 310 311 raw_offset = base->raw_offset + ri * HAMMER_BUFSIZE; 312 vol = base->volume; 313 314 while (ri < re) { 315 if (raw_offset >= vol->ondisk->vol_buf_end) 316 break; 317 if (raw_offset < vol->ondisk->vol_buf_beg) { 318 ++ri; 319 raw_offset += HAMMER_BUFSIZE; 320 continue; 321 } 322 buf_offset = HAMMER_VOL_ENCODE(vol->vol_no) | 323 HAMMER_ZONE_RAW_BUFFER | 324 (raw_offset - vol->ondisk->vol_buf_beg); 325 hi = buffer_hash(raw_offset); 326 TAILQ_FOREACH(buf, &vol->buffer_lists[hi], entry) { 327 if (buf->raw_offset == raw_offset) 328 break; 329 } 330 if (buf == NULL) { 331 buf = get_buffer(buf_offset, -1); 332 rel_buffer(buf); 333 } 334 ++ri; 335 raw_offset += HAMMER_BUFSIZE; 336 } 337 } 338 339 void 340 rel_buffer(struct buffer_info *buffer) 341 { 342 struct volume_info *volume; 343 int hi; 344 345 assert(buffer->cache.refs > 0); 346 if (--buffer->cache.refs == 0) { 347 if (buffer->cache.delete) { 348 hi = buffer_hash(buffer->buf_offset); 349 volume = buffer->volume; 350 if (buffer->cache.modified) 351 flush_buffer(buffer); 352 TAILQ_REMOVE(&volume->buffer_lists[hi], buffer, entry); 353 hammer_cache_del(&buffer->cache); 354 free(buffer->ondisk); 355 free(buffer); 356 rel_volume(volume); 357 } 358 } 359 } 360 361 /* 362 * Retrieve a pointer to a buffer data given a buffer offset. The underlying 363 * bufferp is freed if isnew or the offset is out of range of the cached data. 364 * If bufferp is freed a referenced buffer is loaded into it. 365 */ 366 void * 367 get_buffer_data(hammer_off_t buf_offset, struct buffer_info **bufferp, 368 int isnew) 369 { 370 if (*bufferp != NULL) { 371 if (isnew > 0 || 372 (((*bufferp)->buf_offset ^ buf_offset) & ~HAMMER_BUFMASK64)) { 373 rel_buffer(*bufferp); 374 *bufferp = NULL; 375 } 376 } 377 return(get_ondisk(buf_offset, bufferp, isnew)); 378 } 379 380 /* 381 * Retrieve a pointer to a B-Tree node given a cluster offset. The underlying 382 * bufferp is freed if non-NULL and a referenced buffer is loaded into it. 383 */ 384 hammer_node_ondisk_t 385 get_node(hammer_off_t node_offset, struct buffer_info **bufferp) 386 { 387 if (*bufferp != NULL) { 388 rel_buffer(*bufferp); 389 *bufferp = NULL; 390 } 391 return(get_ondisk(node_offset, bufferp, 0)); 392 } 393 394 /* 395 * Return a pointer to a buffer data given a buffer offset. 396 * If *bufferp is NULL acquire the buffer otherwise use that buffer. 397 */ 398 static __inline 399 void * 400 get_ondisk(hammer_off_t buf_offset, struct buffer_info **bufferp, 401 int isnew) 402 { 403 struct buffer_info *buffer; 404 405 buffer = *bufferp; 406 if (buffer == NULL) { 407 buffer = *bufferp = get_buffer(buf_offset, isnew); 408 if (buffer == NULL) 409 return(NULL); 410 } 411 412 return((char *)buffer->ondisk + 413 ((int32_t)buf_offset & HAMMER_BUFMASK)); 414 } 415 416 /* 417 * Allocate HAMMER elements - btree nodes, data storage, and record elements 418 * 419 * NOTE: hammer_alloc_fifo() initializes the fifo header for the returned 420 * item and zero's out the remainder, so don't bzero() it. 421 */ 422 void * 423 alloc_btree_element(hammer_off_t *offp) 424 { 425 struct buffer_info *buffer = NULL; 426 hammer_node_ondisk_t node; 427 428 node = alloc_blockmap(HAMMER_ZONE_BTREE_INDEX, sizeof(*node), 429 offp, &buffer); 430 bzero(node, sizeof(*node)); 431 /* XXX buffer not released, pointer remains valid */ 432 return(node); 433 } 434 435 void * 436 alloc_data_element(hammer_off_t *offp, int32_t data_len, 437 struct buffer_info **data_bufferp) 438 { 439 void *data; 440 441 if (data_len >= HAMMER_BUFSIZE) { 442 assert(data_len <= HAMMER_BUFSIZE); /* just one buffer */ 443 data = alloc_blockmap(HAMMER_ZONE_LARGE_DATA_INDEX, data_len, 444 offp, data_bufferp); 445 bzero(data, data_len); 446 } else if (data_len) { 447 data = alloc_blockmap(HAMMER_ZONE_SMALL_DATA_INDEX, data_len, 448 offp, data_bufferp); 449 bzero(data, data_len); 450 } else { 451 data = NULL; 452 } 453 return (data); 454 } 455 456 /* 457 * Format a new freemap. Set all layer1 entries to UNAVAIL. The initialize 458 * code will load each volume's freemap. 459 */ 460 void 461 format_freemap(struct volume_info *root_vol, hammer_blockmap_t blockmap) 462 { 463 struct buffer_info *buffer = NULL; 464 hammer_off_t layer1_offset; 465 struct hammer_blockmap_layer1 *layer1; 466 int i, isnew; 467 468 layer1_offset = alloc_bigblock(root_vol, HAMMER_ZONE_FREEMAP_INDEX); 469 for (i = 0; i < (int)HAMMER_BLOCKMAP_RADIX1; ++i) { 470 isnew = ((i % HAMMER_BLOCKMAP_RADIX1_PERBUFFER) == 0); 471 layer1 = get_buffer_data(layer1_offset + i * sizeof(*layer1), 472 &buffer, isnew); 473 bzero(layer1, sizeof(*layer1)); 474 layer1->phys_offset = HAMMER_BLOCKMAP_UNAVAIL; 475 layer1->blocks_free = 0; 476 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE); 477 } 478 rel_buffer(buffer); 479 480 blockmap = &root_vol->ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 481 blockmap->phys_offset = layer1_offset; 482 blockmap->alloc_offset = HAMMER_ENCODE_RAW_BUFFER(255, -1); 483 blockmap->next_offset = HAMMER_ENCODE_RAW_BUFFER(0, 0); 484 blockmap->reserved01 = 0; 485 blockmap->entry_crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE); 486 root_vol->cache.modified = 1; 487 } 488 489 /* 490 * Load the volume's remaining free space into the freemap. 491 * 492 * Returns the number of bigblocks available. 493 */ 494 int64_t 495 initialize_freemap(struct volume_info *vol) 496 { 497 struct volume_info *root_vol; 498 struct buffer_info *buffer1 = NULL; 499 struct buffer_info *buffer2 = NULL; 500 struct hammer_blockmap_layer1 *layer1; 501 struct hammer_blockmap_layer2 *layer2; 502 hammer_off_t layer1_base; 503 hammer_off_t layer1_offset; 504 hammer_off_t layer2_offset; 505 hammer_off_t phys_offset; 506 hammer_off_t aligned_vol_free_end; 507 int64_t count = 0; 508 int modified1 = 0; 509 510 root_vol = get_volume(RootVolNo); 511 aligned_vol_free_end = (vol->vol_free_end + HAMMER_BLOCKMAP_LAYER2_MASK) 512 & ~HAMMER_BLOCKMAP_LAYER2_MASK; 513 514 printf("initialize freemap volume %d\n", vol->vol_no); 515 516 /* 517 * Initialize the freemap. First preallocate the bigblocks required 518 * to implement layer2. This preallocation is a bootstrap allocation 519 * using blocks from the target volume. 520 */ 521 layer1_base = root_vol->ondisk->vol0_blockmap[ 522 HAMMER_ZONE_FREEMAP_INDEX].phys_offset; 523 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 524 phys_offset < aligned_vol_free_end; 525 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 526 layer1_offset = layer1_base + 527 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 528 layer1 = get_buffer_data(layer1_offset, &buffer1, 0); 529 if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) { 530 layer1->phys_offset = alloc_bigblock(vol, 531 HAMMER_ZONE_FREEMAP_INDEX); 532 layer1->blocks_free = 0; 533 buffer1->cache.modified = 1; 534 layer1->layer1_crc = crc32(layer1, 535 HAMMER_LAYER1_CRCSIZE); 536 } 537 } 538 539 /* 540 * Now fill everything in. 541 */ 542 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 543 phys_offset < aligned_vol_free_end; 544 phys_offset += HAMMER_BIGBLOCK_SIZE) { 545 modified1 = 0; 546 layer1_offset = layer1_base + 547 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 548 layer1 = get_buffer_data(layer1_offset, &buffer1, 0); 549 550 assert(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); 551 layer2_offset = layer1->phys_offset + 552 HAMMER_BLOCKMAP_LAYER2_OFFSET(phys_offset); 553 554 layer2 = get_buffer_data(layer2_offset, &buffer2, 0); 555 bzero(layer2, sizeof(*layer2)); 556 if (phys_offset < vol->vol_free_off) { 557 /* 558 * Fixups XXX - bigblocks already allocated as part 559 * of the freemap bootstrap. 560 */ 561 if (layer2->zone == 0) { 562 layer2->zone = HAMMER_ZONE_FREEMAP_INDEX; 563 layer2->append_off = HAMMER_BIGBLOCK_SIZE; 564 layer2->bytes_free = 0; 565 } 566 } else if (phys_offset < vol->vol_free_end) { 567 ++layer1->blocks_free; 568 buffer1->cache.modified = 1; 569 layer2->zone = 0; 570 layer2->append_off = 0; 571 layer2->bytes_free = HAMMER_BIGBLOCK_SIZE; 572 ++count; 573 modified1 = 1; 574 } else { 575 layer2->zone = HAMMER_ZONE_UNAVAIL_INDEX; 576 layer2->append_off = HAMMER_BIGBLOCK_SIZE; 577 layer2->bytes_free = 0; 578 } 579 layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE); 580 buffer2->cache.modified = 1; 581 582 /* 583 * Finish-up layer 1 584 */ 585 if (modified1) { 586 layer1->layer1_crc = crc32(layer1, 587 HAMMER_LAYER1_CRCSIZE); 588 buffer1->cache.modified = 1; 589 } 590 } 591 rel_buffer(buffer1); 592 rel_buffer(buffer2); 593 rel_volume(root_vol); 594 return(count); 595 } 596 597 /* 598 * Allocate big-blocks using our poor-man's volume->vol_free_off. 599 * 600 * If the zone is HAMMER_ZONE_FREEMAP_INDEX we are bootstrapping the freemap 601 * itself and cannot update it yet. 602 */ 603 hammer_off_t 604 alloc_bigblock(struct volume_info *volume, int zone) 605 { 606 struct buffer_info *buffer = NULL; 607 struct volume_info *root_vol; 608 hammer_off_t result_offset; 609 hammer_off_t layer_offset; 610 struct hammer_blockmap_layer1 *layer1; 611 struct hammer_blockmap_layer2 *layer2; 612 int didget; 613 614 if (volume == NULL) { 615 volume = get_volume(RootVolNo); 616 didget = 1; 617 } else { 618 didget = 0; 619 } 620 result_offset = volume->vol_free_off; 621 if (result_offset >= volume->vol_free_end) 622 panic("alloc_bigblock: Ran out of room, filesystem too small"); 623 volume->vol_free_off += HAMMER_BIGBLOCK_SIZE; 624 625 /* 626 * Update the freemap. 627 */ 628 if (zone != HAMMER_ZONE_FREEMAP_INDEX) { 629 root_vol = get_volume(RootVolNo); 630 layer_offset = root_vol->ondisk->vol0_blockmap[ 631 HAMMER_ZONE_FREEMAP_INDEX].phys_offset; 632 layer_offset += HAMMER_BLOCKMAP_LAYER1_OFFSET(result_offset); 633 layer1 = get_buffer_data(layer_offset, &buffer, 0); 634 assert(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); 635 --layer1->blocks_free; 636 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE); 637 buffer->cache.modified = 1; 638 layer_offset = layer1->phys_offset + 639 HAMMER_BLOCKMAP_LAYER2_OFFSET(result_offset); 640 layer2 = get_buffer_data(layer_offset, &buffer, 0); 641 assert(layer2->zone == 0); 642 layer2->zone = zone; 643 layer2->append_off = HAMMER_BIGBLOCK_SIZE; 644 layer2->bytes_free = 0; 645 layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE); 646 buffer->cache.modified = 1; 647 648 --root_vol->ondisk->vol0_stat_freebigblocks; 649 root_vol->cache.modified = 1; 650 651 rel_buffer(buffer); 652 rel_volume(root_vol); 653 } 654 655 if (didget) 656 rel_volume(volume); 657 return(result_offset); 658 } 659 660 /* 661 * Format the undo-map for the root volume. 662 */ 663 void 664 format_undomap(hammer_volume_ondisk_t ondisk) 665 { 666 const int undo_zone = HAMMER_ZONE_UNDO_INDEX; 667 hammer_off_t undo_limit; 668 hammer_blockmap_t blockmap; 669 struct buffer_info *buffer = NULL; 670 hammer_off_t scan; 671 int n; 672 int limit_index; 673 u_int32_t seqno; 674 675 /* 676 * Size the undo buffer in multiples of HAMMER_BIGBLOCK_SIZE, 677 * up to HAMMER_UNDO_LAYER2 big blocks. Size to approximately 678 * 0.1% of the disk. 679 * 680 * The minimum UNDO fifo size is 500MB, or approximately 1% of 681 * the recommended 50G disk. 682 * 683 * Changing this minimum is rather dangerous as complex filesystem 684 * operations can cause the UNDO FIFO to fill up otherwise. 685 */ 686 undo_limit = UndoBufferSize; 687 if (undo_limit == 0) { 688 undo_limit = (ondisk->vol_buf_end - ondisk->vol_buf_beg) / 1000; 689 if (undo_limit < 500*1024*1024) 690 undo_limit = 500*1024*1024; 691 } 692 undo_limit = (undo_limit + HAMMER_BIGBLOCK_MASK64) & 693 ~HAMMER_BIGBLOCK_MASK64; 694 if (undo_limit < HAMMER_BIGBLOCK_SIZE) 695 undo_limit = HAMMER_BIGBLOCK_SIZE; 696 if (undo_limit > HAMMER_BIGBLOCK_SIZE * HAMMER_UNDO_LAYER2) 697 undo_limit = HAMMER_BIGBLOCK_SIZE * HAMMER_UNDO_LAYER2; 698 UndoBufferSize = undo_limit; 699 700 blockmap = &ondisk->vol0_blockmap[undo_zone]; 701 bzero(blockmap, sizeof(*blockmap)); 702 blockmap->phys_offset = HAMMER_BLOCKMAP_UNAVAIL; 703 blockmap->first_offset = HAMMER_ZONE_ENCODE(undo_zone, 0); 704 blockmap->next_offset = blockmap->first_offset; 705 blockmap->alloc_offset = HAMMER_ZONE_ENCODE(undo_zone, undo_limit); 706 blockmap->entry_crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE); 707 708 n = 0; 709 scan = blockmap->next_offset; 710 limit_index = undo_limit / HAMMER_BIGBLOCK_SIZE; 711 712 assert(limit_index <= HAMMER_UNDO_LAYER2); 713 714 for (n = 0; n < limit_index; ++n) { 715 ondisk->vol0_undo_array[n] = alloc_bigblock(NULL, 716 HAMMER_ZONE_UNDO_INDEX); 717 scan += HAMMER_BIGBLOCK_SIZE; 718 } 719 while (n < HAMMER_UNDO_LAYER2) { 720 ondisk->vol0_undo_array[n] = HAMMER_BLOCKMAP_UNAVAIL; 721 ++n; 722 } 723 724 /* 725 * Pre-initialize the UNDO blocks (HAMMER version 4+) 726 */ 727 printf("initializing the undo map (%jd MB)\n", 728 (intmax_t)(blockmap->alloc_offset & HAMMER_OFF_LONG_MASK) / 729 (1024 * 1024)); 730 731 scan = blockmap->first_offset; 732 seqno = 0; 733 734 while (scan < blockmap->alloc_offset) { 735 hammer_fifo_head_t head; 736 hammer_fifo_tail_t tail; 737 int isnew; 738 int bytes = HAMMER_UNDO_ALIGN; 739 740 isnew = ((scan & HAMMER_BUFMASK64) == 0); 741 head = get_buffer_data(scan, &buffer, isnew); 742 buffer->cache.modified = 1; 743 tail = (void *)((char *)head + bytes - sizeof(*tail)); 744 745 bzero(head, bytes); 746 head->hdr_signature = HAMMER_HEAD_SIGNATURE; 747 head->hdr_type = HAMMER_HEAD_TYPE_DUMMY; 748 head->hdr_size = bytes; 749 head->hdr_seq = seqno++; 750 751 tail->tail_signature = HAMMER_TAIL_SIGNATURE; 752 tail->tail_type = HAMMER_HEAD_TYPE_DUMMY; 753 tail->tail_size = bytes; 754 755 head->hdr_crc = crc32(head, HAMMER_FIFO_HEAD_CRCOFF) ^ 756 crc32(head + 1, bytes - sizeof(*head)); 757 758 scan += bytes; 759 } 760 if (buffer) 761 rel_buffer(buffer); 762 } 763 764 /* 765 * Format a new blockmap. This is mostly a degenerate case because 766 * all allocations are now actually done from the freemap. 767 */ 768 void 769 format_blockmap(hammer_blockmap_t blockmap, hammer_off_t zone_base) 770 { 771 blockmap->phys_offset = 0; 772 blockmap->alloc_offset = zone_base | HAMMER_VOL_ENCODE(255) | 773 HAMMER_SHORT_OFF_ENCODE(-1); 774 blockmap->first_offset = zone_base; 775 blockmap->next_offset = zone_base; 776 blockmap->entry_crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE); 777 } 778 779 /* 780 * Allocate a chunk of data out of a blockmap. This is a simplified 781 * version which uses next_offset as a simple allocation iterator. 782 */ 783 static 784 void * 785 alloc_blockmap(int zone, int bytes, hammer_off_t *result_offp, 786 struct buffer_info **bufferp) 787 { 788 struct buffer_info *buffer1 = NULL; 789 struct buffer_info *buffer2 = NULL; 790 struct volume_info *volume; 791 hammer_blockmap_t blockmap; 792 hammer_blockmap_t freemap; 793 struct hammer_blockmap_layer1 *layer1; 794 struct hammer_blockmap_layer2 *layer2; 795 hammer_off_t layer1_offset; 796 hammer_off_t layer2_offset; 797 hammer_off_t zone2_offset; 798 void *ptr; 799 800 volume = get_volume(RootVolNo); 801 802 blockmap = &volume->ondisk->vol0_blockmap[zone]; 803 freemap = &volume->ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 804 805 /* 806 * Alignment and buffer-boundary issues. If the allocation would 807 * cross a buffer boundary we have to skip to the next buffer. 808 */ 809 bytes = (bytes + 15) & ~15; 810 811 again: 812 if ((blockmap->next_offset ^ (blockmap->next_offset + bytes - 1)) & 813 ~HAMMER_BUFMASK64) { 814 volume->cache.modified = 1; 815 blockmap->next_offset = (blockmap->next_offset + bytes) & 816 ~HAMMER_BUFMASK64; 817 } 818 819 /* 820 * Dive layer 1. For now we can't allocate data outside of volume 0. 821 */ 822 layer1_offset = freemap->phys_offset + 823 HAMMER_BLOCKMAP_LAYER1_OFFSET(blockmap->next_offset); 824 825 layer1 = get_buffer_data(layer1_offset, &buffer1, 0); 826 827 if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) { 828 fprintf(stderr, "alloc_blockmap: ran out of space!\n"); 829 exit(1); 830 } 831 832 /* 833 * Dive layer 2 834 */ 835 layer2_offset = layer1->phys_offset + 836 HAMMER_BLOCKMAP_LAYER2_OFFSET(blockmap->next_offset); 837 838 layer2 = get_buffer_data(layer2_offset, &buffer2, 0); 839 840 if (layer2->zone == HAMMER_ZONE_UNAVAIL_INDEX) { 841 fprintf(stderr, "alloc_blockmap: ran out of space!\n"); 842 exit(1); 843 } 844 845 /* 846 * If we are entering a new bigblock assign ownership to our 847 * zone. If the bigblock is owned by another zone skip it. 848 */ 849 if (layer2->zone == 0) { 850 --layer1->blocks_free; 851 layer2->zone = zone; 852 assert(layer2->bytes_free == HAMMER_BIGBLOCK_SIZE); 853 assert(layer2->append_off == 0); 854 } 855 if (layer2->zone != zone) { 856 blockmap->next_offset = (blockmap->next_offset + HAMMER_BIGBLOCK_SIZE) & 857 ~HAMMER_BIGBLOCK_MASK64; 858 goto again; 859 } 860 861 buffer1->cache.modified = 1; 862 buffer2->cache.modified = 1; 863 volume->cache.modified = 1; 864 assert(layer2->append_off == 865 (blockmap->next_offset & HAMMER_BIGBLOCK_MASK)); 866 layer2->bytes_free -= bytes; 867 *result_offp = blockmap->next_offset; 868 blockmap->next_offset += bytes; 869 layer2->append_off = (int)blockmap->next_offset & 870 HAMMER_BIGBLOCK_MASK; 871 872 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE); 873 layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE); 874 875 zone2_offset = (*result_offp & ~HAMMER_OFF_ZONE_MASK) | 876 HAMMER_ZONE_ENCODE(zone, 0); 877 878 ptr = get_buffer_data(zone2_offset, bufferp, 0); 879 (*bufferp)->cache.modified = 1; 880 881 if (buffer1) 882 rel_buffer(buffer1); 883 if (buffer2) 884 rel_buffer(buffer2); 885 886 rel_volume(volume); 887 return(ptr); 888 } 889 890 /* 891 * Flush various tracking structures to disk 892 */ 893 894 /* 895 * Flush various tracking structures to disk 896 */ 897 void 898 flush_all_volumes(void) 899 { 900 struct volume_info *vol; 901 902 TAILQ_FOREACH(vol, &VolList, entry) 903 flush_volume(vol); 904 } 905 906 void 907 flush_volume(struct volume_info *volume) 908 { 909 struct buffer_info *buffer; 910 int i; 911 912 for (i = 0; i < HAMMER_BUFLISTS; ++i) { 913 TAILQ_FOREACH(buffer, &volume->buffer_lists[i], entry) 914 flush_buffer(buffer); 915 } 916 writehammerbuf(volume, volume->ondisk, 0); 917 volume->cache.modified = 0; 918 } 919 920 void 921 flush_buffer(struct buffer_info *buffer) 922 { 923 writehammerbuf(buffer->volume, buffer->ondisk, buffer->raw_offset); 924 buffer->cache.modified = 0; 925 } 926 927 #if 0 928 /* 929 * Generic buffer initialization 930 */ 931 static void 932 init_fifo_head(hammer_fifo_head_t head, u_int16_t hdr_type) 933 { 934 head->hdr_signature = HAMMER_HEAD_SIGNATURE; 935 head->hdr_type = hdr_type; 936 head->hdr_size = 0; 937 head->hdr_crc = 0; 938 head->hdr_seq = 0; 939 } 940 941 #endif 942 943 #if 0 944 /* 945 * Core I/O operations 946 */ 947 static void 948 readhammerbuf(struct volume_info *vol, void *data, int64_t offset) 949 { 950 ssize_t n; 951 952 n = pread(vol->fd, data, HAMMER_BUFSIZE, offset); 953 if (n != HAMMER_BUFSIZE) 954 err(1, "Read volume %d (%s)", vol->vol_no, vol->name); 955 } 956 957 #endif 958 959 static void 960 writehammerbuf(struct volume_info *vol, const void *data, int64_t offset) 961 { 962 ssize_t n; 963 964 n = pwrite(vol->fd, data, HAMMER_BUFSIZE, offset); 965 if (n != HAMMER_BUFSIZE) 966 err(1, "Write volume %d (%s)", vol->vol_no, vol->name); 967 } 968 969 void 970 panic(const char *ctl, ...) 971 { 972 va_list va; 973 974 va_start(va, ctl); 975 vfprintf(stderr, ctl, va); 976 va_end(va); 977 fprintf(stderr, "\n"); 978 exit(1); 979 } 980 981