diff options
author | Dan Williams <dan.j.williams@intel.com> | 2009-09-08 20:55:21 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2009-09-08 20:55:21 -0400 |
commit | bbb20089a3275a19e475dbc21320c3742e3ca423 (patch) | |
tree | 216fdc1cbef450ca688135c5b8969169482d9a48 /mm/memcontrol.c | |
parent | 3e48e656903e9fd8bc805c6a2c4264d7808d315b (diff) | |
parent | 657a77fa7284d8ae28dfa48f1dc5d919bf5b2843 (diff) |
Merge branch 'dmaengine' into async-tx-next
Conflicts:
crypto/async_tx/async_xor.c
drivers/dma/ioat/dma_v2.h
drivers/dma/ioat/pci.c
drivers/md/raid5.c
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 137 |
1 files changed, 123 insertions, 14 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 78eb8552818b..e2fa20dadf40 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -45,7 +45,7 @@ struct cgroup_subsys mem_cgroup_subsys __read_mostly; | |||
45 | #define MEM_CGROUP_RECLAIM_RETRIES 5 | 45 | #define MEM_CGROUP_RECLAIM_RETRIES 5 |
46 | 46 | ||
47 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP | 47 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP |
48 | /* Turned on only when memory cgroup is enabled && really_do_swap_account = 0 */ | 48 | /* Turned on only when memory cgroup is enabled && really_do_swap_account = 1 */ |
49 | int do_swap_account __read_mostly; | 49 | int do_swap_account __read_mostly; |
50 | static int really_do_swap_account __initdata = 1; /* for remember boot option*/ | 50 | static int really_do_swap_account __initdata = 1; /* for remember boot option*/ |
51 | #else | 51 | #else |
@@ -62,7 +62,8 @@ enum mem_cgroup_stat_index { | |||
62 | * For MEM_CONTAINER_TYPE_ALL, usage = pagecache + rss. | 62 | * For MEM_CONTAINER_TYPE_ALL, usage = pagecache + rss. |
63 | */ | 63 | */ |
64 | MEM_CGROUP_STAT_CACHE, /* # of pages charged as cache */ | 64 | MEM_CGROUP_STAT_CACHE, /* # of pages charged as cache */ |
65 | MEM_CGROUP_STAT_RSS, /* # of pages charged as rss */ | 65 | MEM_CGROUP_STAT_RSS, /* # of pages charged as anon rss */ |
66 | MEM_CGROUP_STAT_MAPPED_FILE, /* # of pages charged as file rss */ | ||
66 | MEM_CGROUP_STAT_PGPGIN_COUNT, /* # of pages paged in */ | 67 | MEM_CGROUP_STAT_PGPGIN_COUNT, /* # of pages paged in */ |
67 | MEM_CGROUP_STAT_PGPGOUT_COUNT, /* # of pages paged out */ | 68 | MEM_CGROUP_STAT_PGPGOUT_COUNT, /* # of pages paged out */ |
68 | 69 | ||
@@ -176,6 +177,9 @@ struct mem_cgroup { | |||
176 | 177 | ||
177 | unsigned int swappiness; | 178 | unsigned int swappiness; |
178 | 179 | ||
180 | /* set when res.limit == memsw.limit */ | ||
181 | bool memsw_is_minimum; | ||
182 | |||
179 | /* | 183 | /* |
180 | * statistics. This must be placed at the end of memcg. | 184 | * statistics. This must be placed at the end of memcg. |
181 | */ | 185 | */ |
@@ -188,6 +192,7 @@ enum charge_type { | |||
188 | MEM_CGROUP_CHARGE_TYPE_SHMEM, /* used by page migration of shmem */ | 192 | MEM_CGROUP_CHARGE_TYPE_SHMEM, /* used by page migration of shmem */ |
189 | MEM_CGROUP_CHARGE_TYPE_FORCE, /* used by force_empty */ | 193 | MEM_CGROUP_CHARGE_TYPE_FORCE, /* used by force_empty */ |
190 | MEM_CGROUP_CHARGE_TYPE_SWAPOUT, /* for accounting swapcache */ | 194 | MEM_CGROUP_CHARGE_TYPE_SWAPOUT, /* for accounting swapcache */ |
195 | MEM_CGROUP_CHARGE_TYPE_DROP, /* a page was unused swap cache */ | ||
191 | NR_CHARGE_TYPE, | 196 | NR_CHARGE_TYPE, |
192 | }; | 197 | }; |
193 | 198 | ||
@@ -570,6 +575,17 @@ int mem_cgroup_inactive_anon_is_low(struct mem_cgroup *memcg) | |||
570 | return 0; | 575 | return 0; |
571 | } | 576 | } |
572 | 577 | ||
578 | int mem_cgroup_inactive_file_is_low(struct mem_cgroup *memcg) | ||
579 | { | ||
580 | unsigned long active; | ||
581 | unsigned long inactive; | ||
582 | |||
583 | inactive = mem_cgroup_get_local_zonestat(memcg, LRU_INACTIVE_FILE); | ||
584 | active = mem_cgroup_get_local_zonestat(memcg, LRU_ACTIVE_FILE); | ||
585 | |||
586 | return (active > inactive); | ||
587 | } | ||
588 | |||
573 | unsigned long mem_cgroup_zone_nr_pages(struct mem_cgroup *memcg, | 589 | unsigned long mem_cgroup_zone_nr_pages(struct mem_cgroup *memcg, |
574 | struct zone *zone, | 590 | struct zone *zone, |
575 | enum lru_list lru) | 591 | enum lru_list lru) |
@@ -633,6 +649,7 @@ unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, | |||
633 | int zid = zone_idx(z); | 649 | int zid = zone_idx(z); |
634 | struct mem_cgroup_per_zone *mz; | 650 | struct mem_cgroup_per_zone *mz; |
635 | int lru = LRU_FILE * !!file + !!active; | 651 | int lru = LRU_FILE * !!file + !!active; |
652 | int ret; | ||
636 | 653 | ||
637 | BUG_ON(!mem_cont); | 654 | BUG_ON(!mem_cont); |
638 | mz = mem_cgroup_zoneinfo(mem_cont, nid, zid); | 655 | mz = mem_cgroup_zoneinfo(mem_cont, nid, zid); |
@@ -650,9 +667,19 @@ unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, | |||
650 | continue; | 667 | continue; |
651 | 668 | ||
652 | scan++; | 669 | scan++; |
653 | if (__isolate_lru_page(page, mode, file) == 0) { | 670 | ret = __isolate_lru_page(page, mode, file); |
671 | switch (ret) { | ||
672 | case 0: | ||
654 | list_move(&page->lru, dst); | 673 | list_move(&page->lru, dst); |
674 | mem_cgroup_del_lru(page); | ||
655 | nr_taken++; | 675 | nr_taken++; |
676 | break; | ||
677 | case -EBUSY: | ||
678 | /* we don't affect global LRU but rotate in our LRU */ | ||
679 | mem_cgroup_rotate_lru_list(page, page_lru(page)); | ||
680 | break; | ||
681 | default: | ||
682 | break; | ||
656 | } | 683 | } |
657 | } | 684 | } |
658 | 685 | ||
@@ -834,6 +861,10 @@ static int mem_cgroup_hierarchical_reclaim(struct mem_cgroup *root_mem, | |||
834 | int ret, total = 0; | 861 | int ret, total = 0; |
835 | int loop = 0; | 862 | int loop = 0; |
836 | 863 | ||
864 | /* If memsw_is_minimum==1, swap-out is of-no-use. */ | ||
865 | if (root_mem->memsw_is_minimum) | ||
866 | noswap = true; | ||
867 | |||
837 | while (loop < 2) { | 868 | while (loop < 2) { |
838 | victim = mem_cgroup_select_victim(root_mem); | 869 | victim = mem_cgroup_select_victim(root_mem); |
839 | if (victim == root_mem) | 870 | if (victim == root_mem) |
@@ -889,6 +920,44 @@ static void record_last_oom(struct mem_cgroup *mem) | |||
889 | mem_cgroup_walk_tree(mem, NULL, record_last_oom_cb); | 920 | mem_cgroup_walk_tree(mem, NULL, record_last_oom_cb); |
890 | } | 921 | } |
891 | 922 | ||
923 | /* | ||
924 | * Currently used to update mapped file statistics, but the routine can be | ||
925 | * generalized to update other statistics as well. | ||
926 | */ | ||
927 | void mem_cgroup_update_mapped_file_stat(struct page *page, int val) | ||
928 | { | ||
929 | struct mem_cgroup *mem; | ||
930 | struct mem_cgroup_stat *stat; | ||
931 | struct mem_cgroup_stat_cpu *cpustat; | ||
932 | int cpu; | ||
933 | struct page_cgroup *pc; | ||
934 | |||
935 | if (!page_is_file_cache(page)) | ||
936 | return; | ||
937 | |||
938 | pc = lookup_page_cgroup(page); | ||
939 | if (unlikely(!pc)) | ||
940 | return; | ||
941 | |||
942 | lock_page_cgroup(pc); | ||
943 | mem = pc->mem_cgroup; | ||
944 | if (!mem) | ||
945 | goto done; | ||
946 | |||
947 | if (!PageCgroupUsed(pc)) | ||
948 | goto done; | ||
949 | |||
950 | /* | ||
951 | * Preemption is already disabled, we don't need get_cpu() | ||
952 | */ | ||
953 | cpu = smp_processor_id(); | ||
954 | stat = &mem->stat; | ||
955 | cpustat = &stat->cpustat[cpu]; | ||
956 | |||
957 | __mem_cgroup_stat_add_safe(cpustat, MEM_CGROUP_STAT_MAPPED_FILE, val); | ||
958 | done: | ||
959 | unlock_page_cgroup(pc); | ||
960 | } | ||
892 | 961 | ||
893 | /* | 962 | /* |
894 | * Unlike exported interface, "oom" parameter is added. if oom==true, | 963 | * Unlike exported interface, "oom" parameter is added. if oom==true, |
@@ -1087,6 +1156,10 @@ static int mem_cgroup_move_account(struct page_cgroup *pc, | |||
1087 | struct mem_cgroup_per_zone *from_mz, *to_mz; | 1156 | struct mem_cgroup_per_zone *from_mz, *to_mz; |
1088 | int nid, zid; | 1157 | int nid, zid; |
1089 | int ret = -EBUSY; | 1158 | int ret = -EBUSY; |
1159 | struct page *page; | ||
1160 | int cpu; | ||
1161 | struct mem_cgroup_stat *stat; | ||
1162 | struct mem_cgroup_stat_cpu *cpustat; | ||
1090 | 1163 | ||
1091 | VM_BUG_ON(from == to); | 1164 | VM_BUG_ON(from == to); |
1092 | VM_BUG_ON(PageLRU(pc->page)); | 1165 | VM_BUG_ON(PageLRU(pc->page)); |
@@ -1107,6 +1180,23 @@ static int mem_cgroup_move_account(struct page_cgroup *pc, | |||
1107 | 1180 | ||
1108 | res_counter_uncharge(&from->res, PAGE_SIZE); | 1181 | res_counter_uncharge(&from->res, PAGE_SIZE); |
1109 | mem_cgroup_charge_statistics(from, pc, false); | 1182 | mem_cgroup_charge_statistics(from, pc, false); |
1183 | |||
1184 | page = pc->page; | ||
1185 | if (page_is_file_cache(page) && page_mapped(page)) { | ||
1186 | cpu = smp_processor_id(); | ||
1187 | /* Update mapped_file data for mem_cgroup "from" */ | ||
1188 | stat = &from->stat; | ||
1189 | cpustat = &stat->cpustat[cpu]; | ||
1190 | __mem_cgroup_stat_add_safe(cpustat, MEM_CGROUP_STAT_MAPPED_FILE, | ||
1191 | -1); | ||
1192 | |||
1193 | /* Update mapped_file data for mem_cgroup "to" */ | ||
1194 | stat = &to->stat; | ||
1195 | cpustat = &stat->cpustat[cpu]; | ||
1196 | __mem_cgroup_stat_add_safe(cpustat, MEM_CGROUP_STAT_MAPPED_FILE, | ||
1197 | 1); | ||
1198 | } | ||
1199 | |||
1110 | if (do_swap_account) | 1200 | if (do_swap_account) |
1111 | res_counter_uncharge(&from->memsw, PAGE_SIZE); | 1201 | res_counter_uncharge(&from->memsw, PAGE_SIZE); |
1112 | css_put(&from->css); | 1202 | css_put(&from->css); |
@@ -1422,6 +1512,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) | |||
1422 | 1512 | ||
1423 | switch (ctype) { | 1513 | switch (ctype) { |
1424 | case MEM_CGROUP_CHARGE_TYPE_MAPPED: | 1514 | case MEM_CGROUP_CHARGE_TYPE_MAPPED: |
1515 | case MEM_CGROUP_CHARGE_TYPE_DROP: | ||
1425 | if (page_mapped(page)) | 1516 | if (page_mapped(page)) |
1426 | goto unlock_out; | 1517 | goto unlock_out; |
1427 | break; | 1518 | break; |
@@ -1485,18 +1576,23 @@ void mem_cgroup_uncharge_cache_page(struct page *page) | |||
1485 | * called after __delete_from_swap_cache() and drop "page" account. | 1576 | * called after __delete_from_swap_cache() and drop "page" account. |
1486 | * memcg information is recorded to swap_cgroup of "ent" | 1577 | * memcg information is recorded to swap_cgroup of "ent" |
1487 | */ | 1578 | */ |
1488 | void mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent) | 1579 | void |
1580 | mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout) | ||
1489 | { | 1581 | { |
1490 | struct mem_cgroup *memcg; | 1582 | struct mem_cgroup *memcg; |
1583 | int ctype = MEM_CGROUP_CHARGE_TYPE_SWAPOUT; | ||
1584 | |||
1585 | if (!swapout) /* this was a swap cache but the swap is unused ! */ | ||
1586 | ctype = MEM_CGROUP_CHARGE_TYPE_DROP; | ||
1587 | |||
1588 | memcg = __mem_cgroup_uncharge_common(page, ctype); | ||
1491 | 1589 | ||
1492 | memcg = __mem_cgroup_uncharge_common(page, | ||
1493 | MEM_CGROUP_CHARGE_TYPE_SWAPOUT); | ||
1494 | /* record memcg information */ | 1590 | /* record memcg information */ |
1495 | if (do_swap_account && memcg) { | 1591 | if (do_swap_account && swapout && memcg) { |
1496 | swap_cgroup_record(ent, css_id(&memcg->css)); | 1592 | swap_cgroup_record(ent, css_id(&memcg->css)); |
1497 | mem_cgroup_get(memcg); | 1593 | mem_cgroup_get(memcg); |
1498 | } | 1594 | } |
1499 | if (memcg) | 1595 | if (swapout && memcg) |
1500 | css_put(&memcg->css); | 1596 | css_put(&memcg->css); |
1501 | } | 1597 | } |
1502 | #endif | 1598 | #endif |
@@ -1674,6 +1770,12 @@ static int mem_cgroup_resize_limit(struct mem_cgroup *memcg, | |||
1674 | break; | 1770 | break; |
1675 | } | 1771 | } |
1676 | ret = res_counter_set_limit(&memcg->res, val); | 1772 | ret = res_counter_set_limit(&memcg->res, val); |
1773 | if (!ret) { | ||
1774 | if (memswlimit == val) | ||
1775 | memcg->memsw_is_minimum = true; | ||
1776 | else | ||
1777 | memcg->memsw_is_minimum = false; | ||
1778 | } | ||
1677 | mutex_unlock(&set_limit_mutex); | 1779 | mutex_unlock(&set_limit_mutex); |
1678 | 1780 | ||
1679 | if (!ret) | 1781 | if (!ret) |
@@ -1692,16 +1794,14 @@ static int mem_cgroup_resize_limit(struct mem_cgroup *memcg, | |||
1692 | return ret; | 1794 | return ret; |
1693 | } | 1795 | } |
1694 | 1796 | ||
1695 | int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg, | 1797 | static int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg, |
1696 | unsigned long long val) | 1798 | unsigned long long val) |
1697 | { | 1799 | { |
1698 | int retry_count; | 1800 | int retry_count; |
1699 | u64 memlimit, oldusage, curusage; | 1801 | u64 memlimit, oldusage, curusage; |
1700 | int children = mem_cgroup_count_children(memcg); | 1802 | int children = mem_cgroup_count_children(memcg); |
1701 | int ret = -EBUSY; | 1803 | int ret = -EBUSY; |
1702 | 1804 | ||
1703 | if (!do_swap_account) | ||
1704 | return -EINVAL; | ||
1705 | /* see mem_cgroup_resize_res_limit */ | 1805 | /* see mem_cgroup_resize_res_limit */ |
1706 | retry_count = children * MEM_CGROUP_RECLAIM_RETRIES; | 1806 | retry_count = children * MEM_CGROUP_RECLAIM_RETRIES; |
1707 | oldusage = res_counter_read_u64(&memcg->memsw, RES_USAGE); | 1807 | oldusage = res_counter_read_u64(&memcg->memsw, RES_USAGE); |
@@ -1723,6 +1823,12 @@ int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg, | |||
1723 | break; | 1823 | break; |
1724 | } | 1824 | } |
1725 | ret = res_counter_set_limit(&memcg->memsw, val); | 1825 | ret = res_counter_set_limit(&memcg->memsw, val); |
1826 | if (!ret) { | ||
1827 | if (memlimit == val) | ||
1828 | memcg->memsw_is_minimum = true; | ||
1829 | else | ||
1830 | memcg->memsw_is_minimum = false; | ||
1831 | } | ||
1726 | mutex_unlock(&set_limit_mutex); | 1832 | mutex_unlock(&set_limit_mutex); |
1727 | 1833 | ||
1728 | if (!ret) | 1834 | if (!ret) |
@@ -1936,8 +2042,7 @@ static u64 mem_cgroup_read(struct cgroup *cont, struct cftype *cft) | |||
1936 | val = res_counter_read_u64(&mem->res, name); | 2042 | val = res_counter_read_u64(&mem->res, name); |
1937 | break; | 2043 | break; |
1938 | case _MEMSWAP: | 2044 | case _MEMSWAP: |
1939 | if (do_swap_account) | 2045 | val = res_counter_read_u64(&mem->memsw, name); |
1940 | val = res_counter_read_u64(&mem->memsw, name); | ||
1941 | break; | 2046 | break; |
1942 | default: | 2047 | default: |
1943 | BUG(); | 2048 | BUG(); |
@@ -2035,6 +2140,7 @@ static int mem_cgroup_reset(struct cgroup *cont, unsigned int event) | |||
2035 | enum { | 2140 | enum { |
2036 | MCS_CACHE, | 2141 | MCS_CACHE, |
2037 | MCS_RSS, | 2142 | MCS_RSS, |
2143 | MCS_MAPPED_FILE, | ||
2038 | MCS_PGPGIN, | 2144 | MCS_PGPGIN, |
2039 | MCS_PGPGOUT, | 2145 | MCS_PGPGOUT, |
2040 | MCS_INACTIVE_ANON, | 2146 | MCS_INACTIVE_ANON, |
@@ -2055,6 +2161,7 @@ struct { | |||
2055 | } memcg_stat_strings[NR_MCS_STAT] = { | 2161 | } memcg_stat_strings[NR_MCS_STAT] = { |
2056 | {"cache", "total_cache"}, | 2162 | {"cache", "total_cache"}, |
2057 | {"rss", "total_rss"}, | 2163 | {"rss", "total_rss"}, |
2164 | {"mapped_file", "total_mapped_file"}, | ||
2058 | {"pgpgin", "total_pgpgin"}, | 2165 | {"pgpgin", "total_pgpgin"}, |
2059 | {"pgpgout", "total_pgpgout"}, | 2166 | {"pgpgout", "total_pgpgout"}, |
2060 | {"inactive_anon", "total_inactive_anon"}, | 2167 | {"inactive_anon", "total_inactive_anon"}, |
@@ -2075,6 +2182,8 @@ static int mem_cgroup_get_local_stat(struct mem_cgroup *mem, void *data) | |||
2075 | s->stat[MCS_CACHE] += val * PAGE_SIZE; | 2182 | s->stat[MCS_CACHE] += val * PAGE_SIZE; |
2076 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_RSS); | 2183 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_RSS); |
2077 | s->stat[MCS_RSS] += val * PAGE_SIZE; | 2184 | s->stat[MCS_RSS] += val * PAGE_SIZE; |
2185 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_MAPPED_FILE); | ||
2186 | s->stat[MCS_MAPPED_FILE] += val * PAGE_SIZE; | ||
2078 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_PGPGIN_COUNT); | 2187 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_PGPGIN_COUNT); |
2079 | s->stat[MCS_PGPGIN] += val; | 2188 | s->stat[MCS_PGPGIN] += val; |
2080 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_PGPGOUT_COUNT); | 2189 | val = mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_PGPGOUT_COUNT); |