diff options
author | Christoph Hellwig <hch@infradead.org> | 2008-10-30 01:57:40 -0400 |
---|---|---|
committer | Lachlan McIlroy <lachlan@sgi.com> | 2008-10-30 01:57:40 -0400 |
commit | 4b22a57188d87e873346b73c227607715be96399 (patch) | |
tree | 4cf2d0deede695968b7a32b098c0c64fac8610e5 /fs/xfs/xfs_alloc_btree.c | |
parent | ea77b0a66e6c910ef265d9af522d6303ea6b3055 (diff) |
[XFS] implement generic xfs_btree_insert/insrec
Make the btree insert code generic. Based on a patch from David Chinner
with lots of changes to follow the original btree implementations more
closely. While this loses some of the generic helper routines for
inserting/moving/removing records it also solves some of the one off bugs
in the original code and makes it easier to verify.
SGI-PV: 985583
SGI-Modid: xfs-linux-melb:xfs-kern:32202a
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Signed-off-by: Bill O'Donnell <billodo@sgi.com>
Signed-off-by: David Chinner <david@fromorbit.com>
Diffstat (limited to 'fs/xfs/xfs_alloc_btree.c')
-rw-r--r-- | fs/xfs/xfs_alloc_btree.c | 339 |
1 files changed, 30 insertions, 309 deletions
diff --git a/fs/xfs/xfs_alloc_btree.c b/fs/xfs/xfs_alloc_btree.c index f21a3e9cc3db..818adca77fc6 100644 --- a/fs/xfs/xfs_alloc_btree.c +++ b/fs/xfs/xfs_alloc_btree.c | |||
@@ -583,256 +583,6 @@ error0: | |||
583 | } | 583 | } |
584 | 584 | ||
585 | /* | 585 | /* |
586 | * Insert one record/level. Return information to the caller | ||
587 | * allowing the next level up to proceed if necessary. | ||
588 | */ | ||
589 | STATIC int /* error */ | ||
590 | xfs_alloc_insrec( | ||
591 | xfs_btree_cur_t *cur, /* btree cursor */ | ||
592 | int level, /* level to insert record at */ | ||
593 | xfs_agblock_t *bnop, /* i/o: block number inserted */ | ||
594 | xfs_alloc_rec_t *recp, /* i/o: record data inserted */ | ||
595 | xfs_btree_cur_t **curp, /* output: new cursor replacing cur */ | ||
596 | int *stat) /* output: success/failure */ | ||
597 | { | ||
598 | xfs_agf_t *agf; /* allocation group freelist header */ | ||
599 | xfs_alloc_block_t *block; /* btree block record/key lives in */ | ||
600 | xfs_buf_t *bp; /* buffer for block */ | ||
601 | int error; /* error return value */ | ||
602 | int i; /* loop index */ | ||
603 | xfs_alloc_key_t key; /* key value being inserted */ | ||
604 | xfs_alloc_key_t *kp; /* pointer to btree keys */ | ||
605 | xfs_agblock_t nbno; /* block number of allocated block */ | ||
606 | xfs_btree_cur_t *ncur; /* new cursor to be used at next lvl */ | ||
607 | xfs_alloc_key_t nkey; /* new key value, from split */ | ||
608 | xfs_alloc_rec_t nrec; /* new record value, for caller */ | ||
609 | int numrecs; | ||
610 | int optr; /* old ptr value */ | ||
611 | xfs_alloc_ptr_t *pp; /* pointer to btree addresses */ | ||
612 | int ptr; /* index in btree block for this rec */ | ||
613 | xfs_alloc_rec_t *rp; /* pointer to btree records */ | ||
614 | |||
615 | ASSERT(be32_to_cpu(recp->ar_blockcount) > 0); | ||
616 | |||
617 | /* | ||
618 | * GCC doesn't understand the (arguably complex) control flow in | ||
619 | * this function and complains about uninitialized structure fields | ||
620 | * without this. | ||
621 | */ | ||
622 | memset(&nrec, 0, sizeof(nrec)); | ||
623 | |||
624 | /* | ||
625 | * If we made it to the root level, allocate a new root block | ||
626 | * and we're done. | ||
627 | */ | ||
628 | if (level >= cur->bc_nlevels) { | ||
629 | XFS_STATS_INC(xs_abt_insrec); | ||
630 | if ((error = xfs_btree_new_root(cur, &i))) | ||
631 | return error; | ||
632 | *bnop = NULLAGBLOCK; | ||
633 | *stat = i; | ||
634 | return 0; | ||
635 | } | ||
636 | /* | ||
637 | * Make a key out of the record data to be inserted, and save it. | ||
638 | */ | ||
639 | key.ar_startblock = recp->ar_startblock; | ||
640 | key.ar_blockcount = recp->ar_blockcount; | ||
641 | optr = ptr = cur->bc_ptrs[level]; | ||
642 | /* | ||
643 | * If we're off the left edge, return failure. | ||
644 | */ | ||
645 | if (ptr == 0) { | ||
646 | *stat = 0; | ||
647 | return 0; | ||
648 | } | ||
649 | XFS_STATS_INC(xs_abt_insrec); | ||
650 | /* | ||
651 | * Get pointers to the btree buffer and block. | ||
652 | */ | ||
653 | bp = cur->bc_bufs[level]; | ||
654 | block = XFS_BUF_TO_ALLOC_BLOCK(bp); | ||
655 | numrecs = be16_to_cpu(block->bb_numrecs); | ||
656 | #ifdef DEBUG | ||
657 | if ((error = xfs_btree_check_sblock(cur, block, level, bp))) | ||
658 | return error; | ||
659 | /* | ||
660 | * Check that the new entry is being inserted in the right place. | ||
661 | */ | ||
662 | if (ptr <= numrecs) { | ||
663 | if (level == 0) { | ||
664 | rp = XFS_ALLOC_REC_ADDR(block, ptr, cur); | ||
665 | xfs_btree_check_rec(cur->bc_btnum, recp, rp); | ||
666 | } else { | ||
667 | kp = XFS_ALLOC_KEY_ADDR(block, ptr, cur); | ||
668 | xfs_btree_check_key(cur->bc_btnum, &key, kp); | ||
669 | } | ||
670 | } | ||
671 | #endif | ||
672 | nbno = NULLAGBLOCK; | ||
673 | ncur = NULL; | ||
674 | /* | ||
675 | * If the block is full, we can't insert the new entry until we | ||
676 | * make the block un-full. | ||
677 | */ | ||
678 | if (numrecs == XFS_ALLOC_BLOCK_MAXRECS(level, cur)) { | ||
679 | /* | ||
680 | * First, try shifting an entry to the right neighbor. | ||
681 | */ | ||
682 | if ((error = xfs_btree_rshift(cur, level, &i))) | ||
683 | return error; | ||
684 | if (i) { | ||
685 | /* nothing */ | ||
686 | } | ||
687 | /* | ||
688 | * Next, try shifting an entry to the left neighbor. | ||
689 | */ | ||
690 | else { | ||
691 | if ((error = xfs_btree_lshift(cur, level, &i))) | ||
692 | return error; | ||
693 | if (i) | ||
694 | optr = ptr = cur->bc_ptrs[level]; | ||
695 | else { | ||
696 | union xfs_btree_ptr bno = { .s = cpu_to_be32(nbno) }; | ||
697 | /* | ||
698 | * Next, try splitting the current block in | ||
699 | * half. If this works we have to re-set our | ||
700 | * variables because we could be in a | ||
701 | * different block now. | ||
702 | */ | ||
703 | if ((error = xfs_btree_split(cur, level, &bno, | ||
704 | (union xfs_btree_key *)&nkey, | ||
705 | &ncur, &i))) | ||
706 | return error; | ||
707 | nbno = be32_to_cpu(bno.s); | ||
708 | if (i) { | ||
709 | bp = cur->bc_bufs[level]; | ||
710 | block = XFS_BUF_TO_ALLOC_BLOCK(bp); | ||
711 | #ifdef DEBUG | ||
712 | if ((error = | ||
713 | xfs_btree_check_sblock(cur, | ||
714 | block, level, bp))) | ||
715 | return error; | ||
716 | #endif | ||
717 | ptr = cur->bc_ptrs[level]; | ||
718 | nrec.ar_startblock = nkey.ar_startblock; | ||
719 | nrec.ar_blockcount = nkey.ar_blockcount; | ||
720 | } | ||
721 | /* | ||
722 | * Otherwise the insert fails. | ||
723 | */ | ||
724 | else { | ||
725 | *stat = 0; | ||
726 | return 0; | ||
727 | } | ||
728 | } | ||
729 | } | ||
730 | } | ||
731 | /* | ||
732 | * At this point we know there's room for our new entry in the block | ||
733 | * we're pointing at. | ||
734 | */ | ||
735 | numrecs = be16_to_cpu(block->bb_numrecs); | ||
736 | if (level > 0) { | ||
737 | /* | ||
738 | * It's a non-leaf entry. Make a hole for the new data | ||
739 | * in the key and ptr regions of the block. | ||
740 | */ | ||
741 | kp = XFS_ALLOC_KEY_ADDR(block, 1, cur); | ||
742 | pp = XFS_ALLOC_PTR_ADDR(block, 1, cur); | ||
743 | #ifdef DEBUG | ||
744 | for (i = numrecs; i >= ptr; i--) { | ||
745 | if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(pp[i - 1]), level))) | ||
746 | return error; | ||
747 | } | ||
748 | #endif | ||
749 | memmove(&kp[ptr], &kp[ptr - 1], | ||
750 | (numrecs - ptr + 1) * sizeof(*kp)); | ||
751 | memmove(&pp[ptr], &pp[ptr - 1], | ||
752 | (numrecs - ptr + 1) * sizeof(*pp)); | ||
753 | #ifdef DEBUG | ||
754 | if ((error = xfs_btree_check_sptr(cur, *bnop, level))) | ||
755 | return error; | ||
756 | #endif | ||
757 | /* | ||
758 | * Now stuff the new data in, bump numrecs and log the new data. | ||
759 | */ | ||
760 | kp[ptr - 1] = key; | ||
761 | pp[ptr - 1] = cpu_to_be32(*bnop); | ||
762 | numrecs++; | ||
763 | block->bb_numrecs = cpu_to_be16(numrecs); | ||
764 | xfs_alloc_log_keys(cur, bp, ptr, numrecs); | ||
765 | xfs_alloc_log_ptrs(cur, bp, ptr, numrecs); | ||
766 | #ifdef DEBUG | ||
767 | if (ptr < numrecs) | ||
768 | xfs_btree_check_key(cur->bc_btnum, kp + ptr - 1, | ||
769 | kp + ptr); | ||
770 | #endif | ||
771 | } else { | ||
772 | /* | ||
773 | * It's a leaf entry. Make a hole for the new record. | ||
774 | */ | ||
775 | rp = XFS_ALLOC_REC_ADDR(block, 1, cur); | ||
776 | memmove(&rp[ptr], &rp[ptr - 1], | ||
777 | (numrecs - ptr + 1) * sizeof(*rp)); | ||
778 | /* | ||
779 | * Now stuff the new record in, bump numrecs | ||
780 | * and log the new data. | ||
781 | */ | ||
782 | rp[ptr - 1] = *recp; | ||
783 | numrecs++; | ||
784 | block->bb_numrecs = cpu_to_be16(numrecs); | ||
785 | xfs_alloc_log_recs(cur, bp, ptr, numrecs); | ||
786 | #ifdef DEBUG | ||
787 | if (ptr < numrecs) | ||
788 | xfs_btree_check_rec(cur->bc_btnum, rp + ptr - 1, | ||
789 | rp + ptr); | ||
790 | #endif | ||
791 | } | ||
792 | /* | ||
793 | * Log the new number of records in the btree header. | ||
794 | */ | ||
795 | xfs_alloc_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS); | ||
796 | /* | ||
797 | * If we inserted at the start of a block, update the parents' keys. | ||
798 | */ | ||
799 | if (optr == 1 && (error = xfs_btree_updkey(cur, (union xfs_btree_key *)&key, level + 1))) | ||
800 | return error; | ||
801 | /* | ||
802 | * Look to see if the longest extent in the allocation group | ||
803 | * needs to be updated. | ||
804 | */ | ||
805 | |||
806 | agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp); | ||
807 | if (level == 0 && | ||
808 | cur->bc_btnum == XFS_BTNUM_CNT && | ||
809 | be32_to_cpu(block->bb_rightsib) == NULLAGBLOCK && | ||
810 | be32_to_cpu(recp->ar_blockcount) > be32_to_cpu(agf->agf_longest)) { | ||
811 | /* | ||
812 | * If this is a leaf in the by-size btree and there | ||
813 | * is no right sibling block and this block is bigger | ||
814 | * than the previous longest block, update it. | ||
815 | */ | ||
816 | agf->agf_longest = recp->ar_blockcount; | ||
817 | cur->bc_mp->m_perag[be32_to_cpu(agf->agf_seqno)].pagf_longest | ||
818 | = be32_to_cpu(recp->ar_blockcount); | ||
819 | xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp, | ||
820 | XFS_AGF_LONGEST); | ||
821 | } | ||
822 | /* | ||
823 | * Return the new block number, if any. | ||
824 | * If there is one, give back a record value and a cursor too. | ||
825 | */ | ||
826 | *bnop = nbno; | ||
827 | if (nbno != NULLAGBLOCK) { | ||
828 | *recp = nrec; | ||
829 | *curp = ncur; | ||
830 | } | ||
831 | *stat = 1; | ||
832 | return 0; | ||
833 | } | ||
834 | |||
835 | /* | ||
836 | * Log header fields from a btree block. | 586 | * Log header fields from a btree block. |
837 | */ | 587 | */ |
838 | STATIC void | 588 | STATIC void |
@@ -1019,65 +769,6 @@ xfs_alloc_get_rec( | |||
1019 | return 0; | 769 | return 0; |
1020 | } | 770 | } |
1021 | 771 | ||
1022 | /* | ||
1023 | * Insert the current record at the point referenced by cur. | ||
1024 | * The cursor may be inconsistent on return if splits have been done. | ||
1025 | */ | ||
1026 | int /* error */ | ||
1027 | xfs_alloc_insert( | ||
1028 | xfs_btree_cur_t *cur, /* btree cursor */ | ||
1029 | int *stat) /* success/failure */ | ||
1030 | { | ||
1031 | int error; /* error return value */ | ||
1032 | int i; /* result value, 0 for failure */ | ||
1033 | int level; /* current level number in btree */ | ||
1034 | xfs_agblock_t nbno; /* new block number (split result) */ | ||
1035 | xfs_btree_cur_t *ncur; /* new cursor (split result) */ | ||
1036 | xfs_alloc_rec_t nrec; /* record being inserted this level */ | ||
1037 | xfs_btree_cur_t *pcur; /* previous level's cursor */ | ||
1038 | |||
1039 | level = 0; | ||
1040 | nbno = NULLAGBLOCK; | ||
1041 | nrec.ar_startblock = cpu_to_be32(cur->bc_rec.a.ar_startblock); | ||
1042 | nrec.ar_blockcount = cpu_to_be32(cur->bc_rec.a.ar_blockcount); | ||
1043 | ncur = NULL; | ||
1044 | pcur = cur; | ||
1045 | /* | ||
1046 | * Loop going up the tree, starting at the leaf level. | ||
1047 | * Stop when we don't get a split block, that must mean that | ||
1048 | * the insert is finished with this level. | ||
1049 | */ | ||
1050 | do { | ||
1051 | /* | ||
1052 | * Insert nrec/nbno into this level of the tree. | ||
1053 | * Note if we fail, nbno will be null. | ||
1054 | */ | ||
1055 | if ((error = xfs_alloc_insrec(pcur, level++, &nbno, &nrec, &ncur, | ||
1056 | &i))) { | ||
1057 | if (pcur != cur) | ||
1058 | xfs_btree_del_cursor(pcur, XFS_BTREE_ERROR); | ||
1059 | return error; | ||
1060 | } | ||
1061 | /* | ||
1062 | * See if the cursor we just used is trash. | ||
1063 | * Can't trash the caller's cursor, but otherwise we should | ||
1064 | * if ncur is a new cursor or we're about to be done. | ||
1065 | */ | ||
1066 | if (pcur != cur && (ncur || nbno == NULLAGBLOCK)) { | ||
1067 | cur->bc_nlevels = pcur->bc_nlevels; | ||
1068 | xfs_btree_del_cursor(pcur, XFS_BTREE_NOERROR); | ||
1069 | } | ||
1070 | /* | ||
1071 | * If we got a new cursor, switch to it. | ||
1072 | */ | ||
1073 | if (ncur) { | ||
1074 | pcur = ncur; | ||
1075 | ncur = NULL; | ||
1076 | } | ||
1077 | } while (nbno != NULLAGBLOCK); | ||
1078 | *stat = i; | ||
1079 | return 0; | ||
1080 | } | ||
1081 | 772 | ||
1082 | STATIC struct xfs_btree_cur * | 773 | STATIC struct xfs_btree_cur * |
1083 | xfs_allocbt_dup_cursor( | 774 | xfs_allocbt_dup_cursor( |
@@ -1170,6 +861,12 @@ xfs_allocbt_update_lastrec( | |||
1170 | return; | 861 | return; |
1171 | len = rec->alloc.ar_blockcount; | 862 | len = rec->alloc.ar_blockcount; |
1172 | break; | 863 | break; |
864 | case LASTREC_INSREC: | ||
865 | if (be32_to_cpu(rec->alloc.ar_blockcount) <= | ||
866 | be32_to_cpu(agf->agf_longest)) | ||
867 | return; | ||
868 | len = rec->alloc.ar_blockcount; | ||
869 | break; | ||
1173 | default: | 870 | default: |
1174 | ASSERT(0); | 871 | ASSERT(0); |
1175 | return; | 872 | return; |
@@ -1200,6 +897,28 @@ xfs_allocbt_init_key_from_rec( | |||
1200 | } | 897 | } |
1201 | 898 | ||
1202 | STATIC void | 899 | STATIC void |
900 | xfs_allocbt_init_rec_from_key( | ||
901 | union xfs_btree_key *key, | ||
902 | union xfs_btree_rec *rec) | ||
903 | { | ||
904 | ASSERT(key->alloc.ar_startblock != 0); | ||
905 | |||
906 | rec->alloc.ar_startblock = key->alloc.ar_startblock; | ||
907 | rec->alloc.ar_blockcount = key->alloc.ar_blockcount; | ||
908 | } | ||
909 | |||
910 | STATIC void | ||
911 | xfs_allocbt_init_rec_from_cur( | ||
912 | struct xfs_btree_cur *cur, | ||
913 | union xfs_btree_rec *rec) | ||
914 | { | ||
915 | ASSERT(cur->bc_rec.a.ar_startblock != 0); | ||
916 | |||
917 | rec->alloc.ar_startblock = cpu_to_be32(cur->bc_rec.a.ar_startblock); | ||
918 | rec->alloc.ar_blockcount = cpu_to_be32(cur->bc_rec.a.ar_blockcount); | ||
919 | } | ||
920 | |||
921 | STATIC void | ||
1203 | xfs_allocbt_init_ptr_from_cur( | 922 | xfs_allocbt_init_ptr_from_cur( |
1204 | struct xfs_btree_cur *cur, | 923 | struct xfs_btree_cur *cur, |
1205 | union xfs_btree_ptr *ptr) | 924 | union xfs_btree_ptr *ptr) |
@@ -1309,6 +1028,8 @@ static const struct xfs_btree_ops xfs_allocbt_ops = { | |||
1309 | .update_lastrec = xfs_allocbt_update_lastrec, | 1028 | .update_lastrec = xfs_allocbt_update_lastrec, |
1310 | .get_maxrecs = xfs_allocbt_get_maxrecs, | 1029 | .get_maxrecs = xfs_allocbt_get_maxrecs, |
1311 | .init_key_from_rec = xfs_allocbt_init_key_from_rec, | 1030 | .init_key_from_rec = xfs_allocbt_init_key_from_rec, |
1031 | .init_rec_from_key = xfs_allocbt_init_rec_from_key, | ||
1032 | .init_rec_from_cur = xfs_allocbt_init_rec_from_cur, | ||
1312 | .init_ptr_from_cur = xfs_allocbt_init_ptr_from_cur, | 1033 | .init_ptr_from_cur = xfs_allocbt_init_ptr_from_cur, |
1313 | .key_diff = xfs_allocbt_key_diff, | 1034 | .key_diff = xfs_allocbt_key_diff, |
1314 | 1035 | ||