aboutsummaryrefslogtreecommitdiffstats
path: root/lib/radix-tree.c
diff options
context:
space:
mode:
authorJohannes Weiner <hannes@cmpxchg.org>2014-04-03 17:47:54 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 19:21:01 -0400
commit139e561660fe11e0fc35e142a800df3dd7d03e9d (patch)
treeb46c699ad4840fb76e71fe17b340f890587e2f6a /lib/radix-tree.c
parenta528910e12ec7ee203095eb1711468a66b9b60b0 (diff)
lib: radix_tree: tree node interface
Make struct radix_tree_node part of the public interface and provide API functions to create, look up, and delete whole nodes. Refactor the existing insert, look up, delete functions on top of these new node primitives. This will allow the VM to track and garbage collect page cache radix tree nodes. [sasha.levin@oracle.com: return correct error code on insertion failure] Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> Reviewed-by: Rik van Riel <riel@redhat.com> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Bob Liu <bob.liu@oracle.com> Cc: Christoph Hellwig <hch@infradead.org> Cc: Dave Chinner <david@fromorbit.com> Cc: Greg Thelen <gthelen@google.com> Cc: Hugh Dickins <hughd@google.com> Cc: Jan Kara <jack@suse.cz> Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Cc: Luigi Semenzato <semenzato@google.com> Cc: Mel Gorman <mgorman@suse.de> Cc: Metin Doslu <metin@citusdata.com> Cc: Michel Lespinasse <walken@google.com> Cc: Ozgun Erdogan <ozgun@citusdata.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Roman Gushchin <klamm@yandex-team.ru> Cc: Ryan Mallon <rmallon@gmail.com> Cc: Tejun Heo <tj@kernel.org> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Sasha Levin <sasha.levin@oracle.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'lib/radix-tree.c')
-rw-r--r--lib/radix-tree.c263
1 files changed, 148 insertions, 115 deletions
diff --git a/lib/radix-tree.c b/lib/radix-tree.c
index 7e30d2a7f346..d60be40c111b 100644
--- a/lib/radix-tree.c
+++ b/lib/radix-tree.c
@@ -35,33 +35,6 @@
35#include <linux/hardirq.h> /* in_interrupt() */ 35#include <linux/hardirq.h> /* in_interrupt() */
36 36
37 37
38#ifdef __KERNEL__
39#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6)
40#else
41#define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */
42#endif
43
44#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT)
45#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1)
46
47#define RADIX_TREE_TAG_LONGS \
48 ((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG)
49
50struct radix_tree_node {
51 unsigned int height; /* Height from the bottom */
52 unsigned int count;
53 union {
54 struct radix_tree_node *parent; /* Used when ascending tree */
55 struct rcu_head rcu_head; /* Used when freeing node */
56 };
57 void __rcu *slots[RADIX_TREE_MAP_SIZE];
58 unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
59};
60
61#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long))
62#define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS, \
63 RADIX_TREE_MAP_SHIFT))
64
65/* 38/*
66 * The height_to_maxindex array needs to be one deeper than the maximum 39 * The height_to_maxindex array needs to be one deeper than the maximum
67 * path as height 0 holds only 1 entry. 40 * path as height 0 holds only 1 entry.
@@ -387,23 +360,28 @@ out:
387} 360}
388 361
389/** 362/**
390 * radix_tree_insert - insert into a radix tree 363 * __radix_tree_create - create a slot in a radix tree
391 * @root: radix tree root 364 * @root: radix tree root
392 * @index: index key 365 * @index: index key
393 * @item: item to insert 366 * @nodep: returns node
367 * @slotp: returns slot
394 * 368 *
395 * Insert an item into the radix tree at position @index. 369 * Create, if necessary, and return the node and slot for an item
370 * at position @index in the radix tree @root.
371 *
372 * Until there is more than one item in the tree, no nodes are
373 * allocated and @root->rnode is used as a direct slot instead of
374 * pointing to a node, in which case *@nodep will be NULL.
375 *
376 * Returns -ENOMEM, or 0 for success.
396 */ 377 */
397int radix_tree_insert(struct radix_tree_root *root, 378int __radix_tree_create(struct radix_tree_root *root, unsigned long index,
398 unsigned long index, void *item) 379 struct radix_tree_node **nodep, void ***slotp)
399{ 380{
400 struct radix_tree_node *node = NULL, *slot; 381 struct radix_tree_node *node = NULL, *slot;
401 unsigned int height, shift; 382 unsigned int height, shift, offset;
402 int offset;
403 int error; 383 int error;
404 384
405 BUG_ON(radix_tree_is_indirect_ptr(item));
406
407 /* Make sure the tree is high enough. */ 385 /* Make sure the tree is high enough. */
408 if (index > radix_tree_maxindex(root->height)) { 386 if (index > radix_tree_maxindex(root->height)) {
409 error = radix_tree_extend(root, index); 387 error = radix_tree_extend(root, index);
@@ -439,16 +417,42 @@ int radix_tree_insert(struct radix_tree_root *root,
439 height--; 417 height--;
440 } 418 }
441 419
442 if (slot != NULL) 420 if (nodep)
421 *nodep = node;
422 if (slotp)
423 *slotp = node ? node->slots + offset : (void **)&root->rnode;
424 return 0;
425}
426
427/**
428 * radix_tree_insert - insert into a radix tree
429 * @root: radix tree root
430 * @index: index key
431 * @item: item to insert
432 *
433 * Insert an item into the radix tree at position @index.
434 */
435int radix_tree_insert(struct radix_tree_root *root,
436 unsigned long index, void *item)
437{
438 struct radix_tree_node *node;
439 void **slot;
440 int error;
441
442 BUG_ON(radix_tree_is_indirect_ptr(item));
443
444 error = __radix_tree_create(root, index, &node, &slot);
445 if (error)
446 return error;
447 if (*slot != NULL)
443 return -EEXIST; 448 return -EEXIST;
449 rcu_assign_pointer(*slot, item);
444 450
445 if (node) { 451 if (node) {
446 node->count++; 452 node->count++;
447 rcu_assign_pointer(node->slots[offset], item); 453 BUG_ON(tag_get(node, 0, index & RADIX_TREE_MAP_MASK));
448 BUG_ON(tag_get(node, 0, offset)); 454 BUG_ON(tag_get(node, 1, index & RADIX_TREE_MAP_MASK));
449 BUG_ON(tag_get(node, 1, offset));
450 } else { 455 } else {
451 rcu_assign_pointer(root->rnode, item);
452 BUG_ON(root_tag_get(root, 0)); 456 BUG_ON(root_tag_get(root, 0));
453 BUG_ON(root_tag_get(root, 1)); 457 BUG_ON(root_tag_get(root, 1));
454 } 458 }
@@ -457,15 +461,26 @@ int radix_tree_insert(struct radix_tree_root *root,
457} 461}
458EXPORT_SYMBOL(radix_tree_insert); 462EXPORT_SYMBOL(radix_tree_insert);
459 463
460/* 464/**
461 * is_slot == 1 : search for the slot. 465 * __radix_tree_lookup - lookup an item in a radix tree
462 * is_slot == 0 : search for the node. 466 * @root: radix tree root
467 * @index: index key
468 * @nodep: returns node
469 * @slotp: returns slot
470 *
471 * Lookup and return the item at position @index in the radix
472 * tree @root.
473 *
474 * Until there is more than one item in the tree, no nodes are
475 * allocated and @root->rnode is used as a direct slot instead of
476 * pointing to a node, in which case *@nodep will be NULL.
463 */ 477 */
464static void *radix_tree_lookup_element(struct radix_tree_root *root, 478void *__radix_tree_lookup(struct radix_tree_root *root, unsigned long index,
465 unsigned long index, int is_slot) 479 struct radix_tree_node **nodep, void ***slotp)
466{ 480{
481 struct radix_tree_node *node, *parent;
467 unsigned int height, shift; 482 unsigned int height, shift;
468 struct radix_tree_node *node, **slot; 483 void **slot;
469 484
470 node = rcu_dereference_raw(root->rnode); 485 node = rcu_dereference_raw(root->rnode);
471 if (node == NULL) 486 if (node == NULL)
@@ -474,7 +489,12 @@ static void *radix_tree_lookup_element(struct radix_tree_root *root,
474 if (!radix_tree_is_indirect_ptr(node)) { 489 if (!radix_tree_is_indirect_ptr(node)) {
475 if (index > 0) 490 if (index > 0)
476 return NULL; 491 return NULL;
477 return is_slot ? (void *)&root->rnode : node; 492
493 if (nodep)
494 *nodep = NULL;
495 if (slotp)
496 *slotp = (void **)&root->rnode;
497 return node;
478 } 498 }
479 node = indirect_to_ptr(node); 499 node = indirect_to_ptr(node);
480 500
@@ -485,8 +505,8 @@ static void *radix_tree_lookup_element(struct radix_tree_root *root,
485 shift = (height-1) * RADIX_TREE_MAP_SHIFT; 505 shift = (height-1) * RADIX_TREE_MAP_SHIFT;
486 506
487 do { 507 do {
488 slot = (struct radix_tree_node **) 508 parent = node;
489 (node->slots + ((index>>shift) & RADIX_TREE_MAP_MASK)); 509 slot = node->slots + ((index >> shift) & RADIX_TREE_MAP_MASK);
490 node = rcu_dereference_raw(*slot); 510 node = rcu_dereference_raw(*slot);
491 if (node == NULL) 511 if (node == NULL)
492 return NULL; 512 return NULL;
@@ -495,7 +515,11 @@ static void *radix_tree_lookup_element(struct radix_tree_root *root,
495 height--; 515 height--;
496 } while (height > 0); 516 } while (height > 0);
497 517
498 return is_slot ? (void *)slot : indirect_to_ptr(node); 518 if (nodep)
519 *nodep = parent;
520 if (slotp)
521 *slotp = slot;
522 return node;
499} 523}
500 524
501/** 525/**
@@ -513,7 +537,11 @@ static void *radix_tree_lookup_element(struct radix_tree_root *root,
513 */ 537 */
514void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) 538void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index)
515{ 539{
516 return (void **)radix_tree_lookup_element(root, index, 1); 540 void **slot;
541
542 if (!__radix_tree_lookup(root, index, NULL, &slot))
543 return NULL;
544 return slot;
517} 545}
518EXPORT_SYMBOL(radix_tree_lookup_slot); 546EXPORT_SYMBOL(radix_tree_lookup_slot);
519 547
@@ -531,7 +559,7 @@ EXPORT_SYMBOL(radix_tree_lookup_slot);
531 */ 559 */
532void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) 560void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
533{ 561{
534 return radix_tree_lookup_element(root, index, 0); 562 return __radix_tree_lookup(root, index, NULL, NULL);
535} 563}
536EXPORT_SYMBOL(radix_tree_lookup); 564EXPORT_SYMBOL(radix_tree_lookup);
537 565
@@ -1262,6 +1290,56 @@ static inline void radix_tree_shrink(struct radix_tree_root *root)
1262} 1290}
1263 1291
1264/** 1292/**
1293 * __radix_tree_delete_node - try to free node after clearing a slot
1294 * @root: radix tree root
1295 * @index: index key
1296 * @node: node containing @index
1297 *
1298 * After clearing the slot at @index in @node from radix tree
1299 * rooted at @root, call this function to attempt freeing the
1300 * node and shrinking the tree.
1301 *
1302 * Returns %true if @node was freed, %false otherwise.
1303 */
1304bool __radix_tree_delete_node(struct radix_tree_root *root, unsigned long index,
1305 struct radix_tree_node *node)
1306{
1307 bool deleted = false;
1308
1309 do {
1310 struct radix_tree_node *parent;
1311
1312 if (node->count) {
1313 if (node == indirect_to_ptr(root->rnode)) {
1314 radix_tree_shrink(root);
1315 if (root->height == 0)
1316 deleted = true;
1317 }
1318 return deleted;
1319 }
1320
1321 parent = node->parent;
1322 if (parent) {
1323 index >>= RADIX_TREE_MAP_SHIFT;
1324
1325 parent->slots[index & RADIX_TREE_MAP_MASK] = NULL;
1326 parent->count--;
1327 } else {
1328 root_tag_clear_all(root);
1329 root->height = 0;
1330 root->rnode = NULL;
1331 }
1332
1333 radix_tree_node_free(node);
1334 deleted = true;
1335
1336 node = parent;
1337 } while (node);
1338
1339 return deleted;
1340}
1341
1342/**
1265 * radix_tree_delete_item - delete an item from a radix tree 1343 * radix_tree_delete_item - delete an item from a radix tree
1266 * @root: radix tree root 1344 * @root: radix tree root
1267 * @index: index key 1345 * @index: index key
@@ -1275,43 +1353,26 @@ static inline void radix_tree_shrink(struct radix_tree_root *root)
1275void *radix_tree_delete_item(struct radix_tree_root *root, 1353void *radix_tree_delete_item(struct radix_tree_root *root,
1276 unsigned long index, void *item) 1354 unsigned long index, void *item)
1277{ 1355{
1278 struct radix_tree_node *node = NULL; 1356 struct radix_tree_node *node;
1279 struct radix_tree_node *slot = NULL; 1357 unsigned int offset;
1280 struct radix_tree_node *to_free; 1358 void **slot;
1281 unsigned int height, shift; 1359 void *entry;
1282 int tag; 1360 int tag;
1283 int uninitialized_var(offset);
1284 1361
1285 height = root->height; 1362 entry = __radix_tree_lookup(root, index, &node, &slot);
1286 if (index > radix_tree_maxindex(height)) 1363 if (!entry)
1287 goto out; 1364 return NULL;
1288 1365
1289 slot = root->rnode; 1366 if (item && entry != item)
1290 if (height == 0) { 1367 return NULL;
1368
1369 if (!node) {
1291 root_tag_clear_all(root); 1370 root_tag_clear_all(root);
1292 root->rnode = NULL; 1371 root->rnode = NULL;
1293 goto out; 1372 return entry;
1294 } 1373 }
1295 slot = indirect_to_ptr(slot);
1296 shift = height * RADIX_TREE_MAP_SHIFT;
1297
1298 do {
1299 if (slot == NULL)
1300 goto out;
1301
1302 shift -= RADIX_TREE_MAP_SHIFT;
1303 offset = (index >> shift) & RADIX_TREE_MAP_MASK;
1304 node = slot;
1305 slot = slot->slots[offset];
1306 } while (shift);
1307
1308 if (slot == NULL)
1309 goto out;
1310 1374
1311 if (item && slot != item) { 1375 offset = index & RADIX_TREE_MAP_MASK;
1312 slot = NULL;
1313 goto out;
1314 }
1315 1376
1316 /* 1377 /*
1317 * Clear all tags associated with the item to be deleted. 1378 * Clear all tags associated with the item to be deleted.
@@ -1322,40 +1383,12 @@ void *radix_tree_delete_item(struct radix_tree_root *root,
1322 radix_tree_tag_clear(root, index, tag); 1383 radix_tree_tag_clear(root, index, tag);
1323 } 1384 }
1324 1385
1325 to_free = NULL; 1386 node->slots[offset] = NULL;
1326 /* Now free the nodes we do not need anymore */ 1387 node->count--;
1327 while (node) {
1328 node->slots[offset] = NULL;
1329 node->count--;
1330 /*
1331 * Queue the node for deferred freeing after the
1332 * last reference to it disappears (set NULL, above).
1333 */
1334 if (to_free)
1335 radix_tree_node_free(to_free);
1336
1337 if (node->count) {
1338 if (node == indirect_to_ptr(root->rnode))
1339 radix_tree_shrink(root);
1340 goto out;
1341 }
1342
1343 /* Node with zero slots in use so free it */
1344 to_free = node;
1345
1346 index >>= RADIX_TREE_MAP_SHIFT;
1347 offset = index & RADIX_TREE_MAP_MASK;
1348 node = node->parent;
1349 }
1350 1388
1351 root_tag_clear_all(root); 1389 __radix_tree_delete_node(root, index, node);
1352 root->height = 0;
1353 root->rnode = NULL;
1354 if (to_free)
1355 radix_tree_node_free(to_free);
1356 1390
1357out: 1391 return entry;
1358 return slot;
1359} 1392}
1360EXPORT_SYMBOL(radix_tree_delete_item); 1393EXPORT_SYMBOL(radix_tree_delete_item);
1361 1394