diff options
author | KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> | 2011-07-08 18:39:42 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-09 00:14:43 -0400 |
commit | 4d0c066d29f030d47d19678f8008933e67dd3b72 (patch) | |
tree | 96f46e845b7a937b2e41b735061f696d2cb03293 /mm/memcontrol.c | |
parent | 0b43c3aab0137595335b08b340a3f3e5af9818a6 (diff) |
memcg: fix reclaimable lru check in memcg
Now, in mem_cgroup_hierarchical_reclaim(), mem_cgroup_local_usage() is
used for checking whether the memcg contains reclaimable pages or not. If
no pages in it, the routine skips it.
But, mem_cgroup_local_usage() contains Unevictable pages and cannot handle
"noswap" condition correctly. This doesn't work on a swapless system.
This patch adds test_mem_cgroup_reclaimable() and replaces
mem_cgroup_local_usage(). test_mem_cgroup_reclaimable() see LRU counter
and returns correct answer to the caller. And this new function has
"noswap" argument and can see only FILE LRU if necessary.
[akpm@linux-foundation.org: coding-style fixes]
[akpm@linux-foundation.org: fix kerneldoc layout]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 107 |
1 files changed, 76 insertions, 31 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index ddffc74cdebe..a7a5cb1bf2c7 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -577,15 +577,6 @@ static long mem_cgroup_read_stat(struct mem_cgroup *mem, | |||
577 | return val; | 577 | return val; |
578 | } | 578 | } |
579 | 579 | ||
580 | static long mem_cgroup_local_usage(struct mem_cgroup *mem) | ||
581 | { | ||
582 | long ret; | ||
583 | |||
584 | ret = mem_cgroup_read_stat(mem, MEM_CGROUP_STAT_RSS); | ||
585 | ret += mem_cgroup_read_stat(mem, MEM_CGROUP_STAT_CACHE); | ||
586 | return ret; | ||
587 | } | ||
588 | |||
589 | static void mem_cgroup_swap_statistics(struct mem_cgroup *mem, | 580 | static void mem_cgroup_swap_statistics(struct mem_cgroup *mem, |
590 | bool charge) | 581 | bool charge) |
591 | { | 582 | { |
@@ -1129,7 +1120,6 @@ unsigned long mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg, | |||
1129 | return MEM_CGROUP_ZSTAT(mz, lru); | 1120 | return MEM_CGROUP_ZSTAT(mz, lru); |
1130 | } | 1121 | } |
1131 | 1122 | ||
1132 | #ifdef CONFIG_NUMA | ||
1133 | static unsigned long mem_cgroup_node_nr_file_lru_pages(struct mem_cgroup *memcg, | 1123 | static unsigned long mem_cgroup_node_nr_file_lru_pages(struct mem_cgroup *memcg, |
1134 | int nid) | 1124 | int nid) |
1135 | { | 1125 | { |
@@ -1141,6 +1131,17 @@ static unsigned long mem_cgroup_node_nr_file_lru_pages(struct mem_cgroup *memcg, | |||
1141 | return ret; | 1131 | return ret; |
1142 | } | 1132 | } |
1143 | 1133 | ||
1134 | static unsigned long mem_cgroup_node_nr_anon_lru_pages(struct mem_cgroup *memcg, | ||
1135 | int nid) | ||
1136 | { | ||
1137 | unsigned long ret; | ||
1138 | |||
1139 | ret = mem_cgroup_get_zonestat_node(memcg, nid, LRU_INACTIVE_ANON) + | ||
1140 | mem_cgroup_get_zonestat_node(memcg, nid, LRU_ACTIVE_ANON); | ||
1141 | return ret; | ||
1142 | } | ||
1143 | |||
1144 | #if MAX_NUMNODES > 1 | ||
1144 | static unsigned long mem_cgroup_nr_file_lru_pages(struct mem_cgroup *memcg) | 1145 | static unsigned long mem_cgroup_nr_file_lru_pages(struct mem_cgroup *memcg) |
1145 | { | 1146 | { |
1146 | u64 total = 0; | 1147 | u64 total = 0; |
@@ -1152,17 +1153,6 @@ static unsigned long mem_cgroup_nr_file_lru_pages(struct mem_cgroup *memcg) | |||
1152 | return total; | 1153 | return total; |
1153 | } | 1154 | } |
1154 | 1155 | ||
1155 | static unsigned long mem_cgroup_node_nr_anon_lru_pages(struct mem_cgroup *memcg, | ||
1156 | int nid) | ||
1157 | { | ||
1158 | unsigned long ret; | ||
1159 | |||
1160 | ret = mem_cgroup_get_zonestat_node(memcg, nid, LRU_INACTIVE_ANON) + | ||
1161 | mem_cgroup_get_zonestat_node(memcg, nid, LRU_ACTIVE_ANON); | ||
1162 | |||
1163 | return ret; | ||
1164 | } | ||
1165 | |||
1166 | static unsigned long mem_cgroup_nr_anon_lru_pages(struct mem_cgroup *memcg) | 1156 | static unsigned long mem_cgroup_nr_anon_lru_pages(struct mem_cgroup *memcg) |
1167 | { | 1157 | { |
1168 | u64 total = 0; | 1158 | u64 total = 0; |
@@ -1559,6 +1549,28 @@ mem_cgroup_select_victim(struct mem_cgroup *root_mem) | |||
1559 | return ret; | 1549 | return ret; |
1560 | } | 1550 | } |
1561 | 1551 | ||
1552 | /** | ||
1553 | * test_mem_cgroup_node_reclaimable | ||
1554 | * @mem: the target memcg | ||
1555 | * @nid: the node ID to be checked. | ||
1556 | * @noswap : specify true here if the user wants flle only information. | ||
1557 | * | ||
1558 | * This function returns whether the specified memcg contains any | ||
1559 | * reclaimable pages on a node. Returns true if there are any reclaimable | ||
1560 | * pages in the node. | ||
1561 | */ | ||
1562 | static bool test_mem_cgroup_node_reclaimable(struct mem_cgroup *mem, | ||
1563 | int nid, bool noswap) | ||
1564 | { | ||
1565 | if (mem_cgroup_node_nr_file_lru_pages(mem, nid)) | ||
1566 | return true; | ||
1567 | if (noswap || !total_swap_pages) | ||
1568 | return false; | ||
1569 | if (mem_cgroup_node_nr_anon_lru_pages(mem, nid)) | ||
1570 | return true; | ||
1571 | return false; | ||
1572 | |||
1573 | } | ||
1562 | #if MAX_NUMNODES > 1 | 1574 | #if MAX_NUMNODES > 1 |
1563 | 1575 | ||
1564 | /* | 1576 | /* |
@@ -1580,15 +1592,8 @@ static void mem_cgroup_may_update_nodemask(struct mem_cgroup *mem) | |||
1580 | 1592 | ||
1581 | for_each_node_mask(nid, node_states[N_HIGH_MEMORY]) { | 1593 | for_each_node_mask(nid, node_states[N_HIGH_MEMORY]) { |
1582 | 1594 | ||
1583 | if (mem_cgroup_get_zonestat_node(mem, nid, LRU_INACTIVE_FILE) || | 1595 | if (!test_mem_cgroup_node_reclaimable(mem, nid, false)) |
1584 | mem_cgroup_get_zonestat_node(mem, nid, LRU_ACTIVE_FILE)) | 1596 | node_clear(nid, mem->scan_nodes); |
1585 | continue; | ||
1586 | |||
1587 | if (total_swap_pages && | ||
1588 | (mem_cgroup_get_zonestat_node(mem, nid, LRU_INACTIVE_ANON) || | ||
1589 | mem_cgroup_get_zonestat_node(mem, nid, LRU_ACTIVE_ANON))) | ||
1590 | continue; | ||
1591 | node_clear(nid, mem->scan_nodes); | ||
1592 | } | 1597 | } |
1593 | } | 1598 | } |
1594 | 1599 | ||
@@ -1627,11 +1632,51 @@ int mem_cgroup_select_victim_node(struct mem_cgroup *mem) | |||
1627 | return node; | 1632 | return node; |
1628 | } | 1633 | } |
1629 | 1634 | ||
1635 | /* | ||
1636 | * Check all nodes whether it contains reclaimable pages or not. | ||
1637 | * For quick scan, we make use of scan_nodes. This will allow us to skip | ||
1638 | * unused nodes. But scan_nodes is lazily updated and may not cotain | ||
1639 | * enough new information. We need to do double check. | ||
1640 | */ | ||
1641 | bool mem_cgroup_reclaimable(struct mem_cgroup *mem, bool noswap) | ||
1642 | { | ||
1643 | int nid; | ||
1644 | |||
1645 | /* | ||
1646 | * quick check...making use of scan_node. | ||
1647 | * We can skip unused nodes. | ||
1648 | */ | ||
1649 | if (!nodes_empty(mem->scan_nodes)) { | ||
1650 | for (nid = first_node(mem->scan_nodes); | ||
1651 | nid < MAX_NUMNODES; | ||
1652 | nid = next_node(nid, mem->scan_nodes)) { | ||
1653 | |||
1654 | if (test_mem_cgroup_node_reclaimable(mem, nid, noswap)) | ||
1655 | return true; | ||
1656 | } | ||
1657 | } | ||
1658 | /* | ||
1659 | * Check rest of nodes. | ||
1660 | */ | ||
1661 | for_each_node_state(nid, N_HIGH_MEMORY) { | ||
1662 | if (node_isset(nid, mem->scan_nodes)) | ||
1663 | continue; | ||
1664 | if (test_mem_cgroup_node_reclaimable(mem, nid, noswap)) | ||
1665 | return true; | ||
1666 | } | ||
1667 | return false; | ||
1668 | } | ||
1669 | |||
1630 | #else | 1670 | #else |
1631 | int mem_cgroup_select_victim_node(struct mem_cgroup *mem) | 1671 | int mem_cgroup_select_victim_node(struct mem_cgroup *mem) |
1632 | { | 1672 | { |
1633 | return 0; | 1673 | return 0; |
1634 | } | 1674 | } |
1675 | |||
1676 | bool mem_cgroup_reclaimable(struct mem_cgroup *mem, bool noswap) | ||
1677 | { | ||
1678 | return test_mem_cgroup_node_reclaimable(mem, 0, noswap); | ||
1679 | } | ||
1635 | #endif | 1680 | #endif |
1636 | 1681 | ||
1637 | /* | 1682 | /* |
@@ -1702,7 +1747,7 @@ static int mem_cgroup_hierarchical_reclaim(struct mem_cgroup *root_mem, | |||
1702 | } | 1747 | } |
1703 | } | 1748 | } |
1704 | } | 1749 | } |
1705 | if (!mem_cgroup_local_usage(victim)) { | 1750 | if (!mem_cgroup_reclaimable(victim, noswap)) { |
1706 | /* this cgroup's local usage == 0 */ | 1751 | /* this cgroup's local usage == 0 */ |
1707 | css_put(&victim->css); | 1752 | css_put(&victim->css); |
1708 | continue; | 1753 | continue; |