summaryrefslogtreecommitdiffstats
path: root/mm/workingset.c
diff options
context:
space:
mode:
authorJohannes Weiner <hannes@cmpxchg.org>2016-12-12 19:43:52 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-12-12 21:55:08 -0500
commit14b468791fa955d442f962fdf5207dfd39a131c8 (patch)
treea1a5a3d445b7de16ae06184fa9c0804e3255d9d2 /mm/workingset.c
parent4d693d08607ab319095ec8942909df4b4aebdf66 (diff)
mm: workingset: move shadow entry tracking to radix tree exceptional tracking
Currently, we track the shadow entries in the page cache in the upper bits of the radix_tree_node->count, behind the back of the radix tree implementation. Because the radix tree code has no awareness of them, we rely on random subtleties throughout the implementation (such as the node->count != 1 check in the shrinking code, which is meant to exclude multi-entry nodes but also happens to skip nodes with only one shadow entry, as that's accounted in the upper bits). This is error prone and has, in fact, caused the bug fixed in d3798ae8c6f3 ("mm: filemap: don't plant shadow entries without radix tree node"). To remove these subtleties, this patch moves shadow entry tracking from the upper bits of node->count to the existing counter for exceptional entries. node->count goes back to being a simple counter of valid entries in the tree node and can be shrunk to a single byte. This vastly simplifies the page cache code. All accounting happens natively inside the radix tree implementation, and maintaining the LRU linkage of shadow nodes is consolidated into a single function in the workingset code that is called for leaf nodes affected by a change in the page cache tree. This also removes the last user of the __radix_delete_node() return value. Eliminate it. Link: http://lkml.kernel.org/r/20161117193211.GE23430@cmpxchg.org Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> Reviewed-by: Jan Kara <jack@suse.cz> Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Cc: Hugh Dickins <hughd@google.com> Cc: Matthew Wilcox <mawilcox@linuxonhyperv.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/workingset.c')
-rw-r--r--mm/workingset.c56
1 files changed, 43 insertions, 13 deletions
diff --git a/mm/workingset.c b/mm/workingset.c
index 98f830897b1b..ef556bf1323d 100644
--- a/mm/workingset.c
+++ b/mm/workingset.c
@@ -10,6 +10,7 @@
10#include <linux/atomic.h> 10#include <linux/atomic.h>
11#include <linux/module.h> 11#include <linux/module.h>
12#include <linux/swap.h> 12#include <linux/swap.h>
13#include <linux/dax.h>
13#include <linux/fs.h> 14#include <linux/fs.h>
14#include <linux/mm.h> 15#include <linux/mm.h>
15 16
@@ -334,18 +335,45 @@ out:
334 * point where they would still be useful. 335 * point where they would still be useful.
335 */ 336 */
336 337
337struct list_lru workingset_shadow_nodes; 338static struct list_lru shadow_nodes;
339
340void workingset_update_node(struct radix_tree_node *node, void *private)
341{
342 struct address_space *mapping = private;
343
344 /* Only regular page cache has shadow entries */
345 if (dax_mapping(mapping) || shmem_mapping(mapping))
346 return;
347
348 /*
349 * Track non-empty nodes that contain only shadow entries;
350 * unlink those that contain pages or are being freed.
351 *
352 * Avoid acquiring the list_lru lock when the nodes are
353 * already where they should be. The list_empty() test is safe
354 * as node->private_list is protected by &mapping->tree_lock.
355 */
356 if (node->count && node->count == node->exceptional) {
357 if (list_empty(&node->private_list)) {
358 node->private_data = mapping;
359 list_lru_add(&shadow_nodes, &node->private_list);
360 }
361 } else {
362 if (!list_empty(&node->private_list))
363 list_lru_del(&shadow_nodes, &node->private_list);
364 }
365}
338 366
339static unsigned long count_shadow_nodes(struct shrinker *shrinker, 367static unsigned long count_shadow_nodes(struct shrinker *shrinker,
340 struct shrink_control *sc) 368 struct shrink_control *sc)
341{ 369{
342 unsigned long shadow_nodes;
343 unsigned long max_nodes; 370 unsigned long max_nodes;
371 unsigned long nodes;
344 unsigned long pages; 372 unsigned long pages;
345 373
346 /* list_lru lock nests inside IRQ-safe mapping->tree_lock */ 374 /* list_lru lock nests inside IRQ-safe mapping->tree_lock */
347 local_irq_disable(); 375 local_irq_disable();
348 shadow_nodes = list_lru_shrink_count(&workingset_shadow_nodes, sc); 376 nodes = list_lru_shrink_count(&shadow_nodes, sc);
349 local_irq_enable(); 377 local_irq_enable();
350 378
351 if (sc->memcg) { 379 if (sc->memcg) {
@@ -372,10 +400,10 @@ static unsigned long count_shadow_nodes(struct shrinker *shrinker,
372 */ 400 */
373 max_nodes = pages >> (1 + RADIX_TREE_MAP_SHIFT - 3); 401 max_nodes = pages >> (1 + RADIX_TREE_MAP_SHIFT - 3);
374 402
375 if (shadow_nodes <= max_nodes) 403 if (nodes <= max_nodes)
376 return 0; 404 return 0;
377 405
378 return shadow_nodes - max_nodes; 406 return nodes - max_nodes;
379} 407}
380 408
381static enum lru_status shadow_lru_isolate(struct list_head *item, 409static enum lru_status shadow_lru_isolate(struct list_head *item,
@@ -418,22 +446,25 @@ static enum lru_status shadow_lru_isolate(struct list_head *item,
418 * no pages, so we expect to be able to remove them all and 446 * no pages, so we expect to be able to remove them all and
419 * delete and free the empty node afterwards. 447 * delete and free the empty node afterwards.
420 */ 448 */
421 if (WARN_ON_ONCE(!workingset_node_shadows(node))) 449 if (WARN_ON_ONCE(!node->exceptional))
422 goto out_invalid; 450 goto out_invalid;
423 if (WARN_ON_ONCE(workingset_node_pages(node))) 451 if (WARN_ON_ONCE(node->count != node->exceptional))
424 goto out_invalid; 452 goto out_invalid;
425 for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) { 453 for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) {
426 if (node->slots[i]) { 454 if (node->slots[i]) {
427 if (WARN_ON_ONCE(!radix_tree_exceptional_entry(node->slots[i]))) 455 if (WARN_ON_ONCE(!radix_tree_exceptional_entry(node->slots[i])))
428 goto out_invalid; 456 goto out_invalid;
457 if (WARN_ON_ONCE(!node->exceptional))
458 goto out_invalid;
429 if (WARN_ON_ONCE(!mapping->nrexceptional)) 459 if (WARN_ON_ONCE(!mapping->nrexceptional))
430 goto out_invalid; 460 goto out_invalid;
431 node->slots[i] = NULL; 461 node->slots[i] = NULL;
432 workingset_node_shadows_dec(node); 462 node->exceptional--;
463 node->count--;
433 mapping->nrexceptional--; 464 mapping->nrexceptional--;
434 } 465 }
435 } 466 }
436 if (WARN_ON_ONCE(workingset_node_shadows(node))) 467 if (WARN_ON_ONCE(node->exceptional))
437 goto out_invalid; 468 goto out_invalid;
438 inc_node_state(page_pgdat(virt_to_page(node)), WORKINGSET_NODERECLAIM); 469 inc_node_state(page_pgdat(virt_to_page(node)), WORKINGSET_NODERECLAIM);
439 __radix_tree_delete_node(&mapping->page_tree, node); 470 __radix_tree_delete_node(&mapping->page_tree, node);
@@ -456,8 +487,7 @@ static unsigned long scan_shadow_nodes(struct shrinker *shrinker,
456 487
457 /* list_lru lock nests inside IRQ-safe mapping->tree_lock */ 488 /* list_lru lock nests inside IRQ-safe mapping->tree_lock */
458 local_irq_disable(); 489 local_irq_disable();
459 ret = list_lru_shrink_walk(&workingset_shadow_nodes, sc, 490 ret = list_lru_shrink_walk(&shadow_nodes, sc, shadow_lru_isolate, NULL);
460 shadow_lru_isolate, NULL);
461 local_irq_enable(); 491 local_irq_enable();
462 return ret; 492 return ret;
463} 493}
@@ -496,7 +526,7 @@ static int __init workingset_init(void)
496 pr_info("workingset: timestamp_bits=%d max_order=%d bucket_order=%u\n", 526 pr_info("workingset: timestamp_bits=%d max_order=%d bucket_order=%u\n",
497 timestamp_bits, max_order, bucket_order); 527 timestamp_bits, max_order, bucket_order);
498 528
499 ret = list_lru_init_key(&workingset_shadow_nodes, &shadow_nodes_key); 529 ret = list_lru_init_key(&shadow_nodes, &shadow_nodes_key);
500 if (ret) 530 if (ret)
501 goto err; 531 goto err;
502 ret = register_shrinker(&workingset_shadow_shrinker); 532 ret = register_shrinker(&workingset_shadow_shrinker);
@@ -504,7 +534,7 @@ static int __init workingset_init(void)
504 goto err_list_lru; 534 goto err_list_lru;
505 return 0; 535 return 0;
506err_list_lru: 536err_list_lru:
507 list_lru_destroy(&workingset_shadow_nodes); 537 list_lru_destroy(&shadow_nodes);
508err: 538err:
509 return ret; 539 return ret;
510} 540}