diff options
author | Christoph Hellwig <hch@infradead.org> | 2008-10-30 01:57:03 -0400 |
---|---|---|
committer | Lachlan McIlroy <lachlan@sgi.com> | 2008-10-30 01:57:03 -0400 |
commit | f5eb8e7ca58bc1e92436614444006120d21668ba (patch) | |
tree | 94825b885653224f74c6087956cb4e1930380467 /fs/xfs/xfs_btree.c | |
parent | 687b890a184fef263ebb773926e1f4aa69240d01 (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.c | 268 |
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 | ||
991 | STATIC void | ||
992 | xfs_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 | |||
1013 | STATIC void | ||
1014 | xfs_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 | ||
1057 | STATIC void | ||
1058 | xfs_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 | |||
1015 | STATIC xfs_daddr_t | 1072 | STATIC xfs_daddr_t |
1016 | xfs_btree_ptr_to_daddr( | 1073 | xfs_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 | ||
1111 | STATIC int | ||
1112 | xfs_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 | */ | ||
2290 | int /* error */ | ||
2291 | xfs_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; | ||
2461 | out0: | ||
2462 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
2463 | *stat = 0; | ||
2464 | return 0; | ||
2465 | |||
2466 | error0: | ||
2467 | XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR); | ||
2468 | return error; | ||
2469 | } | ||