diff options
-rw-r--r-- | include/linux/radix-tree.h | 1 | ||||
-rw-r--r-- | lib/radix-tree.c | 92 | ||||
-rw-r--r-- | mm/shmem.c | 38 |
3 files changed, 94 insertions, 37 deletions
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index b7edf8251455..9d4539c52e53 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h | |||
@@ -252,6 +252,7 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root, | |||
252 | unsigned long nr_to_tag, | 252 | unsigned long nr_to_tag, |
253 | unsigned int fromtag, unsigned int totag); | 253 | unsigned int fromtag, unsigned int totag); |
254 | int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag); | 254 | int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag); |
255 | unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item); | ||
255 | 256 | ||
256 | static inline void radix_tree_preload_end(void) | 257 | static inline void radix_tree_preload_end(void) |
257 | { | 258 | { |
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 |
diff --git a/mm/shmem.c b/mm/shmem.c index 3a5be0feb6af..1c702f6f1241 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -357,42 +357,6 @@ export: | |||
357 | } | 357 | } |
358 | 358 | ||
359 | /* | 359 | /* |
360 | * Lockless lookup of swap entry in radix tree, avoiding refcount on pages. | ||
361 | */ | ||
362 | static pgoff_t shmem_find_swap(struct address_space *mapping, void *radswap) | ||
363 | { | ||
364 | void **slots[PAGEVEC_SIZE]; | ||
365 | pgoff_t indices[PAGEVEC_SIZE]; | ||
366 | unsigned int nr_found; | ||
367 | |||
368 | restart: | ||
369 | nr_found = 1; | ||
370 | indices[0] = -1; | ||
371 | while (nr_found) { | ||
372 | pgoff_t index = indices[nr_found - 1] + 1; | ||
373 | unsigned int i; | ||
374 | |||
375 | rcu_read_lock(); | ||
376 | nr_found = radix_tree_gang_lookup_slot(&mapping->page_tree, | ||
377 | slots, indices, index, PAGEVEC_SIZE); | ||
378 | for (i = 0; i < nr_found; i++) { | ||
379 | void *item = radix_tree_deref_slot(slots[i]); | ||
380 | if (radix_tree_deref_retry(item)) { | ||
381 | rcu_read_unlock(); | ||
382 | goto restart; | ||
383 | } | ||
384 | if (item == radswap) { | ||
385 | rcu_read_unlock(); | ||
386 | return indices[i]; | ||
387 | } | ||
388 | } | ||
389 | rcu_read_unlock(); | ||
390 | cond_resched(); | ||
391 | } | ||
392 | return -1; | ||
393 | } | ||
394 | |||
395 | /* | ||
396 | * Remove swap entry from radix tree, free the swap and its page cache. | 360 | * Remove swap entry from radix tree, free the swap and its page cache. |
397 | */ | 361 | */ |
398 | static int shmem_free_swap(struct address_space *mapping, | 362 | static int shmem_free_swap(struct address_space *mapping, |
@@ -612,7 +576,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, | |||
612 | int error; | 576 | int error; |
613 | 577 | ||
614 | radswap = swp_to_radix_entry(swap); | 578 | radswap = swp_to_radix_entry(swap); |
615 | index = shmem_find_swap(mapping, radswap); | 579 | index = radix_tree_locate_item(&mapping->page_tree, radswap); |
616 | if (index == -1) | 580 | if (index == -1) |
617 | return 0; | 581 | return 0; |
618 | 582 | ||