diff options
Diffstat (limited to 'fs/ext3/inode.c')
| -rw-r--r-- | fs/ext3/inode.c | 144 |
1 files changed, 61 insertions, 83 deletions
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 040eb288bb1c..ea5888688f94 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c | |||
| @@ -455,12 +455,11 @@ static unsigned long ext3_find_near(struct inode *inode, Indirect *ind) | |||
| 455 | * @goal: place to store the result. | 455 | * @goal: place to store the result. |
| 456 | * | 456 | * |
| 457 | * Normally this function find the prefered place for block allocation, | 457 | * Normally this function find the prefered place for block allocation, |
| 458 | * stores it in *@goal and returns zero. If the branch had been changed | 458 | * stores it in *@goal and returns zero. |
| 459 | * under us we return -EAGAIN. | ||
| 460 | */ | 459 | */ |
| 461 | 460 | ||
| 462 | static int ext3_find_goal(struct inode *inode, long block, Indirect chain[4], | 461 | static unsigned long ext3_find_goal(struct inode *inode, long block, |
| 463 | Indirect *partial, unsigned long *goal) | 462 | Indirect chain[4], Indirect *partial) |
| 464 | { | 463 | { |
| 465 | struct ext3_block_alloc_info *block_i = EXT3_I(inode)->i_block_alloc_info; | 464 | struct ext3_block_alloc_info *block_i = EXT3_I(inode)->i_block_alloc_info; |
| 466 | 465 | ||
| @@ -470,15 +469,10 @@ static int ext3_find_goal(struct inode *inode, long block, Indirect chain[4], | |||
| 470 | */ | 469 | */ |
| 471 | if (block_i && (block == block_i->last_alloc_logical_block + 1) | 470 | if (block_i && (block == block_i->last_alloc_logical_block + 1) |
| 472 | && (block_i->last_alloc_physical_block != 0)) { | 471 | && (block_i->last_alloc_physical_block != 0)) { |
| 473 | *goal = block_i->last_alloc_physical_block + 1; | 472 | return block_i->last_alloc_physical_block + 1; |
| 474 | return 0; | ||
| 475 | } | 473 | } |
| 476 | 474 | ||
| 477 | if (verify_chain(chain, partial)) { | 475 | return ext3_find_near(inode, partial); |
| 478 | *goal = ext3_find_near(inode, partial); | ||
| 479 | return 0; | ||
| 480 | } | ||
| 481 | return -EAGAIN; | ||
| 482 | } | 476 | } |
| 483 | 477 | ||
| 484 | /** | 478 | /** |
| @@ -582,12 +576,9 @@ static int ext3_alloc_branch(handle_t *handle, struct inode *inode, | |||
| 582 | * @where: location of missing link | 576 | * @where: location of missing link |
| 583 | * @num: number of blocks we are adding | 577 | * @num: number of blocks we are adding |
| 584 | * | 578 | * |
| 585 | * This function verifies that chain (up to the missing link) had not | 579 | * This function fills the missing link and does all housekeeping needed in |
| 586 | * changed, fills the missing link and does all housekeeping needed in | ||
| 587 | * inode (->i_blocks, etc.). In case of success we end up with the full | 580 | * inode (->i_blocks, etc.). In case of success we end up with the full |
| 588 | * chain to new block and return 0. Otherwise (== chain had been changed) | 581 | * chain to new block and return 0. |
| 589 | * we free the new blocks (forgetting their buffer_heads, indeed) and | ||
| 590 | * return -EAGAIN. | ||
| 591 | */ | 582 | */ |
| 592 | 583 | ||
| 593 | static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block, | 584 | static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block, |
| @@ -608,12 +599,6 @@ static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block, | |||
| 608 | if (err) | 599 | if (err) |
| 609 | goto err_out; | 600 | goto err_out; |
| 610 | } | 601 | } |
| 611 | /* Verify that place we are splicing to is still there and vacant */ | ||
| 612 | |||
| 613 | if (!verify_chain(chain, where-1) || *where->p) | ||
| 614 | /* Writer: end */ | ||
| 615 | goto changed; | ||
| 616 | |||
| 617 | /* That's it */ | 602 | /* That's it */ |
| 618 | 603 | ||
| 619 | *where->p = where->key; | 604 | *where->p = where->key; |
| @@ -657,26 +642,11 @@ static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block, | |||
| 657 | } | 642 | } |
| 658 | return err; | 643 | return err; |
| 659 | 644 | ||
| 660 | changed: | ||
| 661 | /* | ||
| 662 | * AKPM: if where[i].bh isn't part of the current updating | ||
| 663 | * transaction then we explode nastily. Test this code path. | ||
| 664 | */ | ||
| 665 | jbd_debug(1, "the chain changed: try again\n"); | ||
| 666 | err = -EAGAIN; | ||
| 667 | |||
| 668 | err_out: | 645 | err_out: |
| 669 | for (i = 1; i < num; i++) { | 646 | for (i = 1; i < num; i++) { |
| 670 | BUFFER_TRACE(where[i].bh, "call journal_forget"); | 647 | BUFFER_TRACE(where[i].bh, "call journal_forget"); |
| 671 | ext3_journal_forget(handle, where[i].bh); | 648 | ext3_journal_forget(handle, where[i].bh); |
| 672 | } | 649 | } |
| 673 | /* For the normal collision cleanup case, we free up the blocks. | ||
| 674 | * On genuine filesystem errors we don't even think about doing | ||
| 675 | * that. */ | ||
| 676 | if (err == -EAGAIN) | ||
| 677 | for (i = 0; i < num; i++) | ||
| 678 | ext3_free_blocks(handle, inode, | ||
| 679 | le32_to_cpu(where[i].key), 1); | ||
| 680 | return err; | 650 | return err; |
| 681 | } | 651 | } |
| 682 | 652 | ||
| @@ -708,7 +678,7 @@ ext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock, | |||
| 708 | unsigned long goal; | 678 | unsigned long goal; |
| 709 | int left; | 679 | int left; |
| 710 | int boundary = 0; | 680 | int boundary = 0; |
| 711 | int depth = ext3_block_to_path(inode, iblock, offsets, &boundary); | 681 | const int depth = ext3_block_to_path(inode, iblock, offsets, &boundary); |
| 712 | struct ext3_inode_info *ei = EXT3_I(inode); | 682 | struct ext3_inode_info *ei = EXT3_I(inode); |
| 713 | 683 | ||
| 714 | J_ASSERT(handle != NULL || create == 0); | 684 | J_ASSERT(handle != NULL || create == 0); |
| @@ -716,54 +686,55 @@ ext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock, | |||
| 716 | if (depth == 0) | 686 | if (depth == 0) |
| 717 | goto out; | 687 | goto out; |
| 718 | 688 | ||
| 719 | reread: | ||
| 720 | partial = ext3_get_branch(inode, depth, offsets, chain, &err); | 689 | partial = ext3_get_branch(inode, depth, offsets, chain, &err); |
| 721 | 690 | ||
| 722 | /* Simplest case - block found, no allocation needed */ | 691 | /* Simplest case - block found, no allocation needed */ |
| 723 | if (!partial) { | 692 | if (!partial) { |
| 724 | clear_buffer_new(bh_result); | 693 | clear_buffer_new(bh_result); |
| 725 | got_it: | 694 | goto got_it; |
| 726 | map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); | ||
| 727 | if (boundary) | ||
| 728 | set_buffer_boundary(bh_result); | ||
| 729 | /* Clean up and exit */ | ||
| 730 | partial = chain+depth-1; /* the whole chain */ | ||
| 731 | goto cleanup; | ||
| 732 | } | 695 | } |
| 733 | 696 | ||
| 734 | /* Next simple case - plain lookup or failed read of indirect block */ | 697 | /* Next simple case - plain lookup or failed read of indirect block */ |
| 735 | if (!create || err == -EIO) { | 698 | if (!create || err == -EIO) |
| 736 | cleanup: | 699 | goto cleanup; |
| 700 | |||
| 701 | down(&ei->truncate_sem); | ||
| 702 | |||
| 703 | /* | ||
| 704 | * If the indirect block is missing while we are reading | ||
| 705 | * the chain(ext3_get_branch() returns -EAGAIN err), or | ||
| 706 | * if the chain has been changed after we grab the semaphore, | ||
| 707 | * (either because another process truncated this branch, or | ||
| 708 | * another get_block allocated this branch) re-grab the chain to see if | ||
| 709 | * the request block has been allocated or not. | ||
| 710 | * | ||
| 711 | * Since we already block the truncate/other get_block | ||
| 712 | * at this point, we will have the current copy of the chain when we | ||
| 713 | * splice the branch into the tree. | ||
| 714 | */ | ||
| 715 | if (err == -EAGAIN || !verify_chain(chain, partial)) { | ||
| 737 | while (partial > chain) { | 716 | while (partial > chain) { |
| 738 | BUFFER_TRACE(partial->bh, "call brelse"); | ||
| 739 | brelse(partial->bh); | 717 | brelse(partial->bh); |
| 740 | partial--; | 718 | partial--; |
| 741 | } | 719 | } |
| 742 | BUFFER_TRACE(bh_result, "returned"); | 720 | partial = ext3_get_branch(inode, depth, offsets, chain, &err); |
| 743 | out: | 721 | if (!partial) { |
| 744 | return err; | 722 | up(&ei->truncate_sem); |
| 723 | if (err) | ||
| 724 | goto cleanup; | ||
| 725 | clear_buffer_new(bh_result); | ||
| 726 | goto got_it; | ||
| 727 | } | ||
| 745 | } | 728 | } |
| 746 | 729 | ||
| 747 | /* | 730 | /* |
| 748 | * Indirect block might be removed by truncate while we were | 731 | * Okay, we need to do block allocation. Lazily initialize the block |
| 749 | * reading it. Handling of that case (forget what we've got and | 732 | * allocation info here if necessary |
| 750 | * reread) is taken out of the main path. | 733 | */ |
| 751 | */ | 734 | if (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info)) |
| 752 | if (err == -EAGAIN) | ||
| 753 | goto changed; | ||
| 754 | |||
| 755 | goal = 0; | ||
| 756 | down(&ei->truncate_sem); | ||
| 757 | |||
| 758 | /* lazy initialize the block allocation info here if necessary */ | ||
| 759 | if (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info)) { | ||
| 760 | ext3_init_block_alloc_info(inode); | 735 | ext3_init_block_alloc_info(inode); |
| 761 | } | ||
| 762 | 736 | ||
| 763 | if (ext3_find_goal(inode, iblock, chain, partial, &goal) < 0) { | 737 | goal = ext3_find_goal(inode, iblock, chain, partial); |
| 764 | up(&ei->truncate_sem); | ||
| 765 | goto changed; | ||
| 766 | } | ||
| 767 | 738 | ||
| 768 | left = (chain + depth) - partial; | 739 | left = (chain + depth) - partial; |
| 769 | 740 | ||
| @@ -771,38 +742,45 @@ out: | |||
| 771 | * Block out ext3_truncate while we alter the tree | 742 | * Block out ext3_truncate while we alter the tree |
| 772 | */ | 743 | */ |
| 773 | err = ext3_alloc_branch(handle, inode, left, goal, | 744 | err = ext3_alloc_branch(handle, inode, left, goal, |
| 774 | offsets+(partial-chain), partial); | 745 | offsets + (partial - chain), partial); |
| 775 | 746 | ||
| 776 | /* The ext3_splice_branch call will free and forget any buffers | 747 | /* |
| 748 | * The ext3_splice_branch call will free and forget any buffers | ||
| 777 | * on the new chain if there is a failure, but that risks using | 749 | * on the new chain if there is a failure, but that risks using |
| 778 | * up transaction credits, especially for bitmaps where the | 750 | * up transaction credits, especially for bitmaps where the |
| 779 | * credits cannot be returned. Can we handle this somehow? We | 751 | * credits cannot be returned. Can we handle this somehow? We |
| 780 | * may need to return -EAGAIN upwards in the worst case. --sct */ | 752 | * may need to return -EAGAIN upwards in the worst case. --sct |
| 753 | */ | ||
| 781 | if (!err) | 754 | if (!err) |
| 782 | err = ext3_splice_branch(handle, inode, iblock, chain, | 755 | err = ext3_splice_branch(handle, inode, iblock, chain, |
| 783 | partial, left); | 756 | partial, left); |
| 784 | /* i_disksize growing is protected by truncate_sem | 757 | /* |
| 785 | * don't forget to protect it if you're about to implement | 758 | * i_disksize growing is protected by truncate_sem. Don't forget to |
| 786 | * concurrent ext3_get_block() -bzzz */ | 759 | * protect it if you're about to implement concurrent |
| 760 | * ext3_get_block() -bzzz | ||
| 761 | */ | ||
| 787 | if (!err && extend_disksize && inode->i_size > ei->i_disksize) | 762 | if (!err && extend_disksize && inode->i_size > ei->i_disksize) |
| 788 | ei->i_disksize = inode->i_size; | 763 | ei->i_disksize = inode->i_size; |
| 789 | up(&ei->truncate_sem); | 764 | up(&ei->truncate_sem); |
| 790 | if (err == -EAGAIN) | ||
| 791 | goto changed; | ||
| 792 | if (err) | 765 | if (err) |
| 793 | goto cleanup; | 766 | goto cleanup; |
| 794 | 767 | ||
| 795 | set_buffer_new(bh_result); | 768 | set_buffer_new(bh_result); |
| 796 | goto got_it; | 769 | got_it: |
| 797 | 770 | map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); | |
| 798 | changed: | 771 | if (boundary) |
| 772 | set_buffer_boundary(bh_result); | ||
| 773 | /* Clean up and exit */ | ||
| 774 | partial = chain + depth - 1; /* the whole chain */ | ||
| 775 | cleanup: | ||
| 799 | while (partial > chain) { | 776 | while (partial > chain) { |
| 800 | jbd_debug(1, "buffer chain changed, retrying\n"); | 777 | BUFFER_TRACE(partial->bh, "call brelse"); |
| 801 | BUFFER_TRACE(partial->bh, "brelsing"); | ||
| 802 | brelse(partial->bh); | 778 | brelse(partial->bh); |
| 803 | partial--; | 779 | partial--; |
| 804 | } | 780 | } |
| 805 | goto reread; | 781 | BUFFER_TRACE(bh_result, "returned"); |
| 782 | out: | ||
| 783 | return err; | ||
| 806 | } | 784 | } |
| 807 | 785 | ||
| 808 | static int ext3_get_block(struct inode *inode, sector_t iblock, | 786 | static int ext3_get_block(struct inode *inode, sector_t iblock, |
