Skip to content

Commit 99c792d

Browse files
Matthew Wilcoxsfrothwell
authored andcommitted
idr: support storing NULL in the IDR
The radix tree interprets storing NULL as a deleted entry. Several users of the IDR API use NULL as a temporary placeholder, or intentionally convert entries between NULL and non-NULL pointers to keep IDs reserved but not necessarily pointing to a valid entry at any given moment. Adapt the radix tree to cope with NULL pointers when being used as an IDR. Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Matthew Wilcox <[email protected]> Cc: Stephen Rothwell <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 61c9695 commit 99c792d

File tree

3 files changed

+65
-14
lines changed

3 files changed

+65
-14
lines changed

lib/idr.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,17 +148,16 @@ EXPORT_SYMBOL(idr_get_next);
148148
void *idr_replace(struct idr *idr, void *ptr, int id)
149149
{
150150
struct radix_tree_node *node;
151-
void **slot;
151+
void **slot = NULL;
152152
void *entry;
153153

154154
if (id < 0)
155155
return ERR_PTR(-EINVAL);
156-
if (!ptr || radix_tree_is_internal_node(ptr))
156+
if (radix_tree_is_internal_node(ptr))
157157
return ERR_PTR(-EINVAL);
158158

159159
entry = __radix_tree_lookup(&idr->idr_rt, id, &node, &slot);
160-
161-
if (!entry)
160+
if (!slot || radix_tree_tag_get(&idr->idr_rt, id, IDR_FREE))
162161
return ERR_PTR(-ENOENT);
163162

164163
__radix_tree_replace(&idr->idr_rt, node, slot, ptr, NULL, NULL);

lib/radix-tree.c

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ static int radix_tree_extend(struct radix_tree_root *root, gfp_t gfp,
604604
maxshift += RADIX_TREE_MAP_SHIFT;
605605

606606
slot = root->rnode;
607-
if (!slot)
607+
if (!slot && (!is_idr(root) || root_tag_get(root, IDR_FREE)))
608608
goto out;
609609

610610
do {
@@ -615,9 +615,10 @@ static int radix_tree_extend(struct radix_tree_root *root, gfp_t gfp,
615615

616616
if (is_idr(root)) {
617617
all_tag_set(node, IDR_FREE);
618-
if (!root_tag_get(root, IDR_FREE))
618+
if (!root_tag_get(root, IDR_FREE)) {
619619
tag_clear(node, IDR_FREE, 0);
620-
root_tag_set(root, IDR_FREE);
620+
root_tag_set(root, IDR_FREE);
621+
}
621622
} else {
622623
/* Propagate the aggregated tag info to the new child */
623624
for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
@@ -1081,7 +1082,11 @@ static void replace_slot(struct radix_tree_root *root,
10811082

10821083
WARN_ON_ONCE(radix_tree_is_internal_node(item));
10831084

1084-
count = !!item - !!old;
1085+
/* NULL is a valid entry in an IDR; do not adjust count */
1086+
if (is_idr(root))
1087+
count = 0;
1088+
else
1089+
count = !!item - !!old;
10851090
exceptional = !!radix_tree_exceptional_entry(item) -
10861091
!!radix_tree_exceptional_entry(old);
10871092

@@ -1188,6 +1193,8 @@ void radix_tree_replace_slot(struct radix_tree_root *root,
11881193
void radix_tree_iter_replace(struct radix_tree_root *root,
11891194
const struct radix_tree_iter *iter, void **slot, void *item)
11901195
{
1196+
if (is_idr(root) && iter->node)
1197+
iter->node->count++;
11911198
__radix_tree_replace(root, iter->node, slot, item, NULL, NULL);
11921199
}
11931200

@@ -1511,8 +1518,6 @@ int radix_tree_tag_get(struct radix_tree_root *root,
15111518
parent = entry_to_node(node);
15121519
offset = radix_tree_descend(parent, &node, index);
15131520

1514-
if (!node)
1515-
return 0;
15161521
if (!tag_get(parent, tag, offset))
15171522
return 0;
15181523
if (node == RADIX_TREE_RETRY)
@@ -1964,7 +1969,7 @@ void *radix_tree_delete_item(struct radix_tree_root *root,
19641969
int tag;
19651970

19661971
entry = __radix_tree_lookup(root, index, &node, &slot);
1967-
if (!entry)
1972+
if (!entry && !is_idr(root))
19681973
return NULL;
19691974

19701975
if (item && entry != item)
@@ -1981,9 +1986,12 @@ void *radix_tree_delete_item(struct radix_tree_root *root,
19811986

19821987
offset = get_slot_offset(node, slot);
19831988

1984-
if (is_idr(root))
1989+
if (is_idr(root)) {
1990+
if (tag_get(node, IDR_FREE, offset))
1991+
return entry;
19851992
node_tag_set(root, node, IDR_FREE, offset);
1986-
else
1993+
node->count--;
1994+
} else
19871995
for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++)
19881996
node_tag_clear(root, node, tag, offset);
19891997

tools/testing/radix-tree/idr-test.c

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,48 @@ void idr_replace_test(void)
7474
idr_destroy(&idr);
7575
}
7676

77+
/*
78+
* Unlike the radix tree, you can put a NULL pointer -- with care -- into
79+
* the IDR. Some interfaces, like idr_find() do not distinguish between
80+
* "present, value is NULL" and "not present", but that's exactly what some
81+
* users want.
82+
*/
83+
void idr_null_test(void)
84+
{
85+
int i;
86+
DEFINE_IDR(idr);
87+
88+
for (i = 0; i < 10; i++) {
89+
assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == i);
90+
}
91+
92+
assert(idr_replace(&idr, DUMMY_PTR, 3) == NULL);
93+
assert(idr_replace(&idr, DUMMY_PTR, 4) == NULL);
94+
assert(idr_replace(&idr, NULL, 4) == DUMMY_PTR);
95+
assert(idr_replace(&idr, DUMMY_PTR, 11) == ERR_PTR(-ENOENT));
96+
idr_remove(&idr, 5);
97+
assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == 5);
98+
idr_remove(&idr, 5);
99+
100+
for (i = 0; i < 10; i++)
101+
idr_remove(&idr, i);
102+
assert(idr_is_empty(&idr));
103+
104+
assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == 0);
105+
assert(idr_replace(&idr, DUMMY_PTR, 3) == ERR_PTR(-ENOENT));
106+
assert(idr_replace(&idr, DUMMY_PTR, 0) == NULL);
107+
assert(idr_replace(&idr, NULL, 0) == DUMMY_PTR);
108+
109+
idr_destroy(&idr);
110+
assert(idr_is_empty(&idr));
111+
112+
for (i = 1; i < 10; i++) {
113+
assert(idr_alloc(&idr, NULL, 1, 0, GFP_KERNEL) == i);
114+
}
115+
116+
idr_destroy(&idr);
117+
}
118+
77119
void idr_checks(void)
78120
{
79121
unsigned long i;
@@ -112,8 +154,8 @@ void idr_checks(void)
112154
idr_destroy(&idr);
113155

114156
idr_replace_test();
115-
116157
idr_alloc_test();
158+
idr_null_test();
117159
}
118160

119161
/*
@@ -176,10 +218,12 @@ void ida_checks(void)
176218
ida_remove(&ida, id);
177219
assert(ida_is_empty(&ida));
178220
ida_destroy(&ida);
221+
assert(ida_is_empty(&ida));
179222

180223
ida_pre_get(&ida, GFP_KERNEL);
181224
ida_get_new_above(&ida, 1, &id);
182225
ida_destroy(&ida);
226+
assert(ida_is_empty(&ida));
183227

184228
for (j = 1; j < 65537; j *= 2) {
185229
for (i = 0; i < j; i++) {

0 commit comments

Comments
 (0)