diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/radix-tree.c | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 348eaefbed74..a2f9da59c197 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c | |||
@@ -1203,6 +1203,98 @@ radix_tree_gang_lookup_tag_slot(struct radix_tree_root *root, void ***results, | |||
1203 | } | 1203 | } |
1204 | EXPORT_SYMBOL(radix_tree_gang_lookup_tag_slot); | 1204 | EXPORT_SYMBOL(radix_tree_gang_lookup_tag_slot); |
1205 | 1205 | ||
1206 | #if defined(CONFIG_SHMEM) && defined(CONFIG_SWAP) | ||
1207 | #include <linux/sched.h> /* for cond_resched() */ | ||
1208 | |||
1209 | /* | ||
1210 | * This linear search is at present only useful to shmem_unuse_inode(). | ||
1211 | */ | ||
1212 | static unsigned long __locate(struct radix_tree_node *slot, void *item, | ||
1213 | unsigned long index, unsigned long *found_index) | ||
1214 | { | ||
1215 | unsigned int shift, height; | ||
1216 | unsigned long i; | ||
1217 | |||
1218 | height = slot->height; | ||
1219 | shift = (height-1) * RADIX_TREE_MAP_SHIFT; | ||
1220 | |||
1221 | for ( ; height > 1; height--) { | ||
1222 | i = (index >> shift) & RADIX_TREE_MAP_MASK; | ||
1223 | for (;;) { | ||
1224 | if (slot->slots[i] != NULL) | ||
1225 | break; | ||
1226 | index &= ~((1UL << shift) - 1); | ||
1227 | index += 1UL << shift; | ||
1228 | if (index == 0) | ||
1229 | goto out; /* 32-bit wraparound */ | ||
1230 | i++; | ||
1231 | if (i == RADIX_TREE_MAP_SIZE) | ||
1232 | goto out; | ||
1233 | } | ||
1234 | |||
1235 | shift -= RADIX_TREE_MAP_SHIFT; | ||
1236 | slot = rcu_dereference_raw(slot->slots[i]); | ||
1237 | if (slot == NULL) | ||
1238 | goto out; | ||
1239 | } | ||
1240 | |||
1241 | /* Bottom level: check items */ | ||
1242 | for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) { | ||
1243 | if (slot->slots[i] == item) { | ||
1244 | *found_index = index + i; | ||
1245 | index = 0; | ||
1246 | goto out; | ||
1247 | } | ||
1248 | } | ||
1249 | index += RADIX_TREE_MAP_SIZE; | ||
1250 | out: | ||
1251 | return index; | ||
1252 | } | ||
1253 | |||
1254 | /** | ||
1255 | * radix_tree_locate_item - search through radix tree for item | ||
1256 | * @root: radix tree root | ||
1257 | * @item: item to be found | ||
1258 | * | ||
1259 | * Returns index where item was found, or -1 if not found. | ||
1260 | * Caller must hold no lock (since this time-consuming function needs | ||
1261 | * to be preemptible), and must check afterwards if item is still there. | ||
1262 | */ | ||
1263 | unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item) | ||
1264 | { | ||
1265 | struct radix_tree_node *node; | ||
1266 | unsigned long max_index; | ||
1267 | unsigned long cur_index = 0; | ||
1268 | unsigned long found_index = -1; | ||
1269 | |||
1270 | do { | ||
1271 | rcu_read_lock(); | ||
1272 | node = rcu_dereference_raw(root->rnode); | ||
1273 | if (!radix_tree_is_indirect_ptr(node)) { | ||
1274 | rcu_read_unlock(); | ||
1275 | if (node == item) | ||
1276 | found_index = 0; | ||
1277 | break; | ||
1278 | } | ||
1279 | |||
1280 | node = indirect_to_ptr(node); | ||
1281 | max_index = radix_tree_maxindex(node->height); | ||
1282 | if (cur_index > max_index) | ||
1283 | break; | ||
1284 | |||
1285 | cur_index = __locate(node, item, cur_index, &found_index); | ||
1286 | rcu_read_unlock(); | ||
1287 | cond_resched(); | ||
1288 | } while (cur_index != 0 && cur_index <= max_index); | ||
1289 | |||
1290 | return found_index; | ||
1291 | } | ||
1292 | #else | ||
1293 | unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item) | ||
1294 | { | ||
1295 | return -1; | ||
1296 | } | ||
1297 | #endif /* CONFIG_SHMEM && CONFIG_SWAP */ | ||
1206 | 1298 | ||
1207 | /** | 1299 | /** |
1208 | * radix_tree_shrink - shrink height of a radix tree to minimal | 1300 | * radix_tree_shrink - shrink height of a radix tree to minimal |