1 /* 2 * Copyright (c) 2009 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> and 6 * Michael Neumann <mneumann@ntecs.de> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 3. Neither the name of The DragonFly Project nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific, prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37 #include "hammer.h" 38 39 static int 40 hammer_format_volume_header(struct hammer_mount *hmp, 41 struct hammer_volume_ondisk *ondisk, 42 const char *vol_name, int vol_no, int vol_count, 43 int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size); 44 45 static int 46 hammer_update_volumes_header(hammer_transaction_t trans, 47 int64_t total_bigblocks, int64_t empty_bigblocks); 48 49 static int 50 hammer_do_reblock(hammer_transaction_t trans, hammer_inode_t ip); 51 52 static int 53 hammer_format_freemap(hammer_transaction_t trans, hammer_volume_t volume); 54 55 static int 56 hammer_free_freemap(hammer_transaction_t trans, hammer_volume_t volume); 57 58 static int 59 hammer_count_bigblocks(hammer_mount_t hmp, hammer_volume_t volume, 60 int64_t *total_bigblocks, int64_t *empty_bigblocks); 61 62 int 63 hammer_ioc_volume_add(hammer_transaction_t trans, hammer_inode_t ip, 64 struct hammer_ioc_volume *ioc) 65 { 66 struct hammer_mount *hmp = trans->hmp; 67 struct mount *mp = hmp->mp; 68 struct hammer_volume_ondisk ondisk; 69 hammer_volume_t volume; 70 int64_t total_bigblocks, empty_bigblocks; 71 int free_vol_no = 0; 72 int error; 73 74 if (mp->mnt_flag & MNT_RDONLY) { 75 hmkprintf(hmp, "Cannot add volume to read-only HAMMER filesystem\n"); 76 return (EINVAL); 77 } 78 79 if (hmp->nvolumes >= HAMMER_MAX_VOLUMES) { 80 hmkprintf(hmp, "Max number of HAMMER volumes exceeded\n"); 81 return (EINVAL); 82 } 83 84 if (hammer_lock_ex_try(&hmp->volume_lock) != 0) { 85 hmkprintf(hmp, "Another volume operation is in progress!\n"); 86 return (EAGAIN); 87 } 88 89 /* 90 * Find an unused volume number. 91 */ 92 while (free_vol_no < HAMMER_MAX_VOLUMES && 93 HAMMER_VOLUME_NUMBER_IS_SET(hmp, free_vol_no)) { 94 ++free_vol_no; 95 } 96 if (free_vol_no >= HAMMER_MAX_VOLUMES) { 97 hmkprintf(hmp, "Max number of HAMMER volumes exceeded\n"); 98 error = EINVAL; 99 goto end; 100 } 101 102 error = hammer_format_volume_header( 103 hmp, 104 &ondisk, 105 hmp->rootvol->ondisk->vol_name, 106 free_vol_no, 107 hmp->nvolumes+1, 108 ioc->vol_size, 109 ioc->boot_area_size, 110 ioc->mem_area_size); 111 if (error) 112 goto end; 113 114 error = hammer_install_volume(hmp, ioc->device_name, NULL, &ondisk); 115 if (error) 116 goto end; 117 118 hammer_sync_lock_sh(trans); 119 hammer_lock_ex(&hmp->blkmap_lock); 120 121 volume = hammer_get_volume(hmp, free_vol_no, &error); 122 KKASSERT(volume != NULL && error == 0); 123 124 error = hammer_format_freemap(trans, volume); 125 KKASSERT(error == 0); 126 127 error = hammer_count_bigblocks(hmp, volume, 128 &total_bigblocks, &empty_bigblocks); 129 KKASSERT(error == 0); 130 KKASSERT(total_bigblocks == empty_bigblocks); 131 132 hammer_rel_volume(volume, 0); 133 134 ++hmp->nvolumes; 135 error = hammer_update_volumes_header(trans, 136 total_bigblocks, empty_bigblocks); 137 KKASSERT(error == 0); 138 139 hammer_unlock(&hmp->blkmap_lock); 140 hammer_sync_unlock(trans); 141 142 KKASSERT(error == 0); 143 end: 144 hammer_unlock(&hmp->volume_lock); 145 if (error) 146 hmkprintf(hmp, "An error occurred: %d\n", error); 147 return (error); 148 } 149 150 151 /* 152 * Remove a volume. 153 */ 154 int 155 hammer_ioc_volume_del(hammer_transaction_t trans, hammer_inode_t ip, 156 struct hammer_ioc_volume *ioc) 157 { 158 struct hammer_mount *hmp = trans->hmp; 159 struct mount *mp = hmp->mp; 160 struct hammer_volume_ondisk ondisk; 161 hammer_volume_t volume; 162 int64_t total_bigblocks, empty_bigblocks; 163 int vol_no; 164 int error = 0; 165 166 if (mp->mnt_flag & MNT_RDONLY) { 167 hmkprintf(hmp, "Cannot del volume from read-only HAMMER filesystem\n"); 168 return (EINVAL); 169 } 170 171 if (hmp->nvolumes <= 1) { 172 hmkprintf(hmp, "No HAMMER volume to delete\n"); 173 return (EINVAL); 174 } 175 176 if (hammer_lock_ex_try(&hmp->volume_lock) != 0) { 177 hmkprintf(hmp, "Another volume operation is in progress!\n"); 178 return (EAGAIN); 179 } 180 181 /* 182 * find volume by volname 183 */ 184 volume = NULL; 185 HAMMER_VOLUME_NUMBER_FOREACH(hmp, vol_no) { 186 volume = hammer_get_volume(hmp, vol_no, &error); 187 KKASSERT(volume != NULL && error == 0); 188 if (strcmp(volume->vol_name, ioc->device_name) == 0) { 189 break; 190 } 191 hammer_rel_volume(volume, 0); 192 volume = NULL; 193 } 194 195 if (volume == NULL) { 196 hmkprintf(hmp, "Couldn't find volume\n"); 197 error = EINVAL; 198 goto end; 199 } 200 201 if (volume == trans->rootvol) { 202 hmkprintf(hmp, "Cannot remove root-volume\n"); 203 hammer_rel_volume(volume, 0); 204 error = EINVAL; 205 goto end; 206 } 207 208 /* 209 * Reblock filesystem if the volume is not empty 210 */ 211 hmp->volume_to_remove = volume->vol_no; 212 213 error = hammer_count_bigblocks(hmp, volume, 214 &total_bigblocks, &empty_bigblocks); 215 KKASSERT(error == 0); 216 217 if (total_bigblocks == empty_bigblocks) { 218 hmkprintf(hmp, "%s is already empty\n", volume->vol_name); 219 } else { 220 error = hammer_do_reblock(trans, ip); 221 if (error) { 222 hmp->volume_to_remove = -1; 223 hammer_rel_volume(volume, 0); 224 goto end; 225 } 226 } 227 228 /* 229 * Sync filesystem 230 */ 231 hammer_flush_dirty(hmp, 30); 232 233 hammer_sync_lock_sh(trans); 234 hammer_lock_ex(&hmp->blkmap_lock); 235 236 error = hammer_count_bigblocks(hmp, volume, 237 &total_bigblocks, &empty_bigblocks); 238 KKASSERT(error == 0); 239 240 error = hammer_free_freemap(trans, volume); 241 if (error) { 242 hmkprintf(hmp, "Failed to free volume: "); 243 if (error == EBUSY) 244 kprintf("Volume %d not empty\n", volume->vol_no); 245 else 246 kprintf("%d\n", error); 247 hmp->volume_to_remove = -1; 248 hammer_rel_volume(volume, 0); 249 goto end1; 250 } 251 hammer_rel_volume(volume, 0); 252 253 /* 254 * Unload buffers 255 */ 256 RB_SCAN(hammer_buf_rb_tree, &hmp->rb_bufs_root, NULL, 257 hammer_unload_buffer, volume); 258 259 bzero(&ondisk, sizeof(ondisk)); 260 error = hammer_unload_volume(volume, &ondisk); 261 if (error == -1) { 262 hmkprintf(hmp, "Failed to unload volume\n"); 263 goto end1; 264 } 265 266 --hmp->nvolumes; 267 error = hammer_update_volumes_header(trans, 268 -total_bigblocks, -empty_bigblocks); 269 KKASSERT(error == 0); 270 hmp->volume_to_remove = -1; 271 272 end1: 273 hammer_unlock(&hmp->blkmap_lock); 274 hammer_sync_unlock(trans); 275 276 end: 277 hammer_unlock(&hmp->volume_lock); 278 if (error) 279 hmkprintf(hmp, "An error occurred: %d\n", error); 280 return (error); 281 } 282 283 284 int 285 hammer_ioc_volume_list(hammer_transaction_t trans, hammer_inode_t ip, 286 struct hammer_ioc_volume_list *ioc) 287 { 288 struct hammer_mount *hmp = trans->hmp; 289 hammer_volume_t volume; 290 int error = 0; 291 int i, len, cnt = 0; 292 293 if (hammer_lock_ex_try(&hmp->volume_lock) != 0) { 294 hmkprintf(hmp, "Another volume operation is in progress!\n"); 295 return (EAGAIN); 296 } 297 298 HAMMER_VOLUME_NUMBER_FOREACH(hmp, i) { 299 if (cnt >= ioc->nvols) 300 break; 301 volume = hammer_get_volume(hmp, i, &error); 302 KKASSERT(volume != NULL && error == 0); 303 304 len = strlen(volume->vol_name) + 1; 305 KKASSERT(len <= MAXPATHLEN); 306 307 error = copyout(volume->vol_name, ioc->vols[cnt].device_name, 308 len); 309 hammer_rel_volume(volume, 0); 310 if (error) 311 goto end; 312 cnt++; 313 } 314 ioc->nvols = cnt; 315 316 end: 317 hammer_unlock(&hmp->volume_lock); 318 return (error); 319 } 320 321 static 322 int 323 hammer_do_reblock(hammer_transaction_t trans, hammer_inode_t ip) 324 { 325 struct hammer_mount *hmp = trans->hmp; 326 int error; 327 int vol_no; 328 329 struct hammer_ioc_reblock reblock; 330 bzero(&reblock, sizeof(reblock)); 331 332 vol_no = trans->hmp->volume_to_remove; 333 KKASSERT(vol_no != -1); 334 335 reblock.key_beg.localization = HAMMER_MIN_LOCALIZATION; 336 reblock.key_beg.obj_id = HAMMER_MIN_OBJID; 337 reblock.key_end.localization = HAMMER_MAX_LOCALIZATION; 338 reblock.key_end.obj_id = HAMMER_MAX_OBJID; 339 reblock.head.flags = HAMMER_IOC_DO_FLAGS; 340 reblock.free_level = 0; /* reblock all big-blocks */ 341 reblock.allpfs = 1; /* reblock all PFS */ 342 reblock.vol_no = vol_no; 343 344 hmkprintf(hmp, "reblock started\n"); 345 error = hammer_ioc_reblock(trans, ip, &reblock); 346 347 if (reblock.head.flags & HAMMER_IOC_HEAD_INTR) { 348 error = EINTR; 349 } 350 351 if (error) { 352 if (error == EINTR) { 353 hmkprintf(hmp, "reblock was interrupted\n"); 354 } else { 355 hmkprintf(hmp, "reblock failed: %d\n", error); 356 } 357 return(error); 358 } 359 360 return(0); 361 } 362 363 static int 364 hammer_format_freemap(hammer_transaction_t trans, hammer_volume_t volume) 365 { 366 struct hammer_mount *hmp = trans->hmp; 367 struct hammer_volume_ondisk *ondisk; 368 hammer_blockmap_t freemap; 369 hammer_off_t alloc_offset; 370 hammer_off_t phys_offset; 371 hammer_off_t block_offset; 372 hammer_off_t layer1_offset; 373 hammer_off_t layer2_offset; 374 hammer_off_t vol_free_end; 375 hammer_off_t aligned_vol_free_end; 376 struct hammer_blockmap_layer1 *layer1; 377 struct hammer_blockmap_layer2 *layer2; 378 hammer_buffer_t buffer1 = NULL; 379 hammer_buffer_t buffer2 = NULL; 380 int64_t vol_buf_size; 381 int64_t layer1_count = 0; 382 int error = 0; 383 384 KKASSERT(volume->vol_no != HAMMER_ROOT_VOLNO); 385 386 ondisk = volume->ondisk; 387 vol_buf_size = ondisk->vol_buf_end - ondisk->vol_buf_beg; 388 vol_free_end = HAMMER_ENCODE_RAW_BUFFER(ondisk->vol_no, 389 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64); 390 aligned_vol_free_end = (vol_free_end + HAMMER_BLOCKMAP_LAYER2_MASK) 391 & ~HAMMER_BLOCKMAP_LAYER2_MASK; 392 393 freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 394 alloc_offset = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0); 395 396 hmkprintf(hmp, "Initialize freemap volume %d\n", volume->vol_no); 397 398 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0); 399 phys_offset < aligned_vol_free_end; 400 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 401 layer1_offset = freemap->phys_offset + 402 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 403 layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1); 404 if (error) 405 goto end; 406 if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) { 407 hammer_modify_buffer(trans, buffer1, layer1, sizeof(*layer1)); 408 bzero(layer1, sizeof(*layer1)); 409 layer1->phys_offset = alloc_offset; 410 layer1->blocks_free = 0; 411 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE); 412 hammer_modify_buffer_done(buffer1); 413 alloc_offset += HAMMER_BIGBLOCK_SIZE; 414 } 415 } 416 417 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0); 418 phys_offset < aligned_vol_free_end; 419 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 420 layer1_count = 0; 421 layer1_offset = freemap->phys_offset + 422 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 423 layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1); 424 if (error) 425 goto end; 426 KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); 427 428 for (block_offset = 0; 429 block_offset < HAMMER_BLOCKMAP_LAYER2; 430 block_offset += HAMMER_BIGBLOCK_SIZE) { 431 layer2_offset = layer1->phys_offset + 432 HAMMER_BLOCKMAP_LAYER2_OFFSET(block_offset); 433 layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer2); 434 if (error) 435 goto end; 436 437 hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2)); 438 bzero(layer2, sizeof(*layer2)); 439 440 if (phys_offset + block_offset < alloc_offset) { 441 layer2->zone = HAMMER_ZONE_FREEMAP_INDEX; 442 layer2->append_off = HAMMER_BIGBLOCK_SIZE; 443 layer2->bytes_free = 0; 444 } else if (phys_offset + block_offset < vol_free_end) { 445 layer2->zone = 0; 446 layer2->append_off = 0; 447 layer2->bytes_free = HAMMER_BIGBLOCK_SIZE; 448 ++layer1_count; 449 } else { 450 layer2->zone = HAMMER_ZONE_UNAVAIL_INDEX; 451 layer2->append_off = HAMMER_BIGBLOCK_SIZE; 452 layer2->bytes_free = 0; 453 } 454 455 layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE); 456 hammer_modify_buffer_done(buffer2); 457 } 458 459 hammer_modify_buffer(trans, buffer1, layer1, sizeof(*layer1)); 460 layer1->blocks_free += layer1_count; 461 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE); 462 hammer_modify_buffer_done(buffer1); 463 } 464 465 end: 466 if (buffer1) 467 hammer_rel_buffer(buffer1, 0); 468 if (buffer2) 469 hammer_rel_buffer(buffer2, 0); 470 471 return error; 472 } 473 474 static int 475 hammer_free_freemap(hammer_transaction_t trans, hammer_volume_t volume) 476 { 477 struct hammer_mount *hmp = trans->hmp; 478 struct hammer_volume_ondisk *ondisk; 479 hammer_blockmap_t freemap; 480 hammer_off_t phys_offset; 481 hammer_off_t block_offset; 482 hammer_off_t layer1_offset; 483 hammer_off_t layer2_offset; 484 hammer_off_t vol_free_end; 485 hammer_off_t aligned_vol_free_end; 486 struct hammer_blockmap_layer1 *layer1; 487 struct hammer_blockmap_layer2 *layer2; 488 hammer_buffer_t buffer1 = NULL; 489 hammer_buffer_t buffer2 = NULL; 490 int64_t vol_buf_size; 491 int64_t layer1_count = 0; 492 int error = 0; 493 494 KKASSERT(volume->vol_no != HAMMER_ROOT_VOLNO); 495 496 ondisk = volume->ondisk; 497 vol_buf_size = ondisk->vol_buf_end - ondisk->vol_buf_beg; 498 vol_free_end = HAMMER_ENCODE_RAW_BUFFER(ondisk->vol_no, 499 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64); 500 aligned_vol_free_end = (vol_free_end + HAMMER_BLOCKMAP_LAYER2_MASK) 501 & ~HAMMER_BLOCKMAP_LAYER2_MASK; 502 503 freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 504 505 hmkprintf(hmp, "Free freemap volume %d\n", volume->vol_no); 506 507 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0); 508 phys_offset < aligned_vol_free_end; 509 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 510 layer1_count = 0; 511 layer1_offset = freemap->phys_offset + 512 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 513 layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1); 514 if (error) 515 goto end; 516 KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); 517 518 for (block_offset = 0; 519 block_offset < HAMMER_BLOCKMAP_LAYER2; 520 block_offset += HAMMER_BIGBLOCK_SIZE) { 521 layer2_offset = layer1->phys_offset + 522 HAMMER_BLOCKMAP_LAYER2_OFFSET(block_offset); 523 layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer2); 524 if (error) 525 goto end; 526 527 switch (layer2->zone) { 528 case HAMMER_ZONE_UNDO_INDEX: 529 KKASSERT(0); 530 case HAMMER_ZONE_FREEMAP_INDEX: 531 case HAMMER_ZONE_UNAVAIL_INDEX: 532 continue; 533 default: 534 KKASSERT(phys_offset + block_offset < aligned_vol_free_end); 535 if (layer2->append_off == 0 && 536 layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) 537 continue; 538 break; 539 } 540 return EBUSY; /* Not empty */ 541 } 542 } 543 544 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0); 545 phys_offset < aligned_vol_free_end; 546 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 547 layer1_count = 0; 548 layer1_offset = freemap->phys_offset + 549 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 550 layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1); 551 if (error) 552 goto end; 553 KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); 554 555 hammer_modify_buffer(trans, buffer1, layer1, sizeof(*layer1)); 556 bzero(layer1, sizeof(*layer1)); 557 layer1->phys_offset = HAMMER_BLOCKMAP_UNAVAIL; 558 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE); 559 hammer_modify_buffer_done(buffer1); 560 } 561 562 end: 563 if (buffer1) 564 hammer_rel_buffer(buffer1, 0); 565 if (buffer2) 566 hammer_rel_buffer(buffer2, 0); 567 568 return error; 569 } 570 571 static int 572 hammer_format_volume_header(struct hammer_mount *hmp, 573 struct hammer_volume_ondisk *ondisk, 574 const char *vol_name, int vol_no, int vol_count, 575 int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size) 576 { 577 int64_t vol_alloc; 578 579 KKASSERT(HAMMER_BUFSIZE >= sizeof(struct hammer_volume_ondisk)); 580 581 bzero(ondisk, sizeof(struct hammer_volume_ondisk)); 582 ksnprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", vol_name); 583 ondisk->vol_fstype = hmp->rootvol->ondisk->vol_fstype; 584 ondisk->vol_signature = HAMMER_FSBUF_VOLUME; 585 ondisk->vol_fsid = hmp->fsid; 586 ondisk->vol_rootvol = hmp->rootvol->vol_no; 587 ondisk->vol_no = vol_no; 588 ondisk->vol_count = vol_count; 589 ondisk->vol_version = hmp->version; 590 591 /* 592 * Reserve space for (future) header junk, copy volume relative 593 * offset from the existing root volume. 594 */ 595 vol_alloc = hmp->rootvol->ondisk->vol_bot_beg; 596 ondisk->vol_bot_beg = vol_alloc; 597 vol_alloc += boot_area_size; 598 ondisk->vol_mem_beg = vol_alloc; 599 vol_alloc += mem_area_size; 600 601 /* 602 * The remaining area is the zone 2 buffer allocation area. 603 */ 604 ondisk->vol_buf_beg = vol_alloc; 605 ondisk->vol_buf_end = vol_size & ~(int64_t)HAMMER_BUFMASK; 606 607 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) { 608 hmkprintf(hmp, "volume %d %s is too small to hold the volume header\n", 609 ondisk->vol_no, ondisk->vol_name); 610 return(EFTYPE); 611 } 612 613 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) / 614 HAMMER_BUFSIZE; 615 ondisk->vol_blocksize = HAMMER_BUFSIZE; 616 return(0); 617 } 618 619 static int 620 hammer_update_volumes_header(hammer_transaction_t trans, 621 int64_t total_bigblocks, int64_t empty_bigblocks) 622 { 623 struct hammer_mount *hmp = trans->hmp; 624 struct mount *mp = hmp->mp; 625 hammer_volume_t volume; 626 int vol_no; 627 int error = 0; 628 629 /* 630 * Set each volume's new value of the vol_count field. 631 */ 632 HAMMER_VOLUME_NUMBER_FOREACH(hmp, vol_no) { 633 volume = hammer_get_volume(hmp, vol_no, &error); 634 KKASSERT(volume != NULL && error == 0); 635 hammer_modify_volume_field(trans, volume, vol_count); 636 volume->ondisk->vol_count = hmp->nvolumes; 637 hammer_modify_volume_done(volume); 638 639 /* 640 * Only changes to the header of the root volume 641 * are automatically flushed to disk. For all 642 * other volumes that we modify we do it here. 643 * 644 * No interlock is needed, volume buffers are not 645 * messed with by bioops. 646 */ 647 if (volume != trans->rootvol && volume->io.modified) { 648 hammer_crc_set_volume(volume->ondisk); 649 hammer_io_flush(&volume->io, 0); 650 } 651 652 hammer_rel_volume(volume, 0); 653 } 654 655 /* 656 * Update the total number of big-blocks. 657 */ 658 hammer_modify_volume_field(trans, trans->rootvol, vol0_stat_bigblocks); 659 trans->rootvol->ondisk->vol0_stat_bigblocks += total_bigblocks; 660 hammer_modify_volume_done(trans->rootvol); 661 662 /* 663 * Big-block count changed so recompute the total number of blocks. 664 */ 665 mp->mnt_stat.f_blocks = trans->rootvol->ondisk->vol0_stat_bigblocks * 666 HAMMER_BUFFERS_PER_BIGBLOCK; 667 mp->mnt_vstat.f_blocks = trans->rootvol->ondisk->vol0_stat_bigblocks * 668 HAMMER_BUFFERS_PER_BIGBLOCK; 669 670 /* 671 * Update the total number of free big-blocks. 672 */ 673 hammer_modify_volume_field(trans, trans->rootvol, 674 vol0_stat_freebigblocks); 675 trans->rootvol->ondisk->vol0_stat_freebigblocks += empty_bigblocks; 676 hammer_modify_volume_done(trans->rootvol); 677 678 /* 679 * Update the copy in hmp. 680 */ 681 hmp->copy_stat_freebigblocks = 682 trans->rootvol->ondisk->vol0_stat_freebigblocks; 683 684 return(error); 685 } 686 687 /* 688 * Count total big-blocks and empty big-blocks within the volume. 689 * The volume must be a non-root volume. 690 * 691 * Note that total big-blocks doesn't include big-blocks for layer2 692 * (and obviously layer1 and undomap). This is requirement of the 693 * volume header and this function is to retrieve that information. 694 */ 695 static int 696 hammer_count_bigblocks(hammer_mount_t hmp, hammer_volume_t volume, 697 int64_t *total_bigblocks, int64_t *empty_bigblocks) 698 { 699 struct hammer_volume_ondisk *ondisk; 700 hammer_blockmap_t freemap; 701 hammer_off_t phys_offset; 702 hammer_off_t block_offset; 703 hammer_off_t layer1_offset; 704 hammer_off_t layer2_offset; 705 hammer_off_t vol_free_end; 706 hammer_off_t aligned_vol_free_end; 707 struct hammer_blockmap_layer1 *layer1; 708 struct hammer_blockmap_layer2 *layer2; 709 hammer_buffer_t buffer1 = NULL; 710 hammer_buffer_t buffer2 = NULL; 711 int64_t vol_buf_size; 712 int64_t total = 0; 713 int64_t empty = 0; 714 int error = 0; 715 716 KKASSERT(volume->vol_no != HAMMER_ROOT_VOLNO); 717 718 ondisk = volume->ondisk; 719 vol_buf_size = ondisk->vol_buf_end - ondisk->vol_buf_beg; 720 vol_free_end = HAMMER_ENCODE_RAW_BUFFER(ondisk->vol_no, 721 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64); 722 aligned_vol_free_end = (vol_free_end + HAMMER_BLOCKMAP_LAYER2_MASK) 723 & ~HAMMER_BLOCKMAP_LAYER2_MASK; 724 725 freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; 726 727 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(volume->ondisk->vol_no, 0); 728 phys_offset < aligned_vol_free_end; 729 phys_offset += HAMMER_BLOCKMAP_LAYER2) { 730 layer1_offset = freemap->phys_offset + 731 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset); 732 layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1); 733 if (error) 734 goto end; 735 736 for (block_offset = 0; 737 block_offset < HAMMER_BLOCKMAP_LAYER2; 738 block_offset += HAMMER_BIGBLOCK_SIZE) { 739 layer2_offset = layer1->phys_offset + 740 HAMMER_BLOCKMAP_LAYER2_OFFSET(block_offset); 741 layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer2); 742 if (error) 743 goto end; 744 745 switch (layer2->zone) { 746 case HAMMER_ZONE_UNDO_INDEX: 747 KKASSERT(0); 748 case HAMMER_ZONE_FREEMAP_INDEX: 749 case HAMMER_ZONE_UNAVAIL_INDEX: 750 continue; 751 default: 752 KKASSERT(phys_offset + block_offset < aligned_vol_free_end); 753 total++; 754 if (layer2->append_off == 0 && 755 layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) 756 empty++; 757 break; 758 } 759 } 760 } 761 762 hmkprintf(hmp, "big-blocks total=%jd empty=%jd\n", total, empty); 763 *total_bigblocks = total; 764 *empty_bigblocks = empty; 765 end: 766 if (buffer1) 767 hammer_rel_buffer(buffer1, 0); 768 if (buffer2) 769 hammer_rel_buffer(buffer2, 0); 770 771 return error; 772 } 773