diff options
-rw-r--r-- | Documentation/cgroups/memory.txt | 1 | ||||
-rw-r--r-- | fs/buffer.c | 34 | ||||
-rw-r--r-- | fs/xfs/xfs_aops.c | 12 | ||||
-rw-r--r-- | include/linux/memcontrol.h | 1 | ||||
-rw-r--r-- | include/linux/mm.h | 6 | ||||
-rw-r--r-- | include/linux/pagemap.h | 3 | ||||
-rw-r--r-- | mm/filemap.c | 31 | ||||
-rw-r--r-- | mm/memcontrol.c | 24 | ||||
-rw-r--r-- | mm/page-writeback.c | 50 | ||||
-rw-r--r-- | mm/rmap.c | 2 | ||||
-rw-r--r-- | mm/truncate.c | 14 | ||||
-rw-r--r-- | mm/vmscan.c | 17 |
12 files changed, 156 insertions, 39 deletions
diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt index f456b4315e86..ff71e16cc752 100644 --- a/Documentation/cgroups/memory.txt +++ b/Documentation/cgroups/memory.txt | |||
@@ -493,6 +493,7 @@ pgpgin - # of charging events to the memory cgroup. The charging | |||
493 | pgpgout - # of uncharging events to the memory cgroup. The uncharging | 493 | pgpgout - # of uncharging events to the memory cgroup. The uncharging |
494 | event happens each time a page is unaccounted from the cgroup. | 494 | event happens each time a page is unaccounted from the cgroup. |
495 | swap - # of bytes of swap usage | 495 | swap - # of bytes of swap usage |
496 | dirty - # of bytes that are waiting to get written back to the disk. | ||
496 | writeback - # of bytes of file/anon cache that are queued for syncing to | 497 | writeback - # of bytes of file/anon cache that are queued for syncing to |
497 | disk. | 498 | disk. |
498 | inactive_anon - # of bytes of anonymous and swap cache memory on inactive | 499 | inactive_anon - # of bytes of anonymous and swap cache memory on inactive |
diff --git a/fs/buffer.c b/fs/buffer.c index f21327d1f673..23b640d5d6e9 100644 --- a/fs/buffer.c +++ b/fs/buffer.c | |||
@@ -623,21 +623,22 @@ EXPORT_SYMBOL(mark_buffer_dirty_inode); | |||
623 | * | 623 | * |
624 | * If warn is true, then emit a warning if the page is not uptodate and has | 624 | * If warn is true, then emit a warning if the page is not uptodate and has |
625 | * not been truncated. | 625 | * not been truncated. |
626 | * | ||
627 | * The caller must hold mem_cgroup_begin_page_stat() lock. | ||
626 | */ | 628 | */ |
627 | static void __set_page_dirty(struct page *page, | 629 | static void __set_page_dirty(struct page *page, struct address_space *mapping, |
628 | struct address_space *mapping, int warn) | 630 | struct mem_cgroup *memcg, int warn) |
629 | { | 631 | { |
630 | unsigned long flags; | 632 | unsigned long flags; |
631 | 633 | ||
632 | spin_lock_irqsave(&mapping->tree_lock, flags); | 634 | spin_lock_irqsave(&mapping->tree_lock, flags); |
633 | if (page->mapping) { /* Race with truncate? */ | 635 | if (page->mapping) { /* Race with truncate? */ |
634 | WARN_ON_ONCE(warn && !PageUptodate(page)); | 636 | WARN_ON_ONCE(warn && !PageUptodate(page)); |
635 | account_page_dirtied(page, mapping); | 637 | account_page_dirtied(page, mapping, memcg); |
636 | radix_tree_tag_set(&mapping->page_tree, | 638 | radix_tree_tag_set(&mapping->page_tree, |
637 | page_index(page), PAGECACHE_TAG_DIRTY); | 639 | page_index(page), PAGECACHE_TAG_DIRTY); |
638 | } | 640 | } |
639 | spin_unlock_irqrestore(&mapping->tree_lock, flags); | 641 | spin_unlock_irqrestore(&mapping->tree_lock, flags); |
640 | __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); | ||
641 | } | 642 | } |
642 | 643 | ||
643 | /* | 644 | /* |
@@ -668,6 +669,7 @@ static void __set_page_dirty(struct page *page, | |||
668 | int __set_page_dirty_buffers(struct page *page) | 669 | int __set_page_dirty_buffers(struct page *page) |
669 | { | 670 | { |
670 | int newly_dirty; | 671 | int newly_dirty; |
672 | struct mem_cgroup *memcg; | ||
671 | struct address_space *mapping = page_mapping(page); | 673 | struct address_space *mapping = page_mapping(page); |
672 | 674 | ||
673 | if (unlikely(!mapping)) | 675 | if (unlikely(!mapping)) |
@@ -683,11 +685,22 @@ int __set_page_dirty_buffers(struct page *page) | |||
683 | bh = bh->b_this_page; | 685 | bh = bh->b_this_page; |
684 | } while (bh != head); | 686 | } while (bh != head); |
685 | } | 687 | } |
688 | /* | ||
689 | * Use mem_group_begin_page_stat() to keep PageDirty synchronized with | ||
690 | * per-memcg dirty page counters. | ||
691 | */ | ||
692 | memcg = mem_cgroup_begin_page_stat(page); | ||
686 | newly_dirty = !TestSetPageDirty(page); | 693 | newly_dirty = !TestSetPageDirty(page); |
687 | spin_unlock(&mapping->private_lock); | 694 | spin_unlock(&mapping->private_lock); |
688 | 695 | ||
689 | if (newly_dirty) | 696 | if (newly_dirty) |
690 | __set_page_dirty(page, mapping, 1); | 697 | __set_page_dirty(page, mapping, memcg, 1); |
698 | |||
699 | mem_cgroup_end_page_stat(memcg); | ||
700 | |||
701 | if (newly_dirty) | ||
702 | __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); | ||
703 | |||
691 | return newly_dirty; | 704 | return newly_dirty; |
692 | } | 705 | } |
693 | EXPORT_SYMBOL(__set_page_dirty_buffers); | 706 | EXPORT_SYMBOL(__set_page_dirty_buffers); |
@@ -1158,11 +1171,18 @@ void mark_buffer_dirty(struct buffer_head *bh) | |||
1158 | 1171 | ||
1159 | if (!test_set_buffer_dirty(bh)) { | 1172 | if (!test_set_buffer_dirty(bh)) { |
1160 | struct page *page = bh->b_page; | 1173 | struct page *page = bh->b_page; |
1174 | struct address_space *mapping = NULL; | ||
1175 | struct mem_cgroup *memcg; | ||
1176 | |||
1177 | memcg = mem_cgroup_begin_page_stat(page); | ||
1161 | if (!TestSetPageDirty(page)) { | 1178 | if (!TestSetPageDirty(page)) { |
1162 | struct address_space *mapping = page_mapping(page); | 1179 | mapping = page_mapping(page); |
1163 | if (mapping) | 1180 | if (mapping) |
1164 | __set_page_dirty(page, mapping, 0); | 1181 | __set_page_dirty(page, mapping, memcg, 0); |
1165 | } | 1182 | } |
1183 | mem_cgroup_end_page_stat(memcg); | ||
1184 | if (mapping) | ||
1185 | __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); | ||
1166 | } | 1186 | } |
1167 | } | 1187 | } |
1168 | EXPORT_SYMBOL(mark_buffer_dirty); | 1188 | EXPORT_SYMBOL(mark_buffer_dirty); |
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 095f94c2d8b5..e5099f268032 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c | |||
@@ -1873,6 +1873,7 @@ xfs_vm_set_page_dirty( | |||
1873 | loff_t end_offset; | 1873 | loff_t end_offset; |
1874 | loff_t offset; | 1874 | loff_t offset; |
1875 | int newly_dirty; | 1875 | int newly_dirty; |
1876 | struct mem_cgroup *memcg; | ||
1876 | 1877 | ||
1877 | if (unlikely(!mapping)) | 1878 | if (unlikely(!mapping)) |
1878 | return !TestSetPageDirty(page); | 1879 | return !TestSetPageDirty(page); |
@@ -1892,6 +1893,11 @@ xfs_vm_set_page_dirty( | |||
1892 | offset += 1 << inode->i_blkbits; | 1893 | offset += 1 << inode->i_blkbits; |
1893 | } while (bh != head); | 1894 | } while (bh != head); |
1894 | } | 1895 | } |
1896 | /* | ||
1897 | * Use mem_group_begin_page_stat() to keep PageDirty synchronized with | ||
1898 | * per-memcg dirty page counters. | ||
1899 | */ | ||
1900 | memcg = mem_cgroup_begin_page_stat(page); | ||
1895 | newly_dirty = !TestSetPageDirty(page); | 1901 | newly_dirty = !TestSetPageDirty(page); |
1896 | spin_unlock(&mapping->private_lock); | 1902 | spin_unlock(&mapping->private_lock); |
1897 | 1903 | ||
@@ -1902,13 +1908,15 @@ xfs_vm_set_page_dirty( | |||
1902 | spin_lock_irqsave(&mapping->tree_lock, flags); | 1908 | spin_lock_irqsave(&mapping->tree_lock, flags); |
1903 | if (page->mapping) { /* Race with truncate? */ | 1909 | if (page->mapping) { /* Race with truncate? */ |
1904 | WARN_ON_ONCE(!PageUptodate(page)); | 1910 | WARN_ON_ONCE(!PageUptodate(page)); |
1905 | account_page_dirtied(page, mapping); | 1911 | account_page_dirtied(page, mapping, memcg); |
1906 | radix_tree_tag_set(&mapping->page_tree, | 1912 | radix_tree_tag_set(&mapping->page_tree, |
1907 | page_index(page), PAGECACHE_TAG_DIRTY); | 1913 | page_index(page), PAGECACHE_TAG_DIRTY); |
1908 | } | 1914 | } |
1909 | spin_unlock_irqrestore(&mapping->tree_lock, flags); | 1915 | spin_unlock_irqrestore(&mapping->tree_lock, flags); |
1910 | __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); | ||
1911 | } | 1916 | } |
1917 | mem_cgroup_end_page_stat(memcg); | ||
1918 | if (newly_dirty) | ||
1919 | __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); | ||
1912 | return newly_dirty; | 1920 | return newly_dirty; |
1913 | } | 1921 | } |
1914 | 1922 | ||
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 72dff5fb0d0c..5fe6411b5e54 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h | |||
@@ -41,6 +41,7 @@ enum mem_cgroup_stat_index { | |||
41 | MEM_CGROUP_STAT_RSS, /* # of pages charged as anon rss */ | 41 | MEM_CGROUP_STAT_RSS, /* # of pages charged as anon rss */ |
42 | MEM_CGROUP_STAT_RSS_HUGE, /* # of pages charged as anon huge */ | 42 | MEM_CGROUP_STAT_RSS_HUGE, /* # of pages charged as anon huge */ |
43 | MEM_CGROUP_STAT_FILE_MAPPED, /* # of pages charged as file rss */ | 43 | MEM_CGROUP_STAT_FILE_MAPPED, /* # of pages charged as file rss */ |
44 | MEM_CGROUP_STAT_DIRTY, /* # of dirty pages in page cache */ | ||
44 | MEM_CGROUP_STAT_WRITEBACK, /* # of pages under writeback */ | 45 | MEM_CGROUP_STAT_WRITEBACK, /* # of pages under writeback */ |
45 | MEM_CGROUP_STAT_SWAP, /* # of pages, swapped out */ | 46 | MEM_CGROUP_STAT_SWAP, /* # of pages, swapped out */ |
46 | MEM_CGROUP_STAT_NSTATS, | 47 | MEM_CGROUP_STAT_NSTATS, |
diff --git a/include/linux/mm.h b/include/linux/mm.h index a83cf3a6f78e..f48d979ced4b 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -1211,8 +1211,10 @@ int __set_page_dirty_nobuffers(struct page *page); | |||
1211 | int __set_page_dirty_no_writeback(struct page *page); | 1211 | int __set_page_dirty_no_writeback(struct page *page); |
1212 | int redirty_page_for_writepage(struct writeback_control *wbc, | 1212 | int redirty_page_for_writepage(struct writeback_control *wbc, |
1213 | struct page *page); | 1213 | struct page *page); |
1214 | void account_page_dirtied(struct page *page, struct address_space *mapping); | 1214 | void account_page_dirtied(struct page *page, struct address_space *mapping, |
1215 | void account_page_cleaned(struct page *page, struct address_space *mapping); | 1215 | struct mem_cgroup *memcg); |
1216 | void account_page_cleaned(struct page *page, struct address_space *mapping, | ||
1217 | struct mem_cgroup *memcg); | ||
1216 | int set_page_dirty(struct page *page); | 1218 | int set_page_dirty(struct page *page); |
1217 | int set_page_dirty_lock(struct page *page); | 1219 | int set_page_dirty_lock(struct page *page); |
1218 | void cancel_dirty_page(struct page *page); | 1220 | void cancel_dirty_page(struct page *page); |
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 4b3736f7065c..fb0814ca65c7 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h | |||
@@ -651,7 +651,8 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping, | |||
651 | int add_to_page_cache_lru(struct page *page, struct address_space *mapping, | 651 | int add_to_page_cache_lru(struct page *page, struct address_space *mapping, |
652 | pgoff_t index, gfp_t gfp_mask); | 652 | pgoff_t index, gfp_t gfp_mask); |
653 | extern void delete_from_page_cache(struct page *page); | 653 | extern void delete_from_page_cache(struct page *page); |
654 | extern void __delete_from_page_cache(struct page *page, void *shadow); | 654 | extern void __delete_from_page_cache(struct page *page, void *shadow, |
655 | struct mem_cgroup *memcg); | ||
655 | int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask); | 656 | int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask); |
656 | 657 | ||
657 | /* | 658 | /* |
diff --git a/mm/filemap.c b/mm/filemap.c index 6bf5e42d560a..7b1443dc3ad0 100644 --- a/mm/filemap.c +++ b/mm/filemap.c | |||
@@ -100,6 +100,7 @@ | |||
100 | * ->tree_lock (page_remove_rmap->set_page_dirty) | 100 | * ->tree_lock (page_remove_rmap->set_page_dirty) |
101 | * bdi.wb->list_lock (page_remove_rmap->set_page_dirty) | 101 | * bdi.wb->list_lock (page_remove_rmap->set_page_dirty) |
102 | * ->inode->i_lock (page_remove_rmap->set_page_dirty) | 102 | * ->inode->i_lock (page_remove_rmap->set_page_dirty) |
103 | * ->memcg->move_lock (page_remove_rmap->mem_cgroup_begin_page_stat) | ||
103 | * bdi.wb->list_lock (zap_pte_range->set_page_dirty) | 104 | * bdi.wb->list_lock (zap_pte_range->set_page_dirty) |
104 | * ->inode->i_lock (zap_pte_range->set_page_dirty) | 105 | * ->inode->i_lock (zap_pte_range->set_page_dirty) |
105 | * ->private_lock (zap_pte_range->__set_page_dirty_buffers) | 106 | * ->private_lock (zap_pte_range->__set_page_dirty_buffers) |
@@ -174,9 +175,11 @@ static void page_cache_tree_delete(struct address_space *mapping, | |||
174 | /* | 175 | /* |
175 | * Delete a page from the page cache and free it. Caller has to make | 176 | * Delete a page from the page cache and free it. Caller has to make |
176 | * sure the page is locked and that nobody else uses it - or that usage | 177 | * sure the page is locked and that nobody else uses it - or that usage |
177 | * is safe. The caller must hold the mapping's tree_lock. | 178 | * is safe. The caller must hold the mapping's tree_lock and |
179 | * mem_cgroup_begin_page_stat(). | ||
178 | */ | 180 | */ |
179 | void __delete_from_page_cache(struct page *page, void *shadow) | 181 | void __delete_from_page_cache(struct page *page, void *shadow, |
182 | struct mem_cgroup *memcg) | ||
180 | { | 183 | { |
181 | struct address_space *mapping = page->mapping; | 184 | struct address_space *mapping = page->mapping; |
182 | 185 | ||
@@ -210,7 +213,7 @@ void __delete_from_page_cache(struct page *page, void *shadow) | |||
210 | * anyway will be cleared before returning page into buddy allocator. | 213 | * anyway will be cleared before returning page into buddy allocator. |
211 | */ | 214 | */ |
212 | if (WARN_ON_ONCE(PageDirty(page))) | 215 | if (WARN_ON_ONCE(PageDirty(page))) |
213 | account_page_cleaned(page, mapping); | 216 | account_page_cleaned(page, mapping, memcg); |
214 | } | 217 | } |
215 | 218 | ||
216 | /** | 219 | /** |
@@ -224,14 +227,20 @@ void __delete_from_page_cache(struct page *page, void *shadow) | |||
224 | void delete_from_page_cache(struct page *page) | 227 | void delete_from_page_cache(struct page *page) |
225 | { | 228 | { |
226 | struct address_space *mapping = page->mapping; | 229 | struct address_space *mapping = page->mapping; |
230 | struct mem_cgroup *memcg; | ||
231 | unsigned long flags; | ||
232 | |||
227 | void (*freepage)(struct page *); | 233 | void (*freepage)(struct page *); |
228 | 234 | ||
229 | BUG_ON(!PageLocked(page)); | 235 | BUG_ON(!PageLocked(page)); |
230 | 236 | ||
231 | freepage = mapping->a_ops->freepage; | 237 | freepage = mapping->a_ops->freepage; |
232 | spin_lock_irq(&mapping->tree_lock); | 238 | |
233 | __delete_from_page_cache(page, NULL); | 239 | memcg = mem_cgroup_begin_page_stat(page); |
234 | spin_unlock_irq(&mapping->tree_lock); | 240 | spin_lock_irqsave(&mapping->tree_lock, flags); |
241 | __delete_from_page_cache(page, NULL, memcg); | ||
242 | spin_unlock_irqrestore(&mapping->tree_lock, flags); | ||
243 | mem_cgroup_end_page_stat(memcg); | ||
235 | 244 | ||
236 | if (freepage) | 245 | if (freepage) |
237 | freepage(page); | 246 | freepage(page); |
@@ -470,6 +479,8 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask) | |||
470 | if (!error) { | 479 | if (!error) { |
471 | struct address_space *mapping = old->mapping; | 480 | struct address_space *mapping = old->mapping; |
472 | void (*freepage)(struct page *); | 481 | void (*freepage)(struct page *); |
482 | struct mem_cgroup *memcg; | ||
483 | unsigned long flags; | ||
473 | 484 | ||
474 | pgoff_t offset = old->index; | 485 | pgoff_t offset = old->index; |
475 | freepage = mapping->a_ops->freepage; | 486 | freepage = mapping->a_ops->freepage; |
@@ -478,15 +489,17 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask) | |||
478 | new->mapping = mapping; | 489 | new->mapping = mapping; |
479 | new->index = offset; | 490 | new->index = offset; |
480 | 491 | ||
481 | spin_lock_irq(&mapping->tree_lock); | 492 | memcg = mem_cgroup_begin_page_stat(old); |
482 | __delete_from_page_cache(old, NULL); | 493 | spin_lock_irqsave(&mapping->tree_lock, flags); |
494 | __delete_from_page_cache(old, NULL, memcg); | ||
483 | error = radix_tree_insert(&mapping->page_tree, offset, new); | 495 | error = radix_tree_insert(&mapping->page_tree, offset, new); |
484 | BUG_ON(error); | 496 | BUG_ON(error); |
485 | mapping->nrpages++; | 497 | mapping->nrpages++; |
486 | __inc_zone_page_state(new, NR_FILE_PAGES); | 498 | __inc_zone_page_state(new, NR_FILE_PAGES); |
487 | if (PageSwapBacked(new)) | 499 | if (PageSwapBacked(new)) |
488 | __inc_zone_page_state(new, NR_SHMEM); | 500 | __inc_zone_page_state(new, NR_SHMEM); |
489 | spin_unlock_irq(&mapping->tree_lock); | 501 | spin_unlock_irqrestore(&mapping->tree_lock, flags); |
502 | mem_cgroup_end_page_stat(memcg); | ||
490 | mem_cgroup_migrate(old, new, true); | 503 | mem_cgroup_migrate(old, new, true); |
491 | radix_tree_preload_end(); | 504 | radix_tree_preload_end(); |
492 | if (freepage) | 505 | if (freepage) |
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 14c2f2017e37..c23c1a3e8e16 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -90,6 +90,7 @@ static const char * const mem_cgroup_stat_names[] = { | |||
90 | "rss", | 90 | "rss", |
91 | "rss_huge", | 91 | "rss_huge", |
92 | "mapped_file", | 92 | "mapped_file", |
93 | "dirty", | ||
93 | "writeback", | 94 | "writeback", |
94 | "swap", | 95 | "swap", |
95 | }; | 96 | }; |
@@ -2011,6 +2012,7 @@ again: | |||
2011 | 2012 | ||
2012 | return memcg; | 2013 | return memcg; |
2013 | } | 2014 | } |
2015 | EXPORT_SYMBOL(mem_cgroup_begin_page_stat); | ||
2014 | 2016 | ||
2015 | /** | 2017 | /** |
2016 | * mem_cgroup_end_page_stat - finish a page state statistics transaction | 2018 | * mem_cgroup_end_page_stat - finish a page state statistics transaction |
@@ -2029,6 +2031,7 @@ void mem_cgroup_end_page_stat(struct mem_cgroup *memcg) | |||
2029 | 2031 | ||
2030 | rcu_read_unlock(); | 2032 | rcu_read_unlock(); |
2031 | } | 2033 | } |
2034 | EXPORT_SYMBOL(mem_cgroup_end_page_stat); | ||
2032 | 2035 | ||
2033 | /** | 2036 | /** |
2034 | * mem_cgroup_update_page_stat - update page state statistics | 2037 | * mem_cgroup_update_page_stat - update page state statistics |
@@ -4746,6 +4749,7 @@ static int mem_cgroup_move_account(struct page *page, | |||
4746 | { | 4749 | { |
4747 | unsigned long flags; | 4750 | unsigned long flags; |
4748 | int ret; | 4751 | int ret; |
4752 | bool anon; | ||
4749 | 4753 | ||
4750 | VM_BUG_ON(from == to); | 4754 | VM_BUG_ON(from == to); |
4751 | VM_BUG_ON_PAGE(PageLRU(page), page); | 4755 | VM_BUG_ON_PAGE(PageLRU(page), page); |
@@ -4771,15 +4775,33 @@ static int mem_cgroup_move_account(struct page *page, | |||
4771 | if (page->mem_cgroup != from) | 4775 | if (page->mem_cgroup != from) |
4772 | goto out_unlock; | 4776 | goto out_unlock; |
4773 | 4777 | ||
4778 | anon = PageAnon(page); | ||
4779 | |||
4774 | spin_lock_irqsave(&from->move_lock, flags); | 4780 | spin_lock_irqsave(&from->move_lock, flags); |
4775 | 4781 | ||
4776 | if (!PageAnon(page) && page_mapped(page)) { | 4782 | if (!anon && page_mapped(page)) { |
4777 | __this_cpu_sub(from->stat->count[MEM_CGROUP_STAT_FILE_MAPPED], | 4783 | __this_cpu_sub(from->stat->count[MEM_CGROUP_STAT_FILE_MAPPED], |
4778 | nr_pages); | 4784 | nr_pages); |
4779 | __this_cpu_add(to->stat->count[MEM_CGROUP_STAT_FILE_MAPPED], | 4785 | __this_cpu_add(to->stat->count[MEM_CGROUP_STAT_FILE_MAPPED], |
4780 | nr_pages); | 4786 | nr_pages); |
4781 | } | 4787 | } |
4782 | 4788 | ||
4789 | /* | ||
4790 | * move_lock grabbed above and caller set from->moving_account, so | ||
4791 | * mem_cgroup_update_page_stat() will serialize updates to PageDirty. | ||
4792 | * So mapping should be stable for dirty pages. | ||
4793 | */ | ||
4794 | if (!anon && PageDirty(page)) { | ||
4795 | struct address_space *mapping = page_mapping(page); | ||
4796 | |||
4797 | if (mapping_cap_account_dirty(mapping)) { | ||
4798 | __this_cpu_sub(from->stat->count[MEM_CGROUP_STAT_DIRTY], | ||
4799 | nr_pages); | ||
4800 | __this_cpu_add(to->stat->count[MEM_CGROUP_STAT_DIRTY], | ||
4801 | nr_pages); | ||
4802 | } | ||
4803 | } | ||
4804 | |||
4783 | if (PageWriteback(page)) { | 4805 | if (PageWriteback(page)) { |
4784 | __this_cpu_sub(from->stat->count[MEM_CGROUP_STAT_WRITEBACK], | 4806 | __this_cpu_sub(from->stat->count[MEM_CGROUP_STAT_WRITEBACK], |
4785 | nr_pages); | 4807 | nr_pages); |
diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 227b867598e1..bdeecad00489 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c | |||
@@ -2090,15 +2090,20 @@ int __set_page_dirty_no_writeback(struct page *page) | |||
2090 | 2090 | ||
2091 | /* | 2091 | /* |
2092 | * Helper function for set_page_dirty family. | 2092 | * Helper function for set_page_dirty family. |
2093 | * | ||
2094 | * Caller must hold mem_cgroup_begin_page_stat(). | ||
2095 | * | ||
2093 | * NOTE: This relies on being atomic wrt interrupts. | 2096 | * NOTE: This relies on being atomic wrt interrupts. |
2094 | */ | 2097 | */ |
2095 | void account_page_dirtied(struct page *page, struct address_space *mapping) | 2098 | void account_page_dirtied(struct page *page, struct address_space *mapping, |
2099 | struct mem_cgroup *memcg) | ||
2096 | { | 2100 | { |
2097 | trace_writeback_dirty_page(page, mapping); | 2101 | trace_writeback_dirty_page(page, mapping); |
2098 | 2102 | ||
2099 | if (mapping_cap_account_dirty(mapping)) { | 2103 | if (mapping_cap_account_dirty(mapping)) { |
2100 | struct backing_dev_info *bdi = inode_to_bdi(mapping->host); | 2104 | struct backing_dev_info *bdi = inode_to_bdi(mapping->host); |
2101 | 2105 | ||
2106 | mem_cgroup_inc_page_stat(memcg, MEM_CGROUP_STAT_DIRTY); | ||
2102 | __inc_zone_page_state(page, NR_FILE_DIRTY); | 2107 | __inc_zone_page_state(page, NR_FILE_DIRTY); |
2103 | __inc_zone_page_state(page, NR_DIRTIED); | 2108 | __inc_zone_page_state(page, NR_DIRTIED); |
2104 | __inc_bdi_stat(bdi, BDI_RECLAIMABLE); | 2109 | __inc_bdi_stat(bdi, BDI_RECLAIMABLE); |
@@ -2112,10 +2117,14 @@ EXPORT_SYMBOL(account_page_dirtied); | |||
2112 | 2117 | ||
2113 | /* | 2118 | /* |
2114 | * Helper function for deaccounting dirty page without writeback. | 2119 | * Helper function for deaccounting dirty page without writeback. |
2120 | * | ||
2121 | * Caller must hold mem_cgroup_begin_page_stat(). | ||
2115 | */ | 2122 | */ |
2116 | void account_page_cleaned(struct page *page, struct address_space *mapping) | 2123 | void account_page_cleaned(struct page *page, struct address_space *mapping, |
2124 | struct mem_cgroup *memcg) | ||
2117 | { | 2125 | { |
2118 | if (mapping_cap_account_dirty(mapping)) { | 2126 | if (mapping_cap_account_dirty(mapping)) { |
2127 | mem_cgroup_dec_page_stat(memcg, MEM_CGROUP_STAT_DIRTY); | ||
2119 | dec_zone_page_state(page, NR_FILE_DIRTY); | 2128 | dec_zone_page_state(page, NR_FILE_DIRTY); |
2120 | dec_bdi_stat(inode_to_bdi(mapping->host), BDI_RECLAIMABLE); | 2129 | dec_bdi_stat(inode_to_bdi(mapping->host), BDI_RECLAIMABLE); |
2121 | task_io_account_cancelled_write(PAGE_CACHE_SIZE); | 2130 | task_io_account_cancelled_write(PAGE_CACHE_SIZE); |
@@ -2136,26 +2145,34 @@ void account_page_cleaned(struct page *page, struct address_space *mapping) | |||
2136 | */ | 2145 | */ |
2137 | int __set_page_dirty_nobuffers(struct page *page) | 2146 | int __set_page_dirty_nobuffers(struct page *page) |
2138 | { | 2147 | { |
2148 | struct mem_cgroup *memcg; | ||
2149 | |||
2150 | memcg = mem_cgroup_begin_page_stat(page); | ||
2139 | if (!TestSetPageDirty(page)) { | 2151 | if (!TestSetPageDirty(page)) { |
2140 | struct address_space *mapping = page_mapping(page); | 2152 | struct address_space *mapping = page_mapping(page); |
2141 | unsigned long flags; | 2153 | unsigned long flags; |
2142 | 2154 | ||
2143 | if (!mapping) | 2155 | if (!mapping) { |
2156 | mem_cgroup_end_page_stat(memcg); | ||
2144 | return 1; | 2157 | return 1; |
2158 | } | ||
2145 | 2159 | ||
2146 | spin_lock_irqsave(&mapping->tree_lock, flags); | 2160 | spin_lock_irqsave(&mapping->tree_lock, flags); |
2147 | BUG_ON(page_mapping(page) != mapping); | 2161 | BUG_ON(page_mapping(page) != mapping); |
2148 | WARN_ON_ONCE(!PagePrivate(page) && !PageUptodate(page)); | 2162 | WARN_ON_ONCE(!PagePrivate(page) && !PageUptodate(page)); |
2149 | account_page_dirtied(page, mapping); | 2163 | account_page_dirtied(page, mapping, memcg); |
2150 | radix_tree_tag_set(&mapping->page_tree, page_index(page), | 2164 | radix_tree_tag_set(&mapping->page_tree, page_index(page), |
2151 | PAGECACHE_TAG_DIRTY); | 2165 | PAGECACHE_TAG_DIRTY); |
2152 | spin_unlock_irqrestore(&mapping->tree_lock, flags); | 2166 | spin_unlock_irqrestore(&mapping->tree_lock, flags); |
2167 | mem_cgroup_end_page_stat(memcg); | ||
2168 | |||
2153 | if (mapping->host) { | 2169 | if (mapping->host) { |
2154 | /* !PageAnon && !swapper_space */ | 2170 | /* !PageAnon && !swapper_space */ |
2155 | __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); | 2171 | __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); |
2156 | } | 2172 | } |
2157 | return 1; | 2173 | return 1; |
2158 | } | 2174 | } |
2175 | mem_cgroup_end_page_stat(memcg); | ||
2159 | return 0; | 2176 | return 0; |
2160 | } | 2177 | } |
2161 | EXPORT_SYMBOL(__set_page_dirty_nobuffers); | 2178 | EXPORT_SYMBOL(__set_page_dirty_nobuffers); |
@@ -2273,8 +2290,20 @@ EXPORT_SYMBOL(set_page_dirty_lock); | |||
2273 | */ | 2290 | */ |
2274 | void cancel_dirty_page(struct page *page) | 2291 | void cancel_dirty_page(struct page *page) |
2275 | { | 2292 | { |
2276 | if (TestClearPageDirty(page)) | 2293 | struct address_space *mapping = page_mapping(page); |
2277 | account_page_cleaned(page, page_mapping(page)); | 2294 | |
2295 | if (mapping_cap_account_dirty(mapping)) { | ||
2296 | struct mem_cgroup *memcg; | ||
2297 | |||
2298 | memcg = mem_cgroup_begin_page_stat(page); | ||
2299 | |||
2300 | if (TestClearPageDirty(page)) | ||
2301 | account_page_cleaned(page, mapping, memcg); | ||
2302 | |||
2303 | mem_cgroup_end_page_stat(memcg); | ||
2304 | } else { | ||
2305 | ClearPageDirty(page); | ||
2306 | } | ||
2278 | } | 2307 | } |
2279 | EXPORT_SYMBOL(cancel_dirty_page); | 2308 | EXPORT_SYMBOL(cancel_dirty_page); |
2280 | 2309 | ||
@@ -2295,6 +2324,8 @@ EXPORT_SYMBOL(cancel_dirty_page); | |||
2295 | int clear_page_dirty_for_io(struct page *page) | 2324 | int clear_page_dirty_for_io(struct page *page) |
2296 | { | 2325 | { |
2297 | struct address_space *mapping = page_mapping(page); | 2326 | struct address_space *mapping = page_mapping(page); |
2327 | struct mem_cgroup *memcg; | ||
2328 | int ret = 0; | ||
2298 | 2329 | ||
2299 | BUG_ON(!PageLocked(page)); | 2330 | BUG_ON(!PageLocked(page)); |
2300 | 2331 | ||
@@ -2334,13 +2365,16 @@ int clear_page_dirty_for_io(struct page *page) | |||
2334 | * always locked coming in here, so we get the desired | 2365 | * always locked coming in here, so we get the desired |
2335 | * exclusion. | 2366 | * exclusion. |
2336 | */ | 2367 | */ |
2368 | memcg = mem_cgroup_begin_page_stat(page); | ||
2337 | if (TestClearPageDirty(page)) { | 2369 | if (TestClearPageDirty(page)) { |
2370 | mem_cgroup_dec_page_stat(memcg, MEM_CGROUP_STAT_DIRTY); | ||
2338 | dec_zone_page_state(page, NR_FILE_DIRTY); | 2371 | dec_zone_page_state(page, NR_FILE_DIRTY); |
2339 | dec_bdi_stat(inode_to_bdi(mapping->host), | 2372 | dec_bdi_stat(inode_to_bdi(mapping->host), |
2340 | BDI_RECLAIMABLE); | 2373 | BDI_RECLAIMABLE); |
2341 | return 1; | 2374 | ret = 1; |
2342 | } | 2375 | } |
2343 | return 0; | 2376 | mem_cgroup_end_page_stat(memcg); |
2377 | return ret; | ||
2344 | } | 2378 | } |
2345 | return TestClearPageDirty(page); | 2379 | return TestClearPageDirty(page); |
2346 | } | 2380 | } |
@@ -30,6 +30,8 @@ | |||
30 | * swap_lock (in swap_duplicate, swap_info_get) | 30 | * swap_lock (in swap_duplicate, swap_info_get) |
31 | * mmlist_lock (in mmput, drain_mmlist and others) | 31 | * mmlist_lock (in mmput, drain_mmlist and others) |
32 | * mapping->private_lock (in __set_page_dirty_buffers) | 32 | * mapping->private_lock (in __set_page_dirty_buffers) |
33 | * mem_cgroup_{begin,end}_page_stat (memcg->move_lock) | ||
34 | * mapping->tree_lock (widely used) | ||
33 | * inode->i_lock (in set_page_dirty's __mark_inode_dirty) | 35 | * inode->i_lock (in set_page_dirty's __mark_inode_dirty) |
34 | * bdi.wb->list_lock (in set_page_dirty's __mark_inode_dirty) | 36 | * bdi.wb->list_lock (in set_page_dirty's __mark_inode_dirty) |
35 | * sb_lock (within inode_lock in fs/fs-writeback.c) | 37 | * sb_lock (within inode_lock in fs/fs-writeback.c) |
diff --git a/mm/truncate.c b/mm/truncate.c index 0c360259c085..76e35ad97102 100644 --- a/mm/truncate.c +++ b/mm/truncate.c | |||
@@ -510,19 +510,24 @@ EXPORT_SYMBOL(invalidate_mapping_pages); | |||
510 | static int | 510 | static int |
511 | invalidate_complete_page2(struct address_space *mapping, struct page *page) | 511 | invalidate_complete_page2(struct address_space *mapping, struct page *page) |
512 | { | 512 | { |
513 | struct mem_cgroup *memcg; | ||
514 | unsigned long flags; | ||
515 | |||
513 | if (page->mapping != mapping) | 516 | if (page->mapping != mapping) |
514 | return 0; | 517 | return 0; |
515 | 518 | ||
516 | if (page_has_private(page) && !try_to_release_page(page, GFP_KERNEL)) | 519 | if (page_has_private(page) && !try_to_release_page(page, GFP_KERNEL)) |
517 | return 0; | 520 | return 0; |
518 | 521 | ||
519 | spin_lock_irq(&mapping->tree_lock); | 522 | memcg = mem_cgroup_begin_page_stat(page); |
523 | spin_lock_irqsave(&mapping->tree_lock, flags); | ||
520 | if (PageDirty(page)) | 524 | if (PageDirty(page)) |
521 | goto failed; | 525 | goto failed; |
522 | 526 | ||
523 | BUG_ON(page_has_private(page)); | 527 | BUG_ON(page_has_private(page)); |
524 | __delete_from_page_cache(page, NULL); | 528 | __delete_from_page_cache(page, NULL, memcg); |
525 | spin_unlock_irq(&mapping->tree_lock); | 529 | spin_unlock_irqrestore(&mapping->tree_lock, flags); |
530 | mem_cgroup_end_page_stat(memcg); | ||
526 | 531 | ||
527 | if (mapping->a_ops->freepage) | 532 | if (mapping->a_ops->freepage) |
528 | mapping->a_ops->freepage(page); | 533 | mapping->a_ops->freepage(page); |
@@ -530,7 +535,8 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page) | |||
530 | page_cache_release(page); /* pagecache ref */ | 535 | page_cache_release(page); /* pagecache ref */ |
531 | return 1; | 536 | return 1; |
532 | failed: | 537 | failed: |
533 | spin_unlock_irq(&mapping->tree_lock); | 538 | spin_unlock_irqrestore(&mapping->tree_lock, flags); |
539 | mem_cgroup_end_page_stat(memcg); | ||
534 | return 0; | 540 | return 0; |
535 | } | 541 | } |
536 | 542 | ||
diff --git a/mm/vmscan.c b/mm/vmscan.c index 5e8eadd71bac..7582f9fcda92 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
@@ -579,10 +579,14 @@ static pageout_t pageout(struct page *page, struct address_space *mapping, | |||
579 | static int __remove_mapping(struct address_space *mapping, struct page *page, | 579 | static int __remove_mapping(struct address_space *mapping, struct page *page, |
580 | bool reclaimed) | 580 | bool reclaimed) |
581 | { | 581 | { |
582 | unsigned long flags; | ||
583 | struct mem_cgroup *memcg; | ||
584 | |||
582 | BUG_ON(!PageLocked(page)); | 585 | BUG_ON(!PageLocked(page)); |
583 | BUG_ON(mapping != page_mapping(page)); | 586 | BUG_ON(mapping != page_mapping(page)); |
584 | 587 | ||
585 | spin_lock_irq(&mapping->tree_lock); | 588 | memcg = mem_cgroup_begin_page_stat(page); |
589 | spin_lock_irqsave(&mapping->tree_lock, flags); | ||
586 | /* | 590 | /* |
587 | * The non racy check for a busy page. | 591 | * The non racy check for a busy page. |
588 | * | 592 | * |
@@ -620,7 +624,8 @@ static int __remove_mapping(struct address_space *mapping, struct page *page, | |||
620 | swp_entry_t swap = { .val = page_private(page) }; | 624 | swp_entry_t swap = { .val = page_private(page) }; |
621 | mem_cgroup_swapout(page, swap); | 625 | mem_cgroup_swapout(page, swap); |
622 | __delete_from_swap_cache(page); | 626 | __delete_from_swap_cache(page); |
623 | spin_unlock_irq(&mapping->tree_lock); | 627 | spin_unlock_irqrestore(&mapping->tree_lock, flags); |
628 | mem_cgroup_end_page_stat(memcg); | ||
624 | swapcache_free(swap); | 629 | swapcache_free(swap); |
625 | } else { | 630 | } else { |
626 | void (*freepage)(struct page *); | 631 | void (*freepage)(struct page *); |
@@ -640,8 +645,9 @@ static int __remove_mapping(struct address_space *mapping, struct page *page, | |||
640 | if (reclaimed && page_is_file_cache(page) && | 645 | if (reclaimed && page_is_file_cache(page) && |
641 | !mapping_exiting(mapping)) | 646 | !mapping_exiting(mapping)) |
642 | shadow = workingset_eviction(mapping, page); | 647 | shadow = workingset_eviction(mapping, page); |
643 | __delete_from_page_cache(page, shadow); | 648 | __delete_from_page_cache(page, shadow, memcg); |
644 | spin_unlock_irq(&mapping->tree_lock); | 649 | spin_unlock_irqrestore(&mapping->tree_lock, flags); |
650 | mem_cgroup_end_page_stat(memcg); | ||
645 | 651 | ||
646 | if (freepage != NULL) | 652 | if (freepage != NULL) |
647 | freepage(page); | 653 | freepage(page); |
@@ -650,7 +656,8 @@ static int __remove_mapping(struct address_space *mapping, struct page *page, | |||
650 | return 1; | 656 | return 1; |
651 | 657 | ||
652 | cannot_free: | 658 | cannot_free: |
653 | spin_unlock_irq(&mapping->tree_lock); | 659 | spin_unlock_irqrestore(&mapping->tree_lock, flags); |
660 | mem_cgroup_end_page_stat(memcg); | ||
654 | return 0; | 661 | return 0; |
655 | } | 662 | } |
656 | 663 | ||