aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Piggin <npiggin@suse.de>2006-06-23 05:03:22 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-23 10:42:49 -0400
commit612d6c19db2fd0dc97b0fa370613ecd4a305ffc3 (patch)
tree3ab670895b5c3e389ff922192a572cbfd8159d03
parent929f97276bcf7f4a95272ed08a85339b98ba210d (diff)
[PATCH] radix-tree: direct data
The ability to have height 0 radix trees (a direct pointer to the data item rather than going through a full node->slot) quietly disappeared with old-2.6-bkcvs commit ffee171812d51652f9ba284302d9e5c5cc14bdfd. On 64-bit machines this causes nearly 600 bytes to be used for every <= 4K file in pagecache. Re-introduce this feature, root tags stored in spare ->gfp_mask bits. Simplify radix_tree_delete's complex tag clearing arrangement (which would become even more complex) by just falling back to tag clearing functions (the pagecache radix-tree never uses this path anyway, so the icache savings will mean it's actually a speedup). On my 4GB G5, this saves 8MB RAM per kernel kernel source+object tree in pagecache. Pagecache lookup, insertion, and removal speed for small files will also be improved. This makes RCU radix tree harder, but it's worth it. Signed-off-by: Nick Piggin <npiggin@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--include/linux/radix-tree.h5
-rw-r--r--lib/radix-tree.c192
2 files changed, 114 insertions, 83 deletions
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h
index dd83cca28001..9158a68140c9 100644
--- a/include/linux/radix-tree.h
+++ b/include/linux/radix-tree.h
@@ -23,6 +23,9 @@
23#include <linux/preempt.h> 23#include <linux/preempt.h>
24#include <linux/types.h> 24#include <linux/types.h>
25 25
26#define RADIX_TREE_MAX_TAGS 2
27
28/* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */
26struct radix_tree_root { 29struct radix_tree_root {
27 unsigned int height; 30 unsigned int height;
28 gfp_t gfp_mask; 31 gfp_t gfp_mask;
@@ -45,8 +48,6 @@ do { \
45 (root)->rnode = NULL; \ 48 (root)->rnode = NULL; \
46} while (0) 49} while (0)
47 50
48#define RADIX_TREE_MAX_TAGS 2
49
50int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); 51int radix_tree_insert(struct radix_tree_root *, unsigned long, void *);
51void *radix_tree_lookup(struct radix_tree_root *, unsigned long); 52void *radix_tree_lookup(struct radix_tree_root *, unsigned long);
52void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); 53void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long);
diff --git a/lib/radix-tree.c b/lib/radix-tree.c
index 7097bb239e40..35a2d93b3528 100644
--- a/lib/radix-tree.c
+++ b/lib/radix-tree.c
@@ -74,6 +74,11 @@ struct radix_tree_preload {
74}; 74};
75DEFINE_PER_CPU(struct radix_tree_preload, radix_tree_preloads) = { 0, }; 75DEFINE_PER_CPU(struct radix_tree_preload, radix_tree_preloads) = { 0, };
76 76
77static inline gfp_t root_gfp_mask(struct radix_tree_root *root)
78{
79 return root->gfp_mask & __GFP_BITS_MASK;
80}
81
77/* 82/*
78 * This assumes that the caller has performed appropriate preallocation, and 83 * This assumes that the caller has performed appropriate preallocation, and
79 * that the caller has pinned this thread of control to the current CPU. 84 * that the caller has pinned this thread of control to the current CPU.
@@ -82,9 +87,10 @@ static struct radix_tree_node *
82radix_tree_node_alloc(struct radix_tree_root *root) 87radix_tree_node_alloc(struct radix_tree_root *root)
83{ 88{
84 struct radix_tree_node *ret; 89 struct radix_tree_node *ret;
90 gfp_t gfp_mask = root_gfp_mask(root);
85 91
86 ret = kmem_cache_alloc(radix_tree_node_cachep, root->gfp_mask); 92 ret = kmem_cache_alloc(radix_tree_node_cachep, gfp_mask);
87 if (ret == NULL && !(root->gfp_mask & __GFP_WAIT)) { 93 if (ret == NULL && !(gfp_mask & __GFP_WAIT)) {
88 struct radix_tree_preload *rtp; 94 struct radix_tree_preload *rtp;
89 95
90 rtp = &__get_cpu_var(radix_tree_preloads); 96 rtp = &__get_cpu_var(radix_tree_preloads);
@@ -152,6 +158,27 @@ static inline int tag_get(struct radix_tree_node *node, unsigned int tag,
152 return test_bit(offset, node->tags[tag]); 158 return test_bit(offset, node->tags[tag]);
153} 159}
154 160
161static inline void root_tag_set(struct radix_tree_root *root, unsigned int tag)
162{
163 root->gfp_mask |= (1 << (tag + __GFP_BITS_SHIFT));
164}
165
166
167static inline void root_tag_clear(struct radix_tree_root *root, unsigned int tag)
168{
169 root->gfp_mask &= ~(1 << (tag + __GFP_BITS_SHIFT));
170}
171
172static inline void root_tag_clear_all(struct radix_tree_root *root)
173{
174 root->gfp_mask &= __GFP_BITS_MASK;
175}
176
177static inline int root_tag_get(struct radix_tree_root *root, unsigned int tag)
178{
179 return root->gfp_mask & (1 << (tag + __GFP_BITS_SHIFT));
180}
181
155/* 182/*
156 * Returns 1 if any slot in the node has this tag set. 183 * Returns 1 if any slot in the node has this tag set.
157 * Otherwise returns 0. 184 * Otherwise returns 0.
@@ -182,7 +209,6 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
182{ 209{
183 struct radix_tree_node *node; 210 struct radix_tree_node *node;
184 unsigned int height; 211 unsigned int height;
185 char tags[RADIX_TREE_MAX_TAGS];
186 int tag; 212 int tag;
187 213
188 /* Figure out what the height should be. */ 214 /* Figure out what the height should be. */
@@ -195,16 +221,6 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
195 goto out; 221 goto out;
196 } 222 }
197 223
198 /*
199 * Prepare the tag status of the top-level node for propagation
200 * into the newly-pushed top-level node(s)
201 */
202 for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
203 tags[tag] = 0;
204 if (any_tag_set(root->rnode, tag))
205 tags[tag] = 1;
206 }
207
208 do { 224 do {
209 if (!(node = radix_tree_node_alloc(root))) 225 if (!(node = radix_tree_node_alloc(root)))
210 return -ENOMEM; 226 return -ENOMEM;
@@ -214,7 +230,7 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
214 230
215 /* Propagate the aggregated tag info into the new root */ 231 /* Propagate the aggregated tag info into the new root */
216 for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { 232 for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
217 if (tags[tag]) 233 if (root_tag_get(root, tag))
218 tag_set(node, tag, 0); 234 tag_set(node, tag, 0);
219 } 235 }
220 236
@@ -243,8 +259,7 @@ int radix_tree_insert(struct radix_tree_root *root,
243 int error; 259 int error;
244 260
245 /* Make sure the tree is high enough. */ 261 /* Make sure the tree is high enough. */
246 if ((!index && !root->rnode) || 262 if (index > radix_tree_maxindex(root->height)) {
247 index > radix_tree_maxindex(root->height)) {
248 error = radix_tree_extend(root, index); 263 error = radix_tree_extend(root, index);
249 if (error) 264 if (error)
250 return error; 265 return error;
@@ -255,7 +270,7 @@ int radix_tree_insert(struct radix_tree_root *root,
255 shift = (height-1) * RADIX_TREE_MAP_SHIFT; 270 shift = (height-1) * RADIX_TREE_MAP_SHIFT;
256 271
257 offset = 0; /* uninitialised var warning */ 272 offset = 0; /* uninitialised var warning */
258 do { 273 while (height > 0) {
259 if (slot == NULL) { 274 if (slot == NULL) {
260 /* Have to add a child node. */ 275 /* Have to add a child node. */
261 if (!(slot = radix_tree_node_alloc(root))) 276 if (!(slot = radix_tree_node_alloc(root)))
@@ -273,16 +288,21 @@ int radix_tree_insert(struct radix_tree_root *root,
273 slot = node->slots[offset]; 288 slot = node->slots[offset];
274 shift -= RADIX_TREE_MAP_SHIFT; 289 shift -= RADIX_TREE_MAP_SHIFT;
275 height--; 290 height--;
276 } while (height > 0); 291 }
277 292
278 if (slot != NULL) 293 if (slot != NULL)
279 return -EEXIST; 294 return -EEXIST;
280 295
281 BUG_ON(!node); 296 if (node) {
282 node->count++; 297 node->count++;
283 node->slots[offset] = item; 298 node->slots[offset] = item;
284 BUG_ON(tag_get(node, 0, offset)); 299 BUG_ON(tag_get(node, 0, offset));
285 BUG_ON(tag_get(node, 1, offset)); 300 BUG_ON(tag_get(node, 1, offset));
301 } else {
302 root->rnode = item;
303 BUG_ON(root_tag_get(root, 0));
304 BUG_ON(root_tag_get(root, 1));
305 }
286 306
287 return 0; 307 return 0;
288} 308}
@@ -295,9 +315,13 @@ static inline void **__lookup_slot(struct radix_tree_root *root,
295 struct radix_tree_node **slot; 315 struct radix_tree_node **slot;
296 316
297 height = root->height; 317 height = root->height;
318
298 if (index > radix_tree_maxindex(height)) 319 if (index > radix_tree_maxindex(height))
299 return NULL; 320 return NULL;
300 321
322 if (height == 0 && root->rnode)
323 return (void **)&root->rnode;
324
301 shift = (height-1) * RADIX_TREE_MAP_SHIFT; 325 shift = (height-1) * RADIX_TREE_MAP_SHIFT;
302 slot = &root->rnode; 326 slot = &root->rnode;
303 327
@@ -368,8 +392,8 @@ void *radix_tree_tag_set(struct radix_tree_root *root,
368 if (index > radix_tree_maxindex(height)) 392 if (index > radix_tree_maxindex(height))
369 return NULL; 393 return NULL;
370 394
371 shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
372 slot = root->rnode; 395 slot = root->rnode;
396 shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
373 397
374 while (height > 0) { 398 while (height > 0) {
375 int offset; 399 int offset;
@@ -383,6 +407,10 @@ void *radix_tree_tag_set(struct radix_tree_root *root,
383 height--; 407 height--;
384 } 408 }
385 409
410 /* set the root's tag bit */
411 if (slot && !root_tag_get(root, tag))
412 root_tag_set(root, tag);
413
386 return slot; 414 return slot;
387} 415}
388EXPORT_SYMBOL(radix_tree_tag_set); 416EXPORT_SYMBOL(radix_tree_tag_set);
@@ -405,9 +433,8 @@ void *radix_tree_tag_clear(struct radix_tree_root *root,
405 unsigned long index, unsigned int tag) 433 unsigned long index, unsigned int tag)
406{ 434{
407 struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; 435 struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path;
408 struct radix_tree_node *slot; 436 struct radix_tree_node *slot = NULL;
409 unsigned int height, shift; 437 unsigned int height, shift;
410 void *ret = NULL;
411 438
412 height = root->height; 439 height = root->height;
413 if (index > radix_tree_maxindex(height)) 440 if (index > radix_tree_maxindex(height))
@@ -432,20 +459,24 @@ void *radix_tree_tag_clear(struct radix_tree_root *root,
432 height--; 459 height--;
433 } 460 }
434 461
435 ret = slot; 462 if (slot == NULL)
436 if (ret == NULL)
437 goto out; 463 goto out;
438 464
439 do { 465 while (pathp->node) {
440 if (!tag_get(pathp->node, tag, pathp->offset)) 466 if (!tag_get(pathp->node, tag, pathp->offset))
441 goto out; 467 goto out;
442 tag_clear(pathp->node, tag, pathp->offset); 468 tag_clear(pathp->node, tag, pathp->offset);
443 if (any_tag_set(pathp->node, tag)) 469 if (any_tag_set(pathp->node, tag))
444 goto out; 470 goto out;
445 pathp--; 471 pathp--;
446 } while (pathp->node); 472 }
473
474 /* clear the root's tag bit */
475 if (root_tag_get(root, tag))
476 root_tag_clear(root, tag);
477
447out: 478out:
448 return ret; 479 return slot;
449} 480}
450EXPORT_SYMBOL(radix_tree_tag_clear); 481EXPORT_SYMBOL(radix_tree_tag_clear);
451 482
@@ -458,9 +489,8 @@ EXPORT_SYMBOL(radix_tree_tag_clear);
458 * 489 *
459 * Return values: 490 * Return values:
460 * 491 *
461 * 0: tag not present 492 * 0: tag not present or not set
462 * 1: tag present, set 493 * 1: tag set
463 * -1: tag present, unset
464 */ 494 */
465int radix_tree_tag_get(struct radix_tree_root *root, 495int radix_tree_tag_get(struct radix_tree_root *root,
466 unsigned long index, unsigned int tag) 496 unsigned long index, unsigned int tag)
@@ -473,6 +503,13 @@ int radix_tree_tag_get(struct radix_tree_root *root,
473 if (index > radix_tree_maxindex(height)) 503 if (index > radix_tree_maxindex(height))
474 return 0; 504 return 0;
475 505
506 /* check the root's tag bit */
507 if (!root_tag_get(root, tag))
508 return 0;
509
510 if (height == 0)
511 return 1;
512
476 shift = (height - 1) * RADIX_TREE_MAP_SHIFT; 513 shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
477 slot = root->rnode; 514 slot = root->rnode;
478 515
@@ -494,7 +531,7 @@ int radix_tree_tag_get(struct radix_tree_root *root,
494 int ret = tag_get(slot, tag, offset); 531 int ret = tag_get(slot, tag, offset);
495 532
496 BUG_ON(ret && saw_unset_tag); 533 BUG_ON(ret && saw_unset_tag);
497 return ret ? 1 : -1; 534 return ret;
498 } 535 }
499 slot = slot->slots[offset]; 536 slot = slot->slots[offset];
500 shift -= RADIX_TREE_MAP_SHIFT; 537 shift -= RADIX_TREE_MAP_SHIFT;
@@ -514,8 +551,11 @@ __lookup(struct radix_tree_root *root, void **results, unsigned long index,
514 unsigned long i; 551 unsigned long i;
515 552
516 height = root->height; 553 height = root->height;
517 if (height == 0) 554 if (height == 0) {
555 if (root->rnode && index == 0)
556 results[nr_found++] = root->rnode;
518 goto out; 557 goto out;
558 }
519 559
520 shift = (height-1) * RADIX_TREE_MAP_SHIFT; 560 shift = (height-1) * RADIX_TREE_MAP_SHIFT;
521 slot = root->rnode; 561 slot = root->rnode;
@@ -603,10 +643,16 @@ __lookup_tag(struct radix_tree_root *root, void **results, unsigned long index,
603 unsigned int height = root->height; 643 unsigned int height = root->height;
604 struct radix_tree_node *slot; 644 struct radix_tree_node *slot;
605 645
646 if (height == 0) {
647 if (root->rnode && index == 0)
648 results[nr_found++] = root->rnode;
649 goto out;
650 }
651
606 shift = (height - 1) * RADIX_TREE_MAP_SHIFT; 652 shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
607 slot = root->rnode; 653 slot = root->rnode;
608 654
609 while (height > 0) { 655 do {
610 unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK; 656 unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK;
611 657
612 for ( ; i < RADIX_TREE_MAP_SIZE; i++) { 658 for ( ; i < RADIX_TREE_MAP_SIZE; i++) {
@@ -637,7 +683,7 @@ __lookup_tag(struct radix_tree_root *root, void **results, unsigned long index,
637 } 683 }
638 shift -= RADIX_TREE_MAP_SHIFT; 684 shift -= RADIX_TREE_MAP_SHIFT;
639 slot = slot->slots[i]; 685 slot = slot->slots[i];
640 } 686 } while (height > 0);
641out: 687out:
642 *next_index = index; 688 *next_index = index;
643 return nr_found; 689 return nr_found;
@@ -665,6 +711,10 @@ radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results,
665 unsigned long cur_index = first_index; 711 unsigned long cur_index = first_index;
666 unsigned int ret = 0; 712 unsigned int ret = 0;
667 713
714 /* check the root's tag bit */
715 if (!root_tag_get(root, tag))
716 return 0;
717
668 while (ret < max_items) { 718 while (ret < max_items) {
669 unsigned int nr_found; 719 unsigned int nr_found;
670 unsigned long next_index; /* Index of next search */ 720 unsigned long next_index; /* Index of next search */
@@ -689,7 +739,7 @@ EXPORT_SYMBOL(radix_tree_gang_lookup_tag);
689static inline void radix_tree_shrink(struct radix_tree_root *root) 739static inline void radix_tree_shrink(struct radix_tree_root *root)
690{ 740{
691 /* try to shrink tree height */ 741 /* try to shrink tree height */
692 while (root->height > 1 && 742 while (root->height > 0 &&
693 root->rnode->count == 1 && 743 root->rnode->count == 1 &&
694 root->rnode->slots[0]) { 744 root->rnode->slots[0]) {
695 struct radix_tree_node *to_free = root->rnode; 745 struct radix_tree_node *to_free = root->rnode;
@@ -717,12 +767,8 @@ static inline void radix_tree_shrink(struct radix_tree_root *root)
717void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) 767void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
718{ 768{
719 struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; 769 struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path;
720 struct radix_tree_path *orig_pathp; 770 struct radix_tree_node *slot = NULL;
721 struct radix_tree_node *slot;
722 unsigned int height, shift; 771 unsigned int height, shift;
723 void *ret = NULL;
724 char tags[RADIX_TREE_MAX_TAGS];
725 int nr_cleared_tags;
726 int tag; 772 int tag;
727 int offset; 773 int offset;
728 774
@@ -730,11 +776,17 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
730 if (index > radix_tree_maxindex(height)) 776 if (index > radix_tree_maxindex(height))
731 goto out; 777 goto out;
732 778
779 slot = root->rnode;
780 if (height == 0 && root->rnode) {
781 root_tag_clear_all(root);
782 root->rnode = NULL;
783 goto out;
784 }
785
733 shift = (height - 1) * RADIX_TREE_MAP_SHIFT; 786 shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
734 pathp->node = NULL; 787 pathp->node = NULL;
735 slot = root->rnode;
736 788
737 for ( ; height > 0; height--) { 789 do {
738 if (slot == NULL) 790 if (slot == NULL)
739 goto out; 791 goto out;
740 792
@@ -744,44 +796,22 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
744 pathp->node = slot; 796 pathp->node = slot;
745 slot = slot->slots[offset]; 797 slot = slot->slots[offset];
746 shift -= RADIX_TREE_MAP_SHIFT; 798 shift -= RADIX_TREE_MAP_SHIFT;
747 } 799 height--;
800 } while (height > 0);
748 801
749 ret = slot; 802 if (slot == NULL)
750 if (ret == NULL)
751 goto out; 803 goto out;
752 804
753 orig_pathp = pathp;
754
755 /* 805 /*
756 * Clear all tags associated with the just-deleted item 806 * Clear all tags associated with the just-deleted item
757 */ 807 */
758 nr_cleared_tags = 0;
759 for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { 808 for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
760 tags[tag] = 1; 809 if (tag_get(pathp->node, tag, pathp->offset))
761 if (tag_get(pathp->node, tag, pathp->offset)) { 810 radix_tree_tag_clear(root, index, tag);
762 tag_clear(pathp->node, tag, pathp->offset);
763 if (!any_tag_set(pathp->node, tag)) {
764 tags[tag] = 0;
765 nr_cleared_tags++;
766 }
767 }
768 }
769
770 for (pathp--; nr_cleared_tags && pathp->node; pathp--) {
771 for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
772 if (tags[tag])
773 continue;
774
775 tag_clear(pathp->node, tag, pathp->offset);
776 if (any_tag_set(pathp->node, tag)) {
777 tags[tag] = 1;
778 nr_cleared_tags--;
779 }
780 }
781 } 811 }
782 812
783 /* Now free the nodes we do not need anymore */ 813 /* Now free the nodes we do not need anymore */
784 for (pathp = orig_pathp; pathp->node; pathp--) { 814 while (pathp->node) {
785 pathp->node->slots[pathp->offset] = NULL; 815 pathp->node->slots[pathp->offset] = NULL;
786 pathp->node->count--; 816 pathp->node->count--;
787 817
@@ -793,11 +823,15 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
793 823
794 /* Node with zero slots in use so free it */ 824 /* Node with zero slots in use so free it */
795 radix_tree_node_free(pathp->node); 825 radix_tree_node_free(pathp->node);
826
827 pathp--;
796 } 828 }
797 root->rnode = NULL; 829 root_tag_clear_all(root);
798 root->height = 0; 830 root->height = 0;
831 root->rnode = NULL;
832
799out: 833out:
800 return ret; 834 return slot;
801} 835}
802EXPORT_SYMBOL(radix_tree_delete); 836EXPORT_SYMBOL(radix_tree_delete);
803 837
@@ -808,11 +842,7 @@ EXPORT_SYMBOL(radix_tree_delete);
808 */ 842 */
809int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag) 843int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag)
810{ 844{
811 struct radix_tree_node *rnode; 845 return root_tag_get(root, tag);
812 rnode = root->rnode;
813 if (!rnode)
814 return 0;
815 return any_tag_set(rnode, tag);
816} 846}
817EXPORT_SYMBOL(radix_tree_tagged); 847EXPORT_SYMBOL(radix_tree_tagged);
818 848