aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_btree.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@infradead.org>2008-10-30 01:57:03 -0400
committerLachlan McIlroy <lachlan@sgi.com>2008-10-30 01:57:03 -0400
commitf5eb8e7ca58bc1e92436614444006120d21668ba (patch)
tree94825b885653224f74c6087956cb4e1930380467 /fs/xfs/xfs_btree.c
parent687b890a184fef263ebb773926e1f4aa69240d01 (diff)
[XFS] implement generic xfs_btree_split
Make the btree split 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:32198a 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.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c
index 2b0d1422c4c6..80576695fbe5 100644
--- a/fs/xfs/xfs_btree.c
+++ b/fs/xfs/xfs_btree.c
@@ -988,6 +988,48 @@ xfs_btree_get_sibling(
988 } 988 }
989} 989}
990 990
991STATIC void
992xfs_btree_set_sibling(
993 struct xfs_btree_cur *cur,
994 struct xfs_btree_block *block,
995 union xfs_btree_ptr *ptr,
996 int lr)
997{
998 ASSERT(lr == XFS_BB_LEFTSIB || lr == XFS_BB_RIGHTSIB);
999
1000 if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
1001 if (lr == XFS_BB_RIGHTSIB)
1002 block->bb_u.l.bb_rightsib = ptr->l;
1003 else
1004 block->bb_u.l.bb_leftsib = ptr->l;
1005 } else {
1006 if (lr == XFS_BB_RIGHTSIB)
1007 block->bb_u.s.bb_rightsib = ptr->s;
1008 else
1009 block->bb_u.s.bb_leftsib = ptr->s;
1010 }
1011}
1012
1013STATIC void
1014xfs_btree_init_block(
1015 struct xfs_btree_cur *cur,
1016 int level,
1017 int numrecs,
1018 struct xfs_btree_block *new) /* new block */
1019{
1020 new->bb_magic = cpu_to_be32(xfs_magics[cur->bc_btnum]);
1021 new->bb_level = cpu_to_be16(level);
1022 new->bb_numrecs = cpu_to_be16(numrecs);
1023
1024 if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
1025 new->bb_u.l.bb_leftsib = cpu_to_be64(NULLFSBLOCK);
1026 new->bb_u.l.bb_rightsib = cpu_to_be64(NULLFSBLOCK);
1027 } else {
1028 new->bb_u.s.bb_leftsib = cpu_to_be32(NULLAGBLOCK);
1029 new->bb_u.s.bb_rightsib = cpu_to_be32(NULLAGBLOCK);
1030 }
1031}
1032
991/* 1033/*
992 * Return true if ptr is the last record in the btree and 1034 * Return true if ptr is the last record in the btree and
993 * we need to track updateѕ to this record. The decision 1035 * we need to track updateѕ to this record. The decision
@@ -1012,6 +1054,21 @@ xfs_btree_is_lastrec(
1012 return 1; 1054 return 1;
1013} 1055}
1014 1056
1057STATIC void
1058xfs_btree_buf_to_ptr(
1059 struct xfs_btree_cur *cur,
1060 struct xfs_buf *bp,
1061 union xfs_btree_ptr *ptr)
1062{
1063 if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
1064 ptr->l = cpu_to_be64(XFS_DADDR_TO_FSB(cur->bc_mp,
1065 XFS_BUF_ADDR(bp)));
1066 else {
1067 ptr->s = cpu_to_be32(XFS_DADDR_TO_AGBNO(cur->bc_mp,
1068 XFS_BUF_ADDR(bp)));
1069 }
1070}
1071
1015STATIC xfs_daddr_t 1072STATIC xfs_daddr_t
1016xfs_btree_ptr_to_daddr( 1073xfs_btree_ptr_to_daddr(
1017 struct xfs_btree_cur *cur, 1074 struct xfs_btree_cur *cur,
@@ -1051,6 +1108,31 @@ xfs_btree_set_refs(
1051 } 1108 }
1052} 1109}
1053 1110
1111STATIC int
1112xfs_btree_get_buf_block(
1113 struct xfs_btree_cur *cur,
1114 union xfs_btree_ptr *ptr,
1115 int flags,
1116 struct xfs_btree_block **block,
1117 struct xfs_buf **bpp)
1118{
1119 struct xfs_mount *mp = cur->bc_mp;
1120 xfs_daddr_t d;
1121
1122 /* need to sort out how callers deal with failures first */
1123 ASSERT(!(flags & XFS_BUF_TRYLOCK));
1124
1125 d = xfs_btree_ptr_to_daddr(cur, ptr);
1126 *bpp = xfs_trans_get_buf(cur->bc_tp, mp->m_ddev_targp, d,
1127 mp->m_bsize, flags);
1128
1129 ASSERT(*bpp);
1130 ASSERT(!XFS_BUF_GETERROR(*bpp));
1131
1132 *block = XFS_BUF_TO_BLOCK(*bpp);
1133 return 0;
1134}
1135
1054/* 1136/*
1055 * Read in the buffer at the given ptr and return the buffer and 1137 * Read in the buffer at the given ptr and return the buffer and
1056 * the block pointer within the buffer. 1138 * the block pointer within the buffer.
@@ -2199,3 +2281,189 @@ error1:
2199 xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR); 2281 xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
2200 return error; 2282 return error;
2201} 2283}
2284
2285/*
2286 * Split cur/level block in half.
2287 * Return new block number and the key to its first
2288 * record (to be inserted into parent).
2289 */
2290int /* error */
2291xfs_btree_split(
2292 struct xfs_btree_cur *cur,
2293 int level,
2294 union xfs_btree_ptr *ptrp,
2295 union xfs_btree_key *key,
2296 struct xfs_btree_cur **curp,
2297 int *stat) /* success/failure */
2298{
2299 union xfs_btree_ptr lptr; /* left sibling block ptr */
2300 struct xfs_buf *lbp; /* left buffer pointer */
2301 struct xfs_btree_block *left; /* left btree block */
2302 union xfs_btree_ptr rptr; /* right sibling block ptr */
2303 struct xfs_buf *rbp; /* right buffer pointer */
2304 struct xfs_btree_block *right; /* right btree block */
2305 union xfs_btree_ptr rrptr; /* right-right sibling ptr */
2306 struct xfs_buf *rrbp; /* right-right buffer pointer */
2307 struct xfs_btree_block *rrblock; /* right-right btree block */
2308 int lrecs;
2309 int rrecs;
2310 int src_index;
2311 int error; /* error return value */
2312#ifdef DEBUG
2313 int i;
2314#endif
2315
2316 XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
2317 XFS_BTREE_TRACE_ARGIPK(cur, level, *ptrp, key);
2318
2319 XFS_BTREE_STATS_INC(cur, split);
2320
2321 /* Set up left block (current one). */
2322 left = xfs_btree_get_block(cur, level, &lbp);
2323
2324#ifdef DEBUG
2325 error = xfs_btree_check_block(cur, left, level, lbp);
2326 if (error)
2327 goto error0;
2328#endif
2329
2330 xfs_btree_buf_to_ptr(cur, lbp, &lptr);
2331
2332 /* Allocate the new block. If we can't do it, we're toast. Give up. */
2333 error = cur->bc_ops->alloc_block(cur, &lptr, &rptr, 1, stat);
2334 if (error)
2335 goto error0;
2336 if (*stat == 0)
2337 goto out0;
2338 XFS_BTREE_STATS_INC(cur, alloc);
2339
2340 /* Set up the new block as "right". */
2341 error = xfs_btree_get_buf_block(cur, &rptr, 0, &right, &rbp);
2342 if (error)
2343 goto error0;
2344
2345 /* Fill in the btree header for the new right block. */
2346 xfs_btree_init_block(cur, xfs_btree_get_level(left), 0, right);
2347
2348 /*
2349 * Split the entries between the old and the new block evenly.
2350 * Make sure that if there's an odd number of entries now, that
2351 * each new block will have the same number of entries.
2352 */
2353 lrecs = xfs_btree_get_numrecs(left);
2354 rrecs = lrecs / 2;
2355 if ((lrecs & 1) && cur->bc_ptrs[level] <= rrecs + 1)
2356 rrecs++;
2357 src_index = (lrecs - rrecs + 1);
2358
2359 XFS_BTREE_STATS_ADD(cur, moves, rrecs);
2360
2361 /*
2362 * Copy btree block entries from the left block over to the
2363 * new block, the right. Update the right block and log the
2364 * changes.
2365 */
2366 if (level > 0) {
2367 /* It's a non-leaf. Move keys and pointers. */
2368 union xfs_btree_key *lkp; /* left btree key */
2369 union xfs_btree_ptr *lpp; /* left address pointer */
2370 union xfs_btree_key *rkp; /* right btree key */
2371 union xfs_btree_ptr *rpp; /* right address pointer */
2372
2373 lkp = xfs_btree_key_addr(cur, src_index, left);
2374 lpp = xfs_btree_ptr_addr(cur, src_index, left);
2375 rkp = xfs_btree_key_addr(cur, 1, right);
2376 rpp = xfs_btree_ptr_addr(cur, 1, right);
2377
2378#ifdef DEBUG
2379 for (i = src_index; i < rrecs; i++) {
2380 error = xfs_btree_check_ptr(cur, lpp, i, level);
2381 if (error)
2382 goto error0;
2383 }
2384#endif
2385
2386 xfs_btree_copy_keys(cur, rkp, lkp, rrecs);
2387 xfs_btree_copy_ptrs(cur, rpp, lpp, rrecs);
2388
2389 xfs_btree_log_keys(cur, rbp, 1, rrecs);
2390 xfs_btree_log_ptrs(cur, rbp, 1, rrecs);
2391
2392 /* Grab the keys to the entries moved to the right block */
2393 xfs_btree_copy_keys(cur, key, rkp, 1);
2394 } else {
2395 /* It's a leaf. Move records. */
2396 union xfs_btree_rec *lrp; /* left record pointer */
2397 union xfs_btree_rec *rrp; /* right record pointer */
2398
2399 lrp = xfs_btree_rec_addr(cur, src_index, left);
2400 rrp = xfs_btree_rec_addr(cur, 1, right);
2401
2402 xfs_btree_copy_recs(cur, rrp, lrp, rrecs);
2403 xfs_btree_log_recs(cur, rbp, 1, rrecs);
2404
2405 cur->bc_ops->init_key_from_rec(key,
2406 xfs_btree_rec_addr(cur, 1, right));
2407 }
2408
2409
2410 /*
2411 * Find the left block number by looking in the buffer.
2412 * Adjust numrecs, sibling pointers.
2413 */
2414 xfs_btree_get_sibling(cur, left, &rrptr, XFS_BB_RIGHTSIB);
2415 xfs_btree_set_sibling(cur, right, &rrptr, XFS_BB_RIGHTSIB);
2416 xfs_btree_set_sibling(cur, right, &lptr, XFS_BB_LEFTSIB);
2417 xfs_btree_set_sibling(cur, left, &rptr, XFS_BB_RIGHTSIB);
2418
2419 lrecs -= rrecs;
2420 xfs_btree_set_numrecs(left, lrecs);
2421 xfs_btree_set_numrecs(right, xfs_btree_get_numrecs(right) + rrecs);
2422
2423 xfs_btree_log_block(cur, rbp, XFS_BB_ALL_BITS);
2424 xfs_btree_log_block(cur, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
2425
2426 /*
2427 * If there's a block to the new block's right, make that block
2428 * point back to right instead of to left.
2429 */
2430 if (!xfs_btree_ptr_is_null(cur, &rrptr)) {
2431 error = xfs_btree_read_buf_block(cur, &rrptr, level,
2432 0, &rrblock, &rrbp);
2433 if (error)
2434 goto error0;
2435 xfs_btree_set_sibling(cur, rrblock, &rptr, XFS_BB_LEFTSIB);
2436 xfs_btree_log_block(cur, rrbp, XFS_BB_LEFTSIB);
2437 }
2438 /*
2439 * If the cursor is really in the right block, move it there.
2440 * If it's just pointing past the last entry in left, then we'll
2441 * insert there, so don't change anything in that case.
2442 */
2443 if (cur->bc_ptrs[level] > lrecs + 1) {
2444 xfs_btree_setbuf(cur, level, rbp);
2445 cur->bc_ptrs[level] -= lrecs;
2446 }
2447 /*
2448 * If there are more levels, we'll need another cursor which refers
2449 * the right block, no matter where this cursor was.
2450 */
2451 if (level + 1 < cur->bc_nlevels) {
2452 error = xfs_btree_dup_cursor(cur, curp);
2453 if (error)
2454 goto error0;
2455 (*curp)->bc_ptrs[level + 1]++;
2456 }
2457 *ptrp = rptr;
2458 XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
2459 *stat = 1;
2460 return 0;
2461out0:
2462 XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
2463 *stat = 0;
2464 return 0;
2465
2466error0:
2467 XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
2468 return error;
2469}