diff options
author | Christoph Hellwig <hch@infradead.org> | 2008-10-30 01:56:09 -0400 |
---|---|---|
committer | Lachlan McIlroy <lachlan@sgi.com> | 2008-10-30 01:56:09 -0400 |
commit | fe033cc848489851f0c7de48f0b1bab5d744ad8a (patch) | |
tree | f69709f4e9c125c528a699c32f439b53ea0969f3 /fs/xfs/xfs_btree.c | |
parent | 8df4da4a0a642d3a016028c0d922bcb4d5a4a6d7 (diff) |
[XFS] implement generic xfs_btree_lookup
From: Dave Chinner <dgc@sgi.com>
[hch: split out from bigger patch and minor adaptions]
SGI-PV: 985583
SGI-Modid: xfs-linux-melb:xfs-kern:32192a
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 | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c index 3d561f8f78d0..41912a01bec7 100644 --- a/fs/xfs/xfs_btree.c +++ b/fs/xfs/xfs_btree.c | |||
@@ -1270,3 +1270,222 @@ error0: | |||
1270 | return error; | 1270 | return error; |
1271 | } | 1271 | } |
1272 | 1272 | ||
1273 | |||
1274 | STATIC int | ||
1275 | xfs_btree_lookup_get_block( | ||
1276 | struct xfs_btree_cur *cur, /* btree cursor */ | ||
1277 | int level, /* level in the btree */ | ||
1278 | union xfs_btree_ptr *pp, /* ptr to btree block */ | ||
1279 | struct xfs_btree_block **blkp) /* return btree block */ | ||
1280 | { | ||
1281 | struct xfs_buf *bp; /* buffer pointer for btree block */ | ||
1282 | int error = 0; | ||
1283 | |||
1284 | /* special case the root block if in an inode */ | ||
1285 | if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) && | ||
1286 | (level == cur->bc_nlevels - 1)) { | ||
1287 | *blkp = xfs_btree_get_iroot(cur); | ||
1288 | return 0; | ||
1289 | } | ||
1290 | |||
1291 | /* | ||
1292 | * If the old buffer at this level for the disk address we are | ||
1293 | * looking for re-use it. | ||
1294 | * | ||
1295 | * Otherwise throw it away and get a new one. | ||
1296 | */ | ||
1297 | bp = cur->bc_bufs[level]; | ||
1298 | if (bp && XFS_BUF_ADDR(bp) == xfs_btree_ptr_to_daddr(cur, pp)) { | ||
1299 | *blkp = XFS_BUF_TO_BLOCK(bp); | ||
1300 | return 0; | ||
1301 | } | ||
1302 | |||
1303 | error = xfs_btree_read_buf_block(cur, pp, level, 0, blkp, &bp); | ||
1304 | if (error) | ||
1305 | return error; | ||
1306 | |||
1307 | xfs_btree_setbuf(cur, level, bp); | ||
1308 | return 0; | ||
1309 | } | ||
1310 | |||
1311 | /* | ||
1312 | * Get current search key. For level 0 we don't actually have a key | ||
1313 | * structure so we make one up from the record. For all other levels | ||
1314 | * we just return the right key. | ||
1315 | */ | ||
1316 | STATIC union xfs_btree_key * | ||
1317 | xfs_lookup_get_search_key( | ||
1318 | struct xfs_btree_cur *cur, | ||
1319 | int level, | ||
1320 | int keyno, | ||
1321 | struct xfs_btree_block *block, | ||
1322 | union xfs_btree_key *kp) | ||
1323 | { | ||
1324 | if (level == 0) { | ||
1325 | cur->bc_ops->init_key_from_rec(kp, | ||
1326 | xfs_btree_rec_addr(cur, keyno, block)); | ||
1327 | return kp; | ||
1328 | } | ||
1329 | |||
1330 | return xfs_btree_key_addr(cur, keyno, block); | ||
1331 | } | ||
1332 | |||
1333 | /* | ||
1334 | * Lookup the record. The cursor is made to point to it, based on dir. | ||
1335 | * Return 0 if can't find any such record, 1 for success. | ||
1336 | */ | ||
1337 | int /* error */ | ||
1338 | xfs_btree_lookup( | ||
1339 | struct xfs_btree_cur *cur, /* btree cursor */ | ||
1340 | xfs_lookup_t dir, /* <=, ==, or >= */ | ||
1341 | int *stat) /* success/failure */ | ||
1342 | { | ||
1343 | struct xfs_btree_block *block; /* current btree block */ | ||
1344 | __int64_t diff; /* difference for the current key */ | ||
1345 | int error; /* error return value */ | ||
1346 | int keyno; /* current key number */ | ||
1347 | int level; /* level in the btree */ | ||
1348 | union xfs_btree_ptr *pp; /* ptr to btree block */ | ||
1349 | union xfs_btree_ptr ptr; /* ptr to btree block */ | ||
1350 | |||
1351 | XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY); | ||
1352 | XFS_BTREE_TRACE_ARGI(cur, dir); | ||
1353 | |||
1354 | XFS_BTREE_STATS_INC(cur, lookup); | ||
1355 | |||
1356 | block = NULL; | ||
1357 | keyno = 0; | ||
1358 | |||
1359 | /* initialise start pointer from cursor */ | ||
1360 | cur->bc_ops->init_ptr_from_cur(cur, &ptr); | ||
1361 | pp = &ptr; | ||
1362 | |||
1363 | /* | ||
1364 | * Iterate over each level in the btree, starting at the root. | ||
1365 | * For each level above the leaves, find the key we need, based | ||
1366 | * on the lookup record, then follow the corresponding block | ||
1367 | * pointer down to the next level. | ||
1368 | */ | ||
1369 | for (level = cur->bc_nlevels - 1, diff = 1; level >= 0; level--) { | ||
1370 | /* Get the block we need to do the lookup on. */ | ||
1371 | error = xfs_btree_lookup_get_block(cur, level, pp, &block); | ||
1372 | if (error) | ||
1373 | goto error0; | ||
1374 | |||
1375 | if (diff == 0) { | ||
1376 | /* | ||
1377 | * If we already had a key match at a higher level, we | ||
1378 | * know we need to use the first entry in this block. | ||
1379 | */ | ||
1380 | keyno = 1; | ||
1381 | } else { | ||
1382 | /* Otherwise search this block. Do a binary search. */ | ||
1383 | |||
1384 | int high; /* high entry number */ | ||
1385 | int low; /* low entry number */ | ||
1386 | |||
1387 | /* Set low and high entry numbers, 1-based. */ | ||
1388 | low = 1; | ||
1389 | high = xfs_btree_get_numrecs(block); | ||
1390 | if (!high) { | ||
1391 | /* Block is empty, must be an empty leaf. */ | ||
1392 | ASSERT(level == 0 && cur->bc_nlevels == 1); | ||
1393 | |||
1394 | cur->bc_ptrs[0] = dir != XFS_LOOKUP_LE; | ||
1395 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
1396 | *stat = 0; | ||
1397 | return 0; | ||
1398 | } | ||
1399 | |||
1400 | /* Binary search the block. */ | ||
1401 | while (low <= high) { | ||
1402 | union xfs_btree_key key; | ||
1403 | union xfs_btree_key *kp; | ||
1404 | |||
1405 | XFS_BTREE_STATS_INC(cur, compare); | ||
1406 | |||
1407 | /* keyno is average of low and high. */ | ||
1408 | keyno = (low + high) >> 1; | ||
1409 | |||
1410 | /* Get current search key */ | ||
1411 | kp = xfs_lookup_get_search_key(cur, level, | ||
1412 | keyno, block, &key); | ||
1413 | |||
1414 | /* | ||
1415 | * Compute difference to get next direction: | ||
1416 | * - less than, move right | ||
1417 | * - greater than, move left | ||
1418 | * - equal, we're done | ||
1419 | */ | ||
1420 | diff = cur->bc_ops->key_diff(cur, kp); | ||
1421 | if (diff < 0) | ||
1422 | low = keyno + 1; | ||
1423 | else if (diff > 0) | ||
1424 | high = keyno - 1; | ||
1425 | else | ||
1426 | break; | ||
1427 | } | ||
1428 | } | ||
1429 | |||
1430 | /* | ||
1431 | * If there are more levels, set up for the next level | ||
1432 | * by getting the block number and filling in the cursor. | ||
1433 | */ | ||
1434 | if (level > 0) { | ||
1435 | /* | ||
1436 | * If we moved left, need the previous key number, | ||
1437 | * unless there isn't one. | ||
1438 | */ | ||
1439 | if (diff > 0 && --keyno < 1) | ||
1440 | keyno = 1; | ||
1441 | pp = xfs_btree_ptr_addr(cur, keyno, block); | ||
1442 | |||
1443 | #ifdef DEBUG | ||
1444 | error = xfs_btree_check_ptr(cur, pp, 0, level); | ||
1445 | if (error) | ||
1446 | goto error0; | ||
1447 | #endif | ||
1448 | cur->bc_ptrs[level] = keyno; | ||
1449 | } | ||
1450 | } | ||
1451 | |||
1452 | /* Done with the search. See if we need to adjust the results. */ | ||
1453 | if (dir != XFS_LOOKUP_LE && diff < 0) { | ||
1454 | keyno++; | ||
1455 | /* | ||
1456 | * If ge search and we went off the end of the block, but it's | ||
1457 | * not the last block, we're in the wrong block. | ||
1458 | */ | ||
1459 | xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_RIGHTSIB); | ||
1460 | if (dir == XFS_LOOKUP_GE && | ||
1461 | keyno > xfs_btree_get_numrecs(block) && | ||
1462 | !xfs_btree_ptr_is_null(cur, &ptr)) { | ||
1463 | int i; | ||
1464 | |||
1465 | cur->bc_ptrs[0] = keyno; | ||
1466 | error = xfs_btree_increment(cur, 0, &i); | ||
1467 | if (error) | ||
1468 | goto error0; | ||
1469 | XFS_WANT_CORRUPTED_RETURN(i == 1); | ||
1470 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
1471 | *stat = 1; | ||
1472 | return 0; | ||
1473 | } | ||
1474 | } else if (dir == XFS_LOOKUP_LE && diff > 0) | ||
1475 | keyno--; | ||
1476 | cur->bc_ptrs[0] = keyno; | ||
1477 | |||
1478 | /* Return if we succeeded or not. */ | ||
1479 | if (keyno == 0 || keyno > xfs_btree_get_numrecs(block)) | ||
1480 | *stat = 0; | ||
1481 | else if (dir != XFS_LOOKUP_EQ || diff == 0) | ||
1482 | *stat = 1; | ||
1483 | else | ||
1484 | *stat = 0; | ||
1485 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
1486 | return 0; | ||
1487 | |||
1488 | error0: | ||
1489 | XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR); | ||
1490 | return error; | ||
1491 | } | ||