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 * $DragonFly: src/sbin/hammer/ondisk.c,v 1.25 2008/08/21 23:28:43 thomas Exp $ 35 */ 36 37 #include <sys/types.h> 38 #include <assert.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <stdarg.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <stddef.h> 45 #include <err.h> 46 #include <fcntl.h> 47 #include "hammer_util.h" 48 49 static void *alloc_blockmap(int zone, int bytes, hammer_off_t *result_offp, 50 struct buffer_info **bufferp); 51 static hammer_off_t alloc_bigblock(struct volume_info *volume, int zone); 52 static void get_buffer_readahead(struct buffer_info *base); 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 Read failed at " 283 "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 void * 362 get_buffer_data(hammer_off_t buf_offset, struct buffer_info **bufferp, 363 int isnew) 364 { 365 struct buffer_info *buffer; 366 367 if ((buffer = *bufferp) != NULL) { 368 if (isnew > 0 || 369 ((buffer->buf_offset ^ buf_offset) & ~HAMMER_BUFMASK64)) { 370 rel_buffer(buffer); 371 buffer = *bufferp = NULL; 372 } 373 } 374 if (buffer == NULL) 375 buffer = *bufferp = get_buffer(buf_offset, isnew); 376 if (buffer == NULL) 377 return (NULL); 378 return((char *)buffer->ondisk + ((int32_t)buf_offset & HAMMER_BUFMASK)); 379 } 380 381 /* 382 * Retrieve a pointer to a B-Tree node given a cluster offset. The underlying 383 * bufp is freed if non-NULL and a referenced buffer is loaded into it. 384 */ 385 hammer_node_ondisk_t 386 get_node(hammer_off_t node_offset, struct buffer_info **bufp) 387 { 388 struct buffer_info *buf; 389 390 if (*bufp) 391 rel_buffer(*bufp); 392 *bufp = buf = get_buffer(node_offset, 0); 393 if (buf) { 394 return((void *)((char *)buf->ondisk + 395 (int32_t)(node_offset & HAMMER_BUFMASK))); 396 } else { 397 return(NULL); 398 } 399 } 400 401 /* 402 * Allocate HAMMER elements - btree nodes, data storage, and record elements 403 * 404 * NOTE: hammer_alloc_fifo() initializes the fifo header for the returned 405 * item and zero's out the remainder, so don't bzero() it. 406 */ 407 void * 408 alloc_btree_element(hammer_off_t *offp) 409 { 410 struct buffer_info *buffer = NULL; 411 hammer_node_ondisk_t node; 412 413 node = alloc_blockmap(HAMMER_ZONE_BTREE_INDEX, sizeof(*node), 414 offp, &buffer); 415 bzero(node, sizeof(*node)); 416 /* XXX buffer not released, pointer remains valid */ 417 return(node); 418 } 419 420 void * 421 alloc_data_element(hammer_off_t *offp, int32_t data_len, 422 struct buffer_info **data_bufferp) 423 { 424 void *data; 425 426 if (data_len >= HAMMER_BUFSIZE) { 427 assert(data_len <= HAMMER_BUFSIZE); /* just one buffer */ 428 data = alloc_blockmap(HAMMER_ZONE_LARGE_DATA_INDEX, data_len, 429 offp, data_bufferp); 430 bzero(data, data_len); 431 } else if (data_len) { 432 data = alloc_blockmap(HAMMER_ZONE_SMALL_DATA_INDEX, data_len, 433 offp, data_bufferp); 434 bzero(data, data_len); 435 } else { 436 data = NULL; 437 } 438 return (data); 439 } 440 441 /* 442 * Format a new freemap. Set all layer1 entries to UNAVAIL. The initialize 443 * code will load each volume's freemap. 444 */ 445 void 446 format_freemap(struct volume_info *root_vol, hammer_blockmap_t blockmap) 447 { 448 struct buffer_info *buffer = NULL; 449 hammer_off_t layer1_offset; 450 struct hammer_blockmap_layer1 *layer1; 451 int i, isnew; 452 453 layer1_offset = alloc_bigblock(root_vol, HAMMER_ZONE_FREEMAP_INDEX); 454 for (i = 0; i < (int)HAMMER_BLOCKMAP_RADIX1; ++i) { 455 isnew = ((i % HAMMER_BLOCKMAP_RADIX1_PERBUFFER) == 0); 456 layer1 = get_buffer_data(layer1_offset + i * sizeof(*layer1), 457 &buffer, isnew); 458 bzero(layer1, sizeof(*layer1)); 459 layer1->phys_offset = HAMMER_BLOCKMAP_UNAVAIL; 460 layer1->blocks_free = 0; 461 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE); 462 } 463 rel_buffer(buffer); 464 465 blockmap = &root_vol->ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 466 blockmap->phys_offset = layer1_offset; 467 blockmap->alloc_offset = HAMMER_ENCODE_RAW_BUFFER(255, -1); 468 blockmap->next_offset = HAMMER_ENCODE_RAW_BUFFER(0, 0); 469 blockmap->reserved01 = 0; 470 blockmap->entry_crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE); 471 root_vol->cache.modified = 1; 472 } 473 474 /* 475 * Load the volume's remaining free space into the freemap. 476 * 477 * Returns the number of bigblocks available. 478 */ 479 int64_t 480 initialize_freemap(struct volume_info *vol) 481 { 482 struct volume_info *root_vol; 483 struct buffer_info *buffer1 = NULL; 484 struct buffer_info *buffer2 = NULL; 485 struct hammer_blockmap_layer1 *layer1; 486 struct hammer_blockmap_layer2 *layer2; 487 hammer_off_t layer1_base; 488 hammer_off_t layer1_offset; 489 hammer_off_t layer2_offset; 490 hammer_off_t phys_offset; 491 hammer_off_t aligned_vol_free_end; 492 int64_t count = 0; 493 int modified1 = 0; 494 495 root_vol = get_volume(RootVolNo); 496 aligned_vol_free_end = (vol->vol_free_end + HAMMER_BLOCKMAP_LAYER2_MASK) 497 & ~HAMMER_BLOCKMAP_LAYER2_MASK; 498 499 printf("initialize freemap volume %d\n", vol->vol_no); 500 501 /* 502 * Initialize the freemap. First preallocate the bigblocks required 503 * to implement layer2. This preallocation is a bootstrap allocation 504 * using blocks from the target volume. 505 */ 506 layer1_base = root_vol->ondisk->vol0_blockmap[ 507 HAMMER_ZONE_FREEMAP_INDEX].phys_offset; 508 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 509 phys_offset < aligned_vol_free_end; 510 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 511 layer1_offset = layer1_base + 512 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 513 layer1 = get_buffer_data(layer1_offset, &buffer1, 0); 514 if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) { 515 layer1->phys_offset = alloc_bigblock(vol, 516 HAMMER_ZONE_FREEMAP_INDEX); 517 layer1->blocks_free = 0; 518 buffer1->cache.modified = 1; 519 layer1->layer1_crc = crc32(layer1, 520 HAMMER_LAYER1_CRCSIZE); 521 } 522 } 523 524 /* 525 * Now fill everything in. 526 */ 527 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 528 phys_offset < aligned_vol_free_end; 529 phys_offset += HAMMER_LARGEBLOCK_SIZE) { 530 modified1 = 0; 531 layer1_offset = layer1_base + 532 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 533 layer1 = get_buffer_data(layer1_offset, &buffer1, 0); 534 535 assert(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); 536 layer2_offset = layer1->phys_offset + 537 HAMMER_BLOCKMAP_LAYER2_OFFSET(phys_offset); 538 539 layer2 = get_buffer_data(layer2_offset, &buffer2, 0); 540 bzero(layer2, sizeof(*layer2)); 541 if (phys_offset < vol->vol_free_off) { 542 /* 543 * Fixups XXX - bigblocks already allocated as part 544 * of the freemap bootstrap. 545 */ 546 if (layer2->zone == 0) { 547 layer2->zone = HAMMER_ZONE_FREEMAP_INDEX; 548 layer2->append_off = HAMMER_LARGEBLOCK_SIZE; 549 layer2->bytes_free = 0; 550 } 551 } else if (phys_offset < vol->vol_free_end) { 552 ++layer1->blocks_free; 553 buffer1->cache.modified = 1; 554 layer2->zone = 0; 555 layer2->append_off = 0; 556 layer2->bytes_free = HAMMER_LARGEBLOCK_SIZE; 557 ++count; 558 modified1 = 1; 559 } else { 560 layer2->zone = HAMMER_ZONE_UNAVAIL_INDEX; 561 layer2->append_off = HAMMER_LARGEBLOCK_SIZE; 562 layer2->bytes_free = 0; 563 } 564 layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE); 565 buffer2->cache.modified = 1; 566 567 /* 568 * Finish-up layer 1 569 */ 570 if (modified1) { 571 layer1->layer1_crc = crc32(layer1, 572 HAMMER_LAYER1_CRCSIZE); 573 buffer1->cache.modified = 1; 574 } 575 } 576 rel_buffer(buffer1); 577 rel_buffer(buffer2); 578 rel_volume(root_vol); 579 return(count); 580 } 581 582 /* 583 * Allocate big-blocks using our poor-man's volume->vol_free_off. 584 * 585 * If the zone is HAMMER_ZONE_FREEMAP_INDEX we are bootstrapping the freemap 586 * itself and cannot update it yet. 587 */ 588 hammer_off_t 589 alloc_bigblock(struct volume_info *volume, int zone) 590 { 591 struct buffer_info *buffer = NULL; 592 struct volume_info *root_vol; 593 hammer_off_t result_offset; 594 hammer_off_t layer_offset; 595 struct hammer_blockmap_layer1 *layer1; 596 struct hammer_blockmap_layer2 *layer2; 597 int didget; 598 599 if (volume == NULL) { 600 volume = get_volume(RootVolNo); 601 didget = 1; 602 } else { 603 didget = 0; 604 } 605 result_offset = volume->vol_free_off; 606 if (result_offset >= volume->vol_free_end) 607 panic("alloc_bigblock: Ran out of room, filesystem too small"); 608 volume->vol_free_off += HAMMER_LARGEBLOCK_SIZE; 609 610 /* 611 * Update the freemap. 612 */ 613 if (zone != HAMMER_ZONE_FREEMAP_INDEX) { 614 root_vol = get_volume(RootVolNo); 615 layer_offset = root_vol->ondisk->vol0_blockmap[ 616 HAMMER_ZONE_FREEMAP_INDEX].phys_offset; 617 layer_offset += HAMMER_BLOCKMAP_LAYER1_OFFSET(result_offset); 618 layer1 = get_buffer_data(layer_offset, &buffer, 0); 619 assert(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); 620 --layer1->blocks_free; 621 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE); 622 buffer->cache.modified = 1; 623 layer_offset = layer1->phys_offset + 624 HAMMER_BLOCKMAP_LAYER2_OFFSET(result_offset); 625 layer2 = get_buffer_data(layer_offset, &buffer, 0); 626 assert(layer2->zone == 0); 627 layer2->zone = zone; 628 layer2->append_off = HAMMER_LARGEBLOCK_SIZE; 629 layer2->bytes_free = 0; 630 layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE); 631 buffer->cache.modified = 1; 632 633 --root_vol->ondisk->vol0_stat_freebigblocks; 634 root_vol->cache.modified = 1; 635 636 rel_buffer(buffer); 637 rel_volume(root_vol); 638 } 639 640 if (didget) 641 rel_volume(volume); 642 return(result_offset); 643 } 644 645 /* 646 * Format the undo-map for the root volume. 647 */ 648 void 649 format_undomap(hammer_volume_ondisk_t ondisk) 650 { 651 const int undo_zone = HAMMER_ZONE_UNDO_INDEX; 652 hammer_off_t undo_limit; 653 hammer_blockmap_t blockmap; 654 struct buffer_info *buffer = NULL; 655 hammer_off_t scan; 656 int n; 657 int limit_index; 658 u_int32_t seqno; 659 660 /* 661 * Size the undo buffer in multiples of HAMMER_LARGEBLOCK_SIZE, 662 * up to HAMMER_UNDO_LAYER2 large blocks. Size to approximately 663 * 0.1% of the disk. 664 * 665 * The minimum UNDO fifo size is 500MB, or approximately 1% of 666 * the recommended 50G disk. 667 * 668 * Changing this minimum is rather dangerous as complex filesystem 669 * operations can cause the UNDO FIFO to fill up otherwise. 670 */ 671 undo_limit = UndoBufferSize; 672 if (undo_limit == 0) { 673 undo_limit = (ondisk->vol_buf_end - ondisk->vol_buf_beg) / 1000; 674 if (undo_limit < 500*1024*1024) 675 undo_limit = 500*1024*1024; 676 } 677 undo_limit = (undo_limit + HAMMER_LARGEBLOCK_MASK64) & 678 ~HAMMER_LARGEBLOCK_MASK64; 679 if (undo_limit < HAMMER_LARGEBLOCK_SIZE) 680 undo_limit = HAMMER_LARGEBLOCK_SIZE; 681 if (undo_limit > HAMMER_LARGEBLOCK_SIZE * HAMMER_UNDO_LAYER2) 682 undo_limit = HAMMER_LARGEBLOCK_SIZE * HAMMER_UNDO_LAYER2; 683 UndoBufferSize = undo_limit; 684 685 blockmap = &ondisk->vol0_blockmap[undo_zone]; 686 bzero(blockmap, sizeof(*blockmap)); 687 blockmap->phys_offset = HAMMER_BLOCKMAP_UNAVAIL; 688 blockmap->first_offset = HAMMER_ZONE_ENCODE(undo_zone, 0); 689 blockmap->next_offset = blockmap->first_offset; 690 blockmap->alloc_offset = HAMMER_ZONE_ENCODE(undo_zone, undo_limit); 691 blockmap->entry_crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE); 692 693 n = 0; 694 scan = blockmap->next_offset; 695 limit_index = undo_limit / HAMMER_LARGEBLOCK_SIZE; 696 697 assert(limit_index <= HAMMER_UNDO_LAYER2); 698 699 for (n = 0; n < limit_index; ++n) { 700 ondisk->vol0_undo_array[n] = alloc_bigblock(NULL, 701 HAMMER_ZONE_UNDO_INDEX); 702 scan += HAMMER_LARGEBLOCK_SIZE; 703 } 704 while (n < HAMMER_UNDO_LAYER2) { 705 ondisk->vol0_undo_array[n] = HAMMER_BLOCKMAP_UNAVAIL; 706 ++n; 707 } 708 709 /* 710 * Pre-initialize the UNDO blocks (HAMMER version 4+) 711 */ 712 printf("initializing the undo map (%jd MB)\n", 713 (intmax_t)(blockmap->alloc_offset & HAMMER_OFF_LONG_MASK) / 714 (1024 * 1024)); 715 716 scan = blockmap->first_offset; 717 seqno = 0; 718 719 while (scan < blockmap->alloc_offset) { 720 hammer_fifo_head_t head; 721 hammer_fifo_tail_t tail; 722 int isnew; 723 int bytes = HAMMER_UNDO_ALIGN; 724 725 isnew = ((scan & HAMMER_BUFMASK64) == 0); 726 head = get_buffer_data(scan, &buffer, isnew); 727 buffer->cache.modified = 1; 728 tail = (void *)((char *)head + bytes - sizeof(*tail)); 729 730 bzero(head, bytes); 731 head->hdr_signature = HAMMER_HEAD_SIGNATURE; 732 head->hdr_type = HAMMER_HEAD_TYPE_DUMMY; 733 head->hdr_size = bytes; 734 head->hdr_seq = seqno++; 735 736 tail->tail_signature = HAMMER_TAIL_SIGNATURE; 737 tail->tail_type = HAMMER_HEAD_TYPE_DUMMY; 738 tail->tail_size = bytes; 739 740 head->hdr_crc = crc32(head, HAMMER_FIFO_HEAD_CRCOFF) ^ 741 crc32(head + 1, bytes - sizeof(*head)); 742 743 scan += bytes; 744 } 745 if (buffer) 746 rel_buffer(buffer); 747 } 748 749 /* 750 * Format a new blockmap. This is mostly a degenerate case because 751 * all allocations are now actually done from the freemap. 752 */ 753 void 754 format_blockmap(hammer_blockmap_t blockmap, hammer_off_t zone_base) 755 { 756 blockmap->phys_offset = 0; 757 blockmap->alloc_offset = zone_base | HAMMER_VOL_ENCODE(255) | 758 HAMMER_SHORT_OFF_ENCODE(-1); 759 blockmap->first_offset = zone_base; 760 blockmap->next_offset = zone_base; 761 blockmap->entry_crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE); 762 } 763 764 /* 765 * Allocate a chunk of data out of a blockmap. This is a simplified 766 * version which uses next_offset as a simple allocation iterator. 767 */ 768 static 769 void * 770 alloc_blockmap(int zone, int bytes, hammer_off_t *result_offp, 771 struct buffer_info **bufferp) 772 { 773 struct buffer_info *buffer1 = NULL; 774 struct buffer_info *buffer2 = NULL; 775 struct volume_info *volume; 776 hammer_blockmap_t blockmap; 777 hammer_blockmap_t freemap; 778 struct hammer_blockmap_layer1 *layer1; 779 struct hammer_blockmap_layer2 *layer2; 780 hammer_off_t layer1_offset; 781 hammer_off_t layer2_offset; 782 hammer_off_t zone2_offset; 783 void *ptr; 784 785 volume = get_volume(RootVolNo); 786 787 blockmap = &volume->ondisk->vol0_blockmap[zone]; 788 freemap = &volume->ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 789 790 /* 791 * Alignment and buffer-boundary issues. If the allocation would 792 * cross a buffer boundary we have to skip to the next buffer. 793 */ 794 bytes = (bytes + 15) & ~15; 795 796 again: 797 if ((blockmap->next_offset ^ (blockmap->next_offset + bytes - 1)) & 798 ~HAMMER_BUFMASK64) { 799 volume->cache.modified = 1; 800 blockmap->next_offset = (blockmap->next_offset + bytes) & 801 ~HAMMER_BUFMASK64; 802 } 803 804 /* 805 * Dive layer 1. For now we can't allocate data outside of volume 0. 806 */ 807 layer1_offset = freemap->phys_offset + 808 HAMMER_BLOCKMAP_LAYER1_OFFSET(blockmap->next_offset); 809 810 layer1 = get_buffer_data(layer1_offset, &buffer1, 0); 811 812 if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) { 813 fprintf(stderr, "alloc_blockmap: ran out of space!\n"); 814 exit(1); 815 } 816 817 /* 818 * Dive layer 2 819 */ 820 layer2_offset = layer1->phys_offset + 821 HAMMER_BLOCKMAP_LAYER2_OFFSET(blockmap->next_offset); 822 823 layer2 = get_buffer_data(layer2_offset, &buffer2, 0); 824 825 if (layer2->zone == HAMMER_ZONE_UNAVAIL_INDEX) { 826 fprintf(stderr, "alloc_blockmap: ran out of space!\n"); 827 exit(1); 828 } 829 830 /* 831 * If we are entering a new bigblock assign ownership to our 832 * zone. If the bigblock is owned by another zone skip it. 833 */ 834 if (layer2->zone == 0) { 835 --layer1->blocks_free; 836 layer2->zone = zone; 837 assert(layer2->bytes_free == HAMMER_LARGEBLOCK_SIZE); 838 assert(layer2->append_off == 0); 839 } 840 if (layer2->zone != zone) { 841 blockmap->next_offset = (blockmap->next_offset + HAMMER_LARGEBLOCK_SIZE) & 842 ~HAMMER_LARGEBLOCK_MASK64; 843 goto again; 844 } 845 846 buffer1->cache.modified = 1; 847 buffer2->cache.modified = 1; 848 volume->cache.modified = 1; 849 assert(layer2->append_off == 850 (blockmap->next_offset & HAMMER_LARGEBLOCK_MASK)); 851 layer2->bytes_free -= bytes; 852 *result_offp = blockmap->next_offset; 853 blockmap->next_offset += bytes; 854 layer2->append_off = (int)blockmap->next_offset & 855 HAMMER_LARGEBLOCK_MASK; 856 857 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE); 858 layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE); 859 860 zone2_offset = (*result_offp & ~HAMMER_OFF_ZONE_MASK) | 861 HAMMER_ZONE_ENCODE(zone, 0); 862 863 ptr = get_buffer_data(zone2_offset, bufferp, 0); 864 (*bufferp)->cache.modified = 1; 865 866 if (buffer1) 867 rel_buffer(buffer1); 868 if (buffer2) 869 rel_buffer(buffer2); 870 871 rel_volume(volume); 872 return(ptr); 873 } 874 875 /* 876 * Flush various tracking structures to disk 877 */ 878 879 /* 880 * Flush various tracking structures to disk 881 */ 882 void 883 flush_all_volumes(void) 884 { 885 struct volume_info *vol; 886 887 TAILQ_FOREACH(vol, &VolList, entry) 888 flush_volume(vol); 889 } 890 891 void 892 flush_volume(struct volume_info *volume) 893 { 894 struct buffer_info *buffer; 895 int i; 896 897 for (i = 0; i < HAMMER_BUFLISTS; ++i) { 898 TAILQ_FOREACH(buffer, &volume->buffer_lists[i], entry) 899 flush_buffer(buffer); 900 } 901 writehammerbuf(volume, volume->ondisk, 0); 902 volume->cache.modified = 0; 903 } 904 905 void 906 flush_buffer(struct buffer_info *buffer) 907 { 908 writehammerbuf(buffer->volume, buffer->ondisk, buffer->raw_offset); 909 buffer->cache.modified = 0; 910 } 911 912 #if 0 913 /* 914 * Generic buffer initialization 915 */ 916 static void 917 init_fifo_head(hammer_fifo_head_t head, u_int16_t hdr_type) 918 { 919 head->hdr_signature = HAMMER_HEAD_SIGNATURE; 920 head->hdr_type = hdr_type; 921 head->hdr_size = 0; 922 head->hdr_crc = 0; 923 head->hdr_seq = 0; 924 } 925 926 #endif 927 928 #if 0 929 /* 930 * Core I/O operations 931 */ 932 static void 933 readhammerbuf(struct volume_info *vol, void *data, int64_t offset) 934 { 935 ssize_t n; 936 937 n = pread(vol->fd, data, HAMMER_BUFSIZE, offset); 938 if (n != HAMMER_BUFSIZE) 939 err(1, "Read volume %d (%s)", vol->vol_no, vol->name); 940 } 941 942 #endif 943 944 static void 945 writehammerbuf(struct volume_info *vol, const void *data, int64_t offset) 946 { 947 ssize_t n; 948 949 n = pwrite(vol->fd, data, HAMMER_BUFSIZE, offset); 950 if (n != HAMMER_BUFSIZE) 951 err(1, "Write volume %d (%s)", vol->vol_no, vol->name); 952 } 953 954 void 955 panic(const char *ctl, ...) 956 { 957 va_list va; 958 959 va_start(va, ctl); 960 vfprintf(stderr, ctl, va); 961 va_end(va); 962 fprintf(stderr, "\n"); 963 exit(1); 964 } 965 966