aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_btree.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@infradead.org>2008-10-30 01:57:40 -0400
committerLachlan McIlroy <lachlan@sgi.com>2008-10-30 01:57:40 -0400
commit4b22a57188d87e873346b73c227607715be96399 (patch)
tree4cf2d0deede695968b7a32b098c0c64fac8610e5 /fs/xfs/xfs_btree.c
parentea77b0a66e6c910ef265d9af522d6303ea6b3055 (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.c362
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
966STATIC void
967xfs_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
2712STATIC int
2713xfs_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 */
2784STATIC int
2785xfs_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
2983error0:
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 */
2995int
2996xfs_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;
3058error0:
3059 XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
3060 return error;
3061}