aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMatthew Wilcox <mawilcox@microsoft.com>2017-11-30 13:45:11 -0500
committerMatthew Wilcox <mawilcox@microsoft.com>2018-02-06 16:41:29 -0500
commit6ce711f2750031d12cec91384ac5cfa0a485b60a (patch)
treed1d3b3754641b22e51cc0fdeecb02a0ac7b31b54 /lib
parent72fd6c7be701d80eef34da305a6294c61520fe13 (diff)
idr: Make 1-based IDRs more efficient
About 20% of the IDR users in the kernel want the allocated IDs to start at 1. The implementation currently searches all the way down the left hand side of the tree, finds no free ID other than ID 0, walks all the way back up, and then all the way down again. This patch 'rebases' the ID so we fill the entire radix tree, rather than leave a gap at 0. Chris Wilson says: "I did the quick hack of allocating index 0 of the idr and that eradicated idr_get_free() from being at the top of the profiles for the many-object stress tests. This improvement will be much appreciated." Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/idr.c70
1 files changed, 61 insertions, 9 deletions
diff --git a/lib/idr.c b/lib/idr.c
index b47055efceb0..c98d77fcf393 100644
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -36,18 +36,21 @@ int idr_alloc_u32(struct idr *idr, void *ptr, u32 *nextid,
36{ 36{
37 struct radix_tree_iter iter; 37 struct radix_tree_iter iter;
38 void __rcu **slot; 38 void __rcu **slot;
39 int base = idr->idr_base;
40 int id = *nextid;
39 41
40 if (WARN_ON_ONCE(radix_tree_is_internal_node(ptr))) 42 if (WARN_ON_ONCE(radix_tree_is_internal_node(ptr)))
41 return -EINVAL; 43 return -EINVAL;
42 if (WARN_ON_ONCE(!(idr->idr_rt.gfp_mask & ROOT_IS_IDR))) 44 if (WARN_ON_ONCE(!(idr->idr_rt.gfp_mask & ROOT_IS_IDR)))
43 idr->idr_rt.gfp_mask |= IDR_RT_MARKER; 45 idr->idr_rt.gfp_mask |= IDR_RT_MARKER;
44 46
45 radix_tree_iter_init(&iter, *nextid); 47 id = (id < base) ? 0 : id - base;
46 slot = idr_get_free(&idr->idr_rt, &iter, gfp, max); 48 radix_tree_iter_init(&iter, id);
49 slot = idr_get_free(&idr->idr_rt, &iter, gfp, max - base);
47 if (IS_ERR(slot)) 50 if (IS_ERR(slot))
48 return PTR_ERR(slot); 51 return PTR_ERR(slot);
49 52
50 *nextid = iter.index; 53 *nextid = iter.index + base;
51 /* there is a memory barrier inside radix_tree_iter_replace() */ 54 /* there is a memory barrier inside radix_tree_iter_replace() */
52 radix_tree_iter_replace(&idr->idr_rt, &iter, slot, ptr); 55 radix_tree_iter_replace(&idr->idr_rt, &iter, slot, ptr);
53 radix_tree_iter_tag_clear(&idr->idr_rt, &iter, IDR_FREE); 56 radix_tree_iter_tag_clear(&idr->idr_rt, &iter, IDR_FREE);
@@ -136,6 +139,46 @@ int idr_alloc_cyclic(struct idr *idr, void *ptr, int start, int end, gfp_t gfp)
136EXPORT_SYMBOL(idr_alloc_cyclic); 139EXPORT_SYMBOL(idr_alloc_cyclic);
137 140
138/** 141/**
142 * idr_remove() - Remove an ID from the IDR.
143 * @idr: IDR handle.
144 * @id: Pointer ID.
145 *
146 * Removes this ID from the IDR. If the ID was not previously in the IDR,
147 * this function returns %NULL.
148 *
149 * Since this function modifies the IDR, the caller should provide their
150 * own locking to ensure that concurrent modification of the same IDR is
151 * not possible.
152 *
153 * Return: The pointer formerly associated with this ID.
154 */
155void *idr_remove(struct idr *idr, unsigned long id)
156{
157 return radix_tree_delete_item(&idr->idr_rt, id - idr->idr_base, NULL);
158}
159EXPORT_SYMBOL_GPL(idr_remove);
160
161/**
162 * idr_find() - Return pointer for given ID.
163 * @idr: IDR handle.
164 * @id: Pointer ID.
165 *
166 * Looks up the pointer associated with this ID. A %NULL pointer may
167 * indicate that @id is not allocated or that the %NULL pointer was
168 * associated with this ID.
169 *
170 * This function can be called under rcu_read_lock(), given that the leaf
171 * pointers lifetimes are correctly managed.
172 *
173 * Return: The pointer associated with this ID.
174 */
175void *idr_find(const struct idr *idr, unsigned long id)
176{
177 return radix_tree_lookup(&idr->idr_rt, id - idr->idr_base);
178}
179EXPORT_SYMBOL_GPL(idr_find);
180
181/**
139 * idr_for_each() - Iterate through all stored pointers. 182 * idr_for_each() - Iterate through all stored pointers.
140 * @idr: IDR handle. 183 * @idr: IDR handle.
141 * @fn: Function to be called for each pointer. 184 * @fn: Function to be called for each pointer.
@@ -157,13 +200,14 @@ int idr_for_each(const struct idr *idr,
157{ 200{
158 struct radix_tree_iter iter; 201 struct radix_tree_iter iter;
159 void __rcu **slot; 202 void __rcu **slot;
203 int base = idr->idr_base;
160 204
161 radix_tree_for_each_slot(slot, &idr->idr_rt, &iter, 0) { 205 radix_tree_for_each_slot(slot, &idr->idr_rt, &iter, 0) {
162 int ret; 206 int ret;
163 207
164 if (WARN_ON_ONCE(iter.index > INT_MAX)) 208 if (WARN_ON_ONCE(iter.index > INT_MAX))
165 break; 209 break;
166 ret = fn(iter.index, rcu_dereference_raw(*slot), data); 210 ret = fn(iter.index + base, rcu_dereference_raw(*slot), data);
167 if (ret) 211 if (ret)
168 return ret; 212 return ret;
169 } 213 }
@@ -186,15 +230,19 @@ void *idr_get_next(struct idr *idr, int *nextid)
186{ 230{
187 struct radix_tree_iter iter; 231 struct radix_tree_iter iter;
188 void __rcu **slot; 232 void __rcu **slot;
233 int base = idr->idr_base;
234 int id = *nextid;
189 235
190 slot = radix_tree_iter_find(&idr->idr_rt, &iter, *nextid); 236 id = (id < base) ? 0 : id - base;
237 slot = radix_tree_iter_find(&idr->idr_rt, &iter, id);
191 if (!slot) 238 if (!slot)
192 return NULL; 239 return NULL;
240 id = iter.index + base;
193 241
194 if (WARN_ON_ONCE(iter.index > INT_MAX)) 242 if (WARN_ON_ONCE(id > INT_MAX))
195 return NULL; 243 return NULL;
196 244
197 *nextid = iter.index; 245 *nextid = id;
198 return rcu_dereference_raw(*slot); 246 return rcu_dereference_raw(*slot);
199} 247}
200EXPORT_SYMBOL(idr_get_next); 248EXPORT_SYMBOL(idr_get_next);
@@ -213,12 +261,15 @@ void *idr_get_next_ul(struct idr *idr, unsigned long *nextid)
213{ 261{
214 struct radix_tree_iter iter; 262 struct radix_tree_iter iter;
215 void __rcu **slot; 263 void __rcu **slot;
264 unsigned long base = idr->idr_base;
265 unsigned long id = *nextid;
216 266
217 slot = radix_tree_iter_find(&idr->idr_rt, &iter, *nextid); 267 id = (id < base) ? 0 : id - base;
268 slot = radix_tree_iter_find(&idr->idr_rt, &iter, id);
218 if (!slot) 269 if (!slot)
219 return NULL; 270 return NULL;
220 271
221 *nextid = iter.index; 272 *nextid = iter.index + base;
222 return rcu_dereference_raw(*slot); 273 return rcu_dereference_raw(*slot);
223} 274}
224EXPORT_SYMBOL(idr_get_next_ul); 275EXPORT_SYMBOL(idr_get_next_ul);
@@ -245,6 +296,7 @@ void *idr_replace(struct idr *idr, void *ptr, unsigned long id)
245 296
246 if (WARN_ON_ONCE(radix_tree_is_internal_node(ptr))) 297 if (WARN_ON_ONCE(radix_tree_is_internal_node(ptr)))
247 return ERR_PTR(-EINVAL); 298 return ERR_PTR(-EINVAL);
299 id -= idr->idr_base;
248 300
249 entry = __radix_tree_lookup(&idr->idr_rt, id, &node, &slot); 301 entry = __radix_tree_lookup(&idr->idr_rt, id, &node, &slot);
250 if (!slot || radix_tree_tag_get(&idr->idr_rt, id, IDR_FREE)) 302 if (!slot || radix_tree_tag_get(&idr->idr_rt, id, IDR_FREE))