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_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_btree.c')
-rw-r--r-- | fs/xfs/xfs_btree.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c index 3b6e01dea669..36477aae77df 100644 --- a/fs/xfs/xfs_btree.c +++ b/fs/xfs/xfs_btree.c | |||
@@ -963,6 +963,17 @@ xfs_btree_ptr_is_null( | |||
963 | return be32_to_cpu(ptr->s) == NULLAGBLOCK; | 963 | return be32_to_cpu(ptr->s) == NULLAGBLOCK; |
964 | } | 964 | } |
965 | 965 | ||
966 | STATIC void | ||
967 | xfs_btree_set_ptr_null( | ||
968 | struct xfs_btree_cur *cur, | ||
969 | union xfs_btree_ptr *ptr) | ||
970 | { | ||
971 | if (cur->bc_flags & XFS_BTREE_LONG_PTRS) | ||
972 | ptr->l = cpu_to_be64(NULLFSBLOCK); | ||
973 | else | ||
974 | ptr->s = cpu_to_be32(NULLAGBLOCK); | ||
975 | } | ||
976 | |||
966 | /* | 977 | /* |
967 | * Get/set/init sibling pointers | 978 | * Get/set/init sibling pointers |
968 | */ | 979 | */ |
@@ -2697,3 +2708,354 @@ out0: | |||
2697 | *stat = 0; | 2708 | *stat = 0; |
2698 | return 0; | 2709 | return 0; |
2699 | } | 2710 | } |
2711 | |||
2712 | STATIC int | ||
2713 | xfs_btree_make_block_unfull( | ||
2714 | struct xfs_btree_cur *cur, /* btree cursor */ | ||
2715 | int level, /* btree level */ | ||
2716 | int numrecs,/* # of recs in block */ | ||
2717 | int *oindex,/* old tree index */ | ||
2718 | int *index, /* new tree index */ | ||
2719 | union xfs_btree_ptr *nptr, /* new btree ptr */ | ||
2720 | struct xfs_btree_cur **ncur, /* new btree cursor */ | ||
2721 | union xfs_btree_rec *nrec, /* new record */ | ||
2722 | int *stat) | ||
2723 | { | ||
2724 | union xfs_btree_key key; /* new btree key value */ | ||
2725 | int error = 0; | ||
2726 | |||
2727 | if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) && | ||
2728 | level == cur->bc_nlevels - 1) { | ||
2729 | struct xfs_inode *ip = cur->bc_private.b.ip; | ||
2730 | |||
2731 | if (numrecs < cur->bc_ops->get_dmaxrecs(cur, level)) { | ||
2732 | /* A root block that can be made bigger. */ | ||
2733 | |||
2734 | xfs_iroot_realloc(ip, 1, cur->bc_private.b.whichfork); | ||
2735 | } else { | ||
2736 | /* A root block that needs replacing */ | ||
2737 | int logflags = 0; | ||
2738 | |||
2739 | error = xfs_btree_new_iroot(cur, &logflags, stat); | ||
2740 | if (error || *stat == 0) | ||
2741 | return error; | ||
2742 | |||
2743 | xfs_trans_log_inode(cur->bc_tp, ip, logflags); | ||
2744 | } | ||
2745 | |||
2746 | return 0; | ||
2747 | } | ||
2748 | |||
2749 | /* First, try shifting an entry to the right neighbor. */ | ||
2750 | error = xfs_btree_rshift(cur, level, stat); | ||
2751 | if (error || *stat) | ||
2752 | return error; | ||
2753 | |||
2754 | /* Next, try shifting an entry to the left neighbor. */ | ||
2755 | error = xfs_btree_lshift(cur, level, stat); | ||
2756 | if (error) | ||
2757 | return error; | ||
2758 | |||
2759 | if (*stat) { | ||
2760 | *oindex = *index = cur->bc_ptrs[level]; | ||
2761 | return 0; | ||
2762 | } | ||
2763 | |||
2764 | /* | ||
2765 | * Next, try splitting the current block in half. | ||
2766 | * | ||
2767 | * If this works we have to re-set our variables because we | ||
2768 | * could be in a different block now. | ||
2769 | */ | ||
2770 | error = xfs_btree_split(cur, level, nptr, &key, ncur, stat); | ||
2771 | if (error || *stat == 0) | ||
2772 | return error; | ||
2773 | |||
2774 | |||
2775 | *index = cur->bc_ptrs[level]; | ||
2776 | cur->bc_ops->init_rec_from_key(&key, nrec); | ||
2777 | return 0; | ||
2778 | } | ||
2779 | |||
2780 | /* | ||
2781 | * Insert one record/level. Return information to the caller | ||
2782 | * allowing the next level up to proceed if necessary. | ||
2783 | */ | ||
2784 | STATIC int | ||
2785 | xfs_btree_insrec( | ||
2786 | struct xfs_btree_cur *cur, /* btree cursor */ | ||
2787 | int level, /* level to insert record at */ | ||
2788 | union xfs_btree_ptr *ptrp, /* i/o: block number inserted */ | ||
2789 | union xfs_btree_rec *recp, /* i/o: record data inserted */ | ||
2790 | struct xfs_btree_cur **curp, /* output: new cursor replacing cur */ | ||
2791 | int *stat) /* success/failure */ | ||
2792 | { | ||
2793 | struct xfs_btree_block *block; /* btree block */ | ||
2794 | struct xfs_buf *bp; /* buffer for block */ | ||
2795 | union xfs_btree_key key; /* btree key */ | ||
2796 | union xfs_btree_ptr nptr; /* new block ptr */ | ||
2797 | struct xfs_btree_cur *ncur; /* new btree cursor */ | ||
2798 | union xfs_btree_rec nrec; /* new record count */ | ||
2799 | int optr; /* old key/record index */ | ||
2800 | int ptr; /* key/record index */ | ||
2801 | int numrecs;/* number of records */ | ||
2802 | int error; /* error return value */ | ||
2803 | #ifdef DEBUG | ||
2804 | int i; | ||
2805 | #endif | ||
2806 | |||
2807 | XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY); | ||
2808 | XFS_BTREE_TRACE_ARGIPR(cur, level, *ptrp, recp); | ||
2809 | |||
2810 | ncur = NULL; | ||
2811 | |||
2812 | /* | ||
2813 | * If we have an external root pointer, and we've made it to the | ||
2814 | * root level, allocate a new root block and we're done. | ||
2815 | */ | ||
2816 | if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) && | ||
2817 | (level >= cur->bc_nlevels)) { | ||
2818 | error = xfs_btree_new_root(cur, stat); | ||
2819 | xfs_btree_set_ptr_null(cur, ptrp); | ||
2820 | |||
2821 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
2822 | return error; | ||
2823 | } | ||
2824 | |||
2825 | /* If we're off the left edge, return failure. */ | ||
2826 | ptr = cur->bc_ptrs[level]; | ||
2827 | if (ptr == 0) { | ||
2828 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
2829 | *stat = 0; | ||
2830 | return 0; | ||
2831 | } | ||
2832 | |||
2833 | /* Make a key out of the record data to be inserted, and save it. */ | ||
2834 | cur->bc_ops->init_key_from_rec(&key, recp); | ||
2835 | |||
2836 | optr = ptr; | ||
2837 | |||
2838 | XFS_BTREE_STATS_INC(cur, insrec); | ||
2839 | |||
2840 | /* Get pointers to the btree buffer and block. */ | ||
2841 | block = xfs_btree_get_block(cur, level, &bp); | ||
2842 | numrecs = xfs_btree_get_numrecs(block); | ||
2843 | |||
2844 | #ifdef DEBUG | ||
2845 | error = xfs_btree_check_block(cur, block, level, bp); | ||
2846 | if (error) | ||
2847 | goto error0; | ||
2848 | |||
2849 | /* Check that the new entry is being inserted in the right place. */ | ||
2850 | if (ptr <= numrecs) { | ||
2851 | if (level == 0) { | ||
2852 | xfs_btree_check_rec(cur->bc_btnum, recp, | ||
2853 | xfs_btree_rec_addr(cur, ptr, block)); | ||
2854 | } else { | ||
2855 | xfs_btree_check_key(cur->bc_btnum, &key, | ||
2856 | xfs_btree_key_addr(cur, ptr, block)); | ||
2857 | } | ||
2858 | } | ||
2859 | #endif | ||
2860 | |||
2861 | /* | ||
2862 | * If the block is full, we can't insert the new entry until we | ||
2863 | * make the block un-full. | ||
2864 | */ | ||
2865 | xfs_btree_set_ptr_null(cur, &nptr); | ||
2866 | if (numrecs == cur->bc_ops->get_maxrecs(cur, level)) { | ||
2867 | error = xfs_btree_make_block_unfull(cur, level, numrecs, | ||
2868 | &optr, &ptr, &nptr, &ncur, &nrec, stat); | ||
2869 | if (error || *stat == 0) | ||
2870 | goto error0; | ||
2871 | } | ||
2872 | |||
2873 | /* | ||
2874 | * The current block may have changed if the block was | ||
2875 | * previously full and we have just made space in it. | ||
2876 | */ | ||
2877 | block = xfs_btree_get_block(cur, level, &bp); | ||
2878 | numrecs = xfs_btree_get_numrecs(block); | ||
2879 | |||
2880 | #ifdef DEBUG | ||
2881 | error = xfs_btree_check_block(cur, block, level, bp); | ||
2882 | if (error) | ||
2883 | return error; | ||
2884 | #endif | ||
2885 | |||
2886 | /* | ||
2887 | * At this point we know there's room for our new entry in the block | ||
2888 | * we're pointing at. | ||
2889 | */ | ||
2890 | XFS_BTREE_STATS_ADD(cur, moves, numrecs - ptr + 1); | ||
2891 | |||
2892 | if (level > 0) { | ||
2893 | /* It's a nonleaf. make a hole in the keys and ptrs */ | ||
2894 | union xfs_btree_key *kp; | ||
2895 | union xfs_btree_ptr *pp; | ||
2896 | |||
2897 | kp = xfs_btree_key_addr(cur, ptr, block); | ||
2898 | pp = xfs_btree_ptr_addr(cur, ptr, block); | ||
2899 | |||
2900 | #ifdef DEBUG | ||
2901 | for (i = numrecs - ptr; i >= 0; i--) { | ||
2902 | error = xfs_btree_check_ptr(cur, pp, i, level); | ||
2903 | if (error) | ||
2904 | return error; | ||
2905 | } | ||
2906 | #endif | ||
2907 | |||
2908 | xfs_btree_shift_keys(cur, kp, 1, numrecs - ptr + 1); | ||
2909 | xfs_btree_shift_ptrs(cur, pp, 1, numrecs - ptr + 1); | ||
2910 | |||
2911 | #ifdef DEBUG | ||
2912 | error = xfs_btree_check_ptr(cur, ptrp, 0, level); | ||
2913 | if (error) | ||
2914 | goto error0; | ||
2915 | #endif | ||
2916 | |||
2917 | /* Now put the new data in, bump numrecs and log it. */ | ||
2918 | xfs_btree_copy_keys(cur, kp, &key, 1); | ||
2919 | xfs_btree_copy_ptrs(cur, pp, ptrp, 1); | ||
2920 | numrecs++; | ||
2921 | xfs_btree_set_numrecs(block, numrecs); | ||
2922 | xfs_btree_log_ptrs(cur, bp, ptr, numrecs); | ||
2923 | xfs_btree_log_keys(cur, bp, ptr, numrecs); | ||
2924 | #ifdef DEBUG | ||
2925 | if (ptr < numrecs) { | ||
2926 | xfs_btree_check_key(cur->bc_btnum, kp, | ||
2927 | xfs_btree_key_addr(cur, ptr + 1, block)); | ||
2928 | } | ||
2929 | #endif | ||
2930 | } else { | ||
2931 | /* It's a leaf. make a hole in the records */ | ||
2932 | union xfs_btree_rec *rp; | ||
2933 | |||
2934 | rp = xfs_btree_rec_addr(cur, ptr, block); | ||
2935 | |||
2936 | xfs_btree_shift_recs(cur, rp, 1, numrecs - ptr + 1); | ||
2937 | |||
2938 | /* Now put the new data in, bump numrecs and log it. */ | ||
2939 | xfs_btree_copy_recs(cur, rp, recp, 1); | ||
2940 | xfs_btree_set_numrecs(block, ++numrecs); | ||
2941 | xfs_btree_log_recs(cur, bp, ptr, numrecs); | ||
2942 | #ifdef DEBUG | ||
2943 | if (ptr < numrecs) { | ||
2944 | xfs_btree_check_rec(cur->bc_btnum, rp, | ||
2945 | xfs_btree_rec_addr(cur, ptr + 1, block)); | ||
2946 | } | ||
2947 | #endif | ||
2948 | } | ||
2949 | |||
2950 | /* Log the new number of records in the btree header. */ | ||
2951 | xfs_btree_log_block(cur, bp, XFS_BB_NUMRECS); | ||
2952 | |||
2953 | /* If we inserted at the start of a block, update the parents' keys. */ | ||
2954 | if (optr == 1) { | ||
2955 | error = xfs_btree_updkey(cur, &key, level + 1); | ||
2956 | if (error) | ||
2957 | goto error0; | ||
2958 | } | ||
2959 | |||
2960 | /* | ||
2961 | * If we are tracking the last record in the tree and | ||
2962 | * we are at the far right edge of the tree, update it. | ||
2963 | */ | ||
2964 | if (xfs_btree_is_lastrec(cur, block, level)) { | ||
2965 | cur->bc_ops->update_lastrec(cur, block, recp, | ||
2966 | ptr, LASTREC_INSREC); | ||
2967 | } | ||
2968 | |||
2969 | /* | ||
2970 | * Return the new block number, if any. | ||
2971 | * If there is one, give back a record value and a cursor too. | ||
2972 | */ | ||
2973 | *ptrp = nptr; | ||
2974 | if (!xfs_btree_ptr_is_null(cur, &nptr)) { | ||
2975 | *recp = nrec; | ||
2976 | *curp = ncur; | ||
2977 | } | ||
2978 | |||
2979 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
2980 | *stat = 1; | ||
2981 | return 0; | ||
2982 | |||
2983 | error0: | ||
2984 | XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR); | ||
2985 | return error; | ||
2986 | } | ||
2987 | |||
2988 | /* | ||
2989 | * Insert the record at the point referenced by cur. | ||
2990 | * | ||
2991 | * A multi-level split of the tree on insert will invalidate the original | ||
2992 | * cursor. All callers of this function should assume that the cursor is | ||
2993 | * no longer valid and revalidate it. | ||
2994 | */ | ||
2995 | int | ||
2996 | xfs_btree_insert( | ||
2997 | struct xfs_btree_cur *cur, | ||
2998 | int *stat) | ||
2999 | { | ||
3000 | int error; /* error return value */ | ||
3001 | int i; /* result value, 0 for failure */ | ||
3002 | int level; /* current level number in btree */ | ||
3003 | union xfs_btree_ptr nptr; /* new block number (split result) */ | ||
3004 | struct xfs_btree_cur *ncur; /* new cursor (split result) */ | ||
3005 | struct xfs_btree_cur *pcur; /* previous level's cursor */ | ||
3006 | union xfs_btree_rec rec; /* record to insert */ | ||
3007 | |||
3008 | level = 0; | ||
3009 | ncur = NULL; | ||
3010 | pcur = cur; | ||
3011 | |||
3012 | xfs_btree_set_ptr_null(cur, &nptr); | ||
3013 | cur->bc_ops->init_rec_from_cur(cur, &rec); | ||
3014 | |||
3015 | /* | ||
3016 | * Loop going up the tree, starting at the leaf level. | ||
3017 | * Stop when we don't get a split block, that must mean that | ||
3018 | * the insert is finished with this level. | ||
3019 | */ | ||
3020 | do { | ||
3021 | /* | ||
3022 | * Insert nrec/nptr into this level of the tree. | ||
3023 | * Note if we fail, nptr will be null. | ||
3024 | */ | ||
3025 | error = xfs_btree_insrec(pcur, level, &nptr, &rec, &ncur, &i); | ||
3026 | if (error) { | ||
3027 | if (pcur != cur) | ||
3028 | xfs_btree_del_cursor(pcur, XFS_BTREE_ERROR); | ||
3029 | goto error0; | ||
3030 | } | ||
3031 | |||
3032 | XFS_WANT_CORRUPTED_GOTO(i == 1, error0); | ||
3033 | level++; | ||
3034 | |||
3035 | /* | ||
3036 | * See if the cursor we just used is trash. | ||
3037 | * Can't trash the caller's cursor, but otherwise we should | ||
3038 | * if ncur is a new cursor or we're about to be done. | ||
3039 | */ | ||
3040 | if (pcur != cur && | ||
3041 | (ncur || xfs_btree_ptr_is_null(cur, &nptr))) { | ||
3042 | /* Save the state from the cursor before we trash it */ | ||
3043 | if (cur->bc_ops->update_cursor) | ||
3044 | cur->bc_ops->update_cursor(pcur, cur); | ||
3045 | cur->bc_nlevels = pcur->bc_nlevels; | ||
3046 | xfs_btree_del_cursor(pcur, XFS_BTREE_NOERROR); | ||
3047 | } | ||
3048 | /* If we got a new cursor, switch to it. */ | ||
3049 | if (ncur) { | ||
3050 | pcur = ncur; | ||
3051 | ncur = NULL; | ||
3052 | } | ||
3053 | } while (!xfs_btree_ptr_is_null(cur, &nptr)); | ||
3054 | |||
3055 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
3056 | *stat = i; | ||
3057 | return 0; | ||
3058 | error0: | ||
3059 | XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR); | ||
3060 | return error; | ||
3061 | } | ||