aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_btree.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@infradead.org>2008-10-30 01:56:09 -0400
committerLachlan McIlroy <lachlan@sgi.com>2008-10-30 01:56:09 -0400
commitfe033cc848489851f0c7de48f0b1bab5d744ad8a (patch)
treef69709f4e9c125c528a699c32f439b53ea0969f3 /fs/xfs/xfs_btree.c
parent8df4da4a0a642d3a016028c0d922bcb4d5a4a6d7 (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.c219
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
1274STATIC int
1275xfs_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 */
1316STATIC union xfs_btree_key *
1317xfs_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 */
1337int /* error */
1338xfs_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
1488error0:
1489 XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
1490 return error;
1491}