diff options
author | Tao Ma <tao.ma@oracle.com> | 2009-08-23 23:12:02 -0400 |
---|---|---|
committer | Joel Becker <joel.becker@oracle.com> | 2009-09-22 23:09:30 -0400 |
commit | 8bf396de984e68491569b49770e4fd7aca40ba65 (patch) | |
tree | 1c755fa7af20e14722378df729201e6f8497fadb /fs/ocfs2 | |
parent | 374a263e790c4de85844283c098810a92985f623 (diff) |
ocfs2: Basic tree root operation.
Add basic refcount tree root operation.
Signed-off-by: Tao Ma <tao.ma@oracle.com>
Diffstat (limited to 'fs/ocfs2')
-rw-r--r-- | fs/ocfs2/journal.h | 14 | ||||
-rw-r--r-- | fs/ocfs2/refcounttree.c | 345 |
2 files changed, 353 insertions, 6 deletions
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index b2dc125c6e9a..bd88c8b9f2fb 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h | |||
@@ -490,6 +490,20 @@ static inline int ocfs2_calc_dxi_expand_credits(struct super_block *sb) | |||
490 | return credits; | 490 | return credits; |
491 | } | 491 | } |
492 | 492 | ||
493 | /* inode update, new refcount block and its allocation credits. */ | ||
494 | #define OCFS2_REFCOUNT_TREE_CREATE_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1 \ | ||
495 | + OCFS2_SUBALLOC_ALLOC) | ||
496 | |||
497 | /* inode and the refcount block update. */ | ||
498 | #define OCFS2_REFCOUNT_TREE_SET_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1) | ||
499 | |||
500 | /* | ||
501 | * inode and the refcount block update. | ||
502 | * It doesn't include the credits for sub alloc change. | ||
503 | * So if we need to free the bit, OCFS2_SUBALLOC_FREE needs to be added. | ||
504 | */ | ||
505 | #define OCFS2_REFCOUNT_TREE_REMOVE_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1) | ||
506 | |||
493 | /* | 507 | /* |
494 | * Please note that the caller must make sure that root_el is the root | 508 | * Please note that the caller must make sure that root_el is the root |
495 | * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise | 509 | * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise |
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 8d79de8637b8..d0d6fa312b01 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "buffer_head_io.h" | 27 | #include "buffer_head_io.h" |
28 | #include "blockcheck.h" | 28 | #include "blockcheck.h" |
29 | #include "refcounttree.h" | 29 | #include "refcounttree.h" |
30 | #include "sysfile.h" | ||
30 | #include "dlmglue.h" | 31 | #include "dlmglue.h" |
31 | 32 | ||
32 | static inline struct ocfs2_refcount_tree * | 33 | static inline struct ocfs2_refcount_tree * |
@@ -272,6 +273,22 @@ static inline void ocfs2_init_refcount_tree_lock(struct ocfs2_super *osb, | |||
272 | rf_blkno, generation); | 273 | rf_blkno, generation); |
273 | } | 274 | } |
274 | 275 | ||
276 | static struct ocfs2_refcount_tree* | ||
277 | ocfs2_allocate_refcount_tree(struct ocfs2_super *osb, u64 rf_blkno) | ||
278 | { | ||
279 | struct ocfs2_refcount_tree *new; | ||
280 | |||
281 | new = kzalloc(sizeof(struct ocfs2_refcount_tree), GFP_NOFS); | ||
282 | if (!new) | ||
283 | return NULL; | ||
284 | |||
285 | new->rf_blkno = rf_blkno; | ||
286 | kref_init(&new->rf_getcnt); | ||
287 | ocfs2_init_refcount_tree_ci(new, osb->sb); | ||
288 | |||
289 | return new; | ||
290 | } | ||
291 | |||
275 | static int ocfs2_get_refcount_tree(struct ocfs2_super *osb, u64 rf_blkno, | 292 | static int ocfs2_get_refcount_tree(struct ocfs2_super *osb, u64 rf_blkno, |
276 | struct ocfs2_refcount_tree **ret_tree) | 293 | struct ocfs2_refcount_tree **ret_tree) |
277 | { | 294 | { |
@@ -291,16 +308,12 @@ static int ocfs2_get_refcount_tree(struct ocfs2_super *osb, u64 rf_blkno, | |||
291 | 308 | ||
292 | spin_unlock(&osb->osb_lock); | 309 | spin_unlock(&osb->osb_lock); |
293 | 310 | ||
294 | new = kzalloc(sizeof(struct ocfs2_refcount_tree), GFP_NOFS); | 311 | new = ocfs2_allocate_refcount_tree(osb, rf_blkno); |
295 | if (!new) { | 312 | if (!new) { |
296 | ret = -ENOMEM; | 313 | ret = -ENOMEM; |
314 | mlog_errno(ret); | ||
297 | return ret; | 315 | return ret; |
298 | } | 316 | } |
299 | |||
300 | new->rf_blkno = rf_blkno; | ||
301 | kref_init(&new->rf_getcnt); | ||
302 | ocfs2_init_refcount_tree_ci(new, osb->sb); | ||
303 | |||
304 | /* | 317 | /* |
305 | * We need the generation to create the refcount tree lock and since | 318 | * We need the generation to create the refcount tree lock and since |
306 | * it isn't changed during the tree modification, we are safe here to | 319 | * it isn't changed during the tree modification, we are safe here to |
@@ -515,3 +528,323 @@ void ocfs2_purge_refcount_trees(struct ocfs2_super *osb) | |||
515 | ocfs2_free_refcount_tree(tree); | 528 | ocfs2_free_refcount_tree(tree); |
516 | } | 529 | } |
517 | } | 530 | } |
531 | |||
532 | /* | ||
533 | * Create a refcount tree for an inode. | ||
534 | * We take for granted that the inode is already locked. | ||
535 | */ | ||
536 | static int ocfs2_create_refcount_tree(struct inode *inode, | ||
537 | struct buffer_head *di_bh) | ||
538 | { | ||
539 | int ret; | ||
540 | handle_t *handle = NULL; | ||
541 | struct ocfs2_alloc_context *meta_ac = NULL; | ||
542 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; | ||
543 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
544 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
545 | struct buffer_head *new_bh = NULL; | ||
546 | struct ocfs2_refcount_block *rb; | ||
547 | struct ocfs2_refcount_tree *new_tree = NULL, *tree = NULL; | ||
548 | u16 suballoc_bit_start; | ||
549 | u32 num_got; | ||
550 | u64 first_blkno; | ||
551 | |||
552 | BUG_ON(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL); | ||
553 | |||
554 | mlog(0, "create tree for inode %lu\n", inode->i_ino); | ||
555 | |||
556 | ret = ocfs2_reserve_new_metadata_blocks(osb, 1, &meta_ac); | ||
557 | if (ret) { | ||
558 | mlog_errno(ret); | ||
559 | goto out; | ||
560 | } | ||
561 | |||
562 | handle = ocfs2_start_trans(osb, OCFS2_REFCOUNT_TREE_CREATE_CREDITS); | ||
563 | if (IS_ERR(handle)) { | ||
564 | ret = PTR_ERR(handle); | ||
565 | mlog_errno(ret); | ||
566 | goto out; | ||
567 | } | ||
568 | |||
569 | ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh, | ||
570 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
571 | if (ret) { | ||
572 | mlog_errno(ret); | ||
573 | goto out_commit; | ||
574 | } | ||
575 | |||
576 | ret = ocfs2_claim_metadata(osb, handle, meta_ac, 1, | ||
577 | &suballoc_bit_start, &num_got, | ||
578 | &first_blkno); | ||
579 | if (ret) { | ||
580 | mlog_errno(ret); | ||
581 | goto out_commit; | ||
582 | } | ||
583 | |||
584 | new_tree = ocfs2_allocate_refcount_tree(osb, first_blkno); | ||
585 | if (!new_tree) { | ||
586 | ret = -ENOMEM; | ||
587 | mlog_errno(ret); | ||
588 | goto out_commit; | ||
589 | } | ||
590 | |||
591 | new_bh = sb_getblk(inode->i_sb, first_blkno); | ||
592 | ocfs2_set_new_buffer_uptodate(&new_tree->rf_ci, new_bh); | ||
593 | |||
594 | ret = ocfs2_journal_access_rb(handle, &new_tree->rf_ci, new_bh, | ||
595 | OCFS2_JOURNAL_ACCESS_CREATE); | ||
596 | if (ret) { | ||
597 | mlog_errno(ret); | ||
598 | goto out_commit; | ||
599 | } | ||
600 | |||
601 | /* Initialize ocfs2_refcount_block. */ | ||
602 | rb = (struct ocfs2_refcount_block *)new_bh->b_data; | ||
603 | memset(rb, 0, inode->i_sb->s_blocksize); | ||
604 | strcpy((void *)rb, OCFS2_REFCOUNT_BLOCK_SIGNATURE); | ||
605 | rb->rf_suballoc_slot = cpu_to_le16(osb->slot_num); | ||
606 | rb->rf_suballoc_bit = cpu_to_le16(suballoc_bit_start); | ||
607 | rb->rf_fs_generation = cpu_to_le32(osb->fs_generation); | ||
608 | rb->rf_blkno = cpu_to_le64(first_blkno); | ||
609 | rb->rf_count = cpu_to_le32(1); | ||
610 | rb->rf_records.rl_count = | ||
611 | cpu_to_le16(ocfs2_refcount_recs_per_rb(osb->sb)); | ||
612 | spin_lock(&osb->osb_lock); | ||
613 | rb->rf_generation = osb->s_next_generation++; | ||
614 | spin_unlock(&osb->osb_lock); | ||
615 | |||
616 | ocfs2_journal_dirty(handle, new_bh); | ||
617 | |||
618 | spin_lock(&oi->ip_lock); | ||
619 | oi->ip_dyn_features |= OCFS2_HAS_REFCOUNT_FL; | ||
620 | di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); | ||
621 | di->i_refcount_loc = cpu_to_le64(first_blkno); | ||
622 | spin_unlock(&oi->ip_lock); | ||
623 | |||
624 | mlog(0, "created tree for inode %lu, refblock %llu\n", | ||
625 | inode->i_ino, (unsigned long long)first_blkno); | ||
626 | |||
627 | ocfs2_journal_dirty(handle, di_bh); | ||
628 | |||
629 | /* | ||
630 | * We have to init the tree lock here since it will use | ||
631 | * the generation number to create it. | ||
632 | */ | ||
633 | new_tree->rf_generation = le32_to_cpu(rb->rf_generation); | ||
634 | ocfs2_init_refcount_tree_lock(osb, new_tree, first_blkno, | ||
635 | new_tree->rf_generation); | ||
636 | |||
637 | spin_lock(&osb->osb_lock); | ||
638 | tree = ocfs2_find_refcount_tree(osb, first_blkno); | ||
639 | |||
640 | /* | ||
641 | * We've just created a new refcount tree in this block. If | ||
642 | * we found a refcount tree on the ocfs2_super, it must be | ||
643 | * one we just deleted. We free the old tree before | ||
644 | * inserting the new tree. | ||
645 | */ | ||
646 | BUG_ON(tree && tree->rf_generation == new_tree->rf_generation); | ||
647 | if (tree) | ||
648 | ocfs2_erase_refcount_tree_from_list_no_lock(osb, tree); | ||
649 | ocfs2_insert_refcount_tree(osb, new_tree); | ||
650 | spin_unlock(&osb->osb_lock); | ||
651 | new_tree = NULL; | ||
652 | if (tree) | ||
653 | ocfs2_refcount_tree_put(tree); | ||
654 | |||
655 | out_commit: | ||
656 | ocfs2_commit_trans(osb, handle); | ||
657 | |||
658 | out: | ||
659 | if (new_tree) { | ||
660 | ocfs2_metadata_cache_exit(&new_tree->rf_ci); | ||
661 | kfree(new_tree); | ||
662 | } | ||
663 | |||
664 | brelse(new_bh); | ||
665 | if (meta_ac) | ||
666 | ocfs2_free_alloc_context(meta_ac); | ||
667 | |||
668 | return ret; | ||
669 | } | ||
670 | |||
671 | static int ocfs2_set_refcount_tree(struct inode *inode, | ||
672 | struct buffer_head *di_bh, | ||
673 | u64 refcount_loc) | ||
674 | { | ||
675 | int ret; | ||
676 | handle_t *handle = NULL; | ||
677 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; | ||
678 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
679 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
680 | struct buffer_head *ref_root_bh = NULL; | ||
681 | struct ocfs2_refcount_block *rb; | ||
682 | struct ocfs2_refcount_tree *ref_tree; | ||
683 | |||
684 | BUG_ON(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL); | ||
685 | |||
686 | ret = ocfs2_lock_refcount_tree(osb, refcount_loc, 1, | ||
687 | &ref_tree, &ref_root_bh); | ||
688 | if (ret) { | ||
689 | mlog_errno(ret); | ||
690 | return ret; | ||
691 | } | ||
692 | |||
693 | handle = ocfs2_start_trans(osb, OCFS2_REFCOUNT_TREE_SET_CREDITS); | ||
694 | if (IS_ERR(handle)) { | ||
695 | ret = PTR_ERR(handle); | ||
696 | mlog_errno(ret); | ||
697 | goto out; | ||
698 | } | ||
699 | |||
700 | ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh, | ||
701 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
702 | if (ret) { | ||
703 | mlog_errno(ret); | ||
704 | goto out_commit; | ||
705 | } | ||
706 | |||
707 | ret = ocfs2_journal_access_rb(handle, &ref_tree->rf_ci, ref_root_bh, | ||
708 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
709 | if (ret) { | ||
710 | mlog_errno(ret); | ||
711 | goto out_commit; | ||
712 | } | ||
713 | |||
714 | rb = (struct ocfs2_refcount_block *)ref_root_bh->b_data; | ||
715 | le32_add_cpu(&rb->rf_count, 1); | ||
716 | |||
717 | ocfs2_journal_dirty(handle, ref_root_bh); | ||
718 | |||
719 | spin_lock(&oi->ip_lock); | ||
720 | oi->ip_dyn_features |= OCFS2_HAS_REFCOUNT_FL; | ||
721 | di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); | ||
722 | di->i_refcount_loc = cpu_to_le64(refcount_loc); | ||
723 | spin_unlock(&oi->ip_lock); | ||
724 | ocfs2_journal_dirty(handle, di_bh); | ||
725 | |||
726 | out_commit: | ||
727 | ocfs2_commit_trans(osb, handle); | ||
728 | out: | ||
729 | ocfs2_unlock_refcount_tree(osb, ref_tree, 1); | ||
730 | brelse(ref_root_bh); | ||
731 | |||
732 | return ret; | ||
733 | } | ||
734 | |||
735 | int ocfs2_remove_refcount_tree(struct inode *inode, struct buffer_head *di_bh) | ||
736 | { | ||
737 | int ret, delete_tree = 0; | ||
738 | handle_t *handle = NULL; | ||
739 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; | ||
740 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
741 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
742 | struct ocfs2_refcount_block *rb; | ||
743 | struct inode *alloc_inode = NULL; | ||
744 | struct buffer_head *alloc_bh = NULL; | ||
745 | struct buffer_head *blk_bh = NULL; | ||
746 | struct ocfs2_refcount_tree *ref_tree; | ||
747 | int credits = OCFS2_REFCOUNT_TREE_REMOVE_CREDITS; | ||
748 | u64 blk = 0, bg_blkno = 0, ref_blkno = le64_to_cpu(di->i_refcount_loc); | ||
749 | u16 bit = 0; | ||
750 | |||
751 | if (!(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL)) | ||
752 | return 0; | ||
753 | |||
754 | BUG_ON(!ref_blkno); | ||
755 | ret = ocfs2_lock_refcount_tree(osb, ref_blkno, 1, &ref_tree, &blk_bh); | ||
756 | if (ret) { | ||
757 | mlog_errno(ret); | ||
758 | return ret; | ||
759 | } | ||
760 | |||
761 | rb = (struct ocfs2_refcount_block *)blk_bh->b_data; | ||
762 | |||
763 | /* | ||
764 | * If we are the last user, we need to free the block. | ||
765 | * So lock the allocator ahead. | ||
766 | */ | ||
767 | if (le32_to_cpu(rb->rf_count) == 1) { | ||
768 | blk = le64_to_cpu(rb->rf_blkno); | ||
769 | bit = le16_to_cpu(rb->rf_suballoc_bit); | ||
770 | bg_blkno = ocfs2_which_suballoc_group(blk, bit); | ||
771 | |||
772 | alloc_inode = ocfs2_get_system_file_inode(osb, | ||
773 | EXTENT_ALLOC_SYSTEM_INODE, | ||
774 | le16_to_cpu(rb->rf_suballoc_slot)); | ||
775 | if (!alloc_inode) { | ||
776 | ret = -ENOMEM; | ||
777 | mlog_errno(ret); | ||
778 | goto out; | ||
779 | } | ||
780 | mutex_lock(&alloc_inode->i_mutex); | ||
781 | |||
782 | ret = ocfs2_inode_lock(alloc_inode, &alloc_bh, 1); | ||
783 | if (ret) { | ||
784 | mlog_errno(ret); | ||
785 | goto out_mutex; | ||
786 | } | ||
787 | |||
788 | credits += OCFS2_SUBALLOC_FREE; | ||
789 | } | ||
790 | |||
791 | handle = ocfs2_start_trans(osb, credits); | ||
792 | if (IS_ERR(handle)) { | ||
793 | ret = PTR_ERR(handle); | ||
794 | mlog_errno(ret); | ||
795 | goto out_unlock; | ||
796 | } | ||
797 | |||
798 | ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh, | ||
799 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
800 | if (ret) { | ||
801 | mlog_errno(ret); | ||
802 | goto out_commit; | ||
803 | } | ||
804 | |||
805 | ret = ocfs2_journal_access_rb(handle, &ref_tree->rf_ci, blk_bh, | ||
806 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
807 | if (ret) { | ||
808 | mlog_errno(ret); | ||
809 | goto out_commit; | ||
810 | } | ||
811 | |||
812 | spin_lock(&oi->ip_lock); | ||
813 | oi->ip_dyn_features &= ~OCFS2_HAS_REFCOUNT_FL; | ||
814 | di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); | ||
815 | di->i_refcount_loc = 0; | ||
816 | spin_unlock(&oi->ip_lock); | ||
817 | ocfs2_journal_dirty(handle, di_bh); | ||
818 | |||
819 | le32_add_cpu(&rb->rf_count , -1); | ||
820 | ocfs2_journal_dirty(handle, blk_bh); | ||
821 | |||
822 | if (!rb->rf_count) { | ||
823 | delete_tree = 1; | ||
824 | ocfs2_erase_refcount_tree_from_list(osb, ref_tree); | ||
825 | ret = ocfs2_free_suballoc_bits(handle, alloc_inode, | ||
826 | alloc_bh, bit, bg_blkno, 1); | ||
827 | if (ret) | ||
828 | mlog_errno(ret); | ||
829 | } | ||
830 | |||
831 | out_commit: | ||
832 | ocfs2_commit_trans(osb, handle); | ||
833 | out_unlock: | ||
834 | if (alloc_inode) { | ||
835 | ocfs2_inode_unlock(alloc_inode, 1); | ||
836 | brelse(alloc_bh); | ||
837 | } | ||
838 | out_mutex: | ||
839 | if (alloc_inode) { | ||
840 | mutex_unlock(&alloc_inode->i_mutex); | ||
841 | iput(alloc_inode); | ||
842 | } | ||
843 | out: | ||
844 | ocfs2_unlock_refcount_tree(osb, ref_tree, 1); | ||
845 | if (delete_tree) | ||
846 | ocfs2_refcount_tree_put(ref_tree); | ||
847 | brelse(blk_bh); | ||
848 | |||
849 | return ret; | ||
850 | } | ||