aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc
diff options
context:
space:
mode:
authorVlastimil Babka <vbabka@suse.cz>2016-01-14 18:19:17 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-01-14 19:00:49 -0500
commitc261e7d94f0dd33a34b6cf98686e8b9699b62340 (patch)
treed47540072dd03aae0fe2a4af548cef0cd5c44756 /fs/proc
parentbf9683d6990589390b5178dafe8fd06808869293 (diff)
mm, proc: account for shmem swap in /proc/pid/smaps
Currently, /proc/pid/smaps will always show "Swap: 0 kB" for shmem-backed mappings, even if the mapped portion does contain pages that were swapped out. This is because unlike private anonymous mappings, shmem does not change pte to swap entry, but pte_none when swapping the page out. In the smaps page walk, such page thus looks like it was never faulted in. This patch changes smaps_pte_entry() to determine the swap status for such pte_none entries for shmem mappings, similarly to how mincore_page() does it. Swapped out shmem pages are thus accounted for. For private mappings of tmpfs files that COWed some of the pages, swaped out status of the original shmem pages is naturally ignored. If some of the private copies was also swapped out, they are accounted via their page table swap entries, so the resulting reported swap usage is then a sum of both swapped out private copies, and swapped out shmem pages that were not COWed. No double accounting can thus happen. The accounting is arguably still not as precise as for private anonymous mappings, since now we will count also pages that the process in question never accessed, but another process populated them and then let them become swapped out. I believe it is still less confusing and subtle than not showing any swap usage by shmem mappings at all. Swapped out counter might of interest of users who would like to prevent from future swapins during performance critical operation and pre-fault them at their convenience. Especially for larger swapped out regions the cost of swapin is much higher than a fresh page allocation. So a differentiation between pte_none vs. swapped out is important for those usecases. One downside of this patch is that it makes /proc/pid/smaps more expensive for shmem mappings, as we consult the radix tree for each pte_none entry, so the overal complexity is O(n*log(n)). I have measured this on a process that creates a 2GB mapping and dirties single pages with a stride of 2MB, and time how long does it take to cat /proc/pid/smaps of this process 100 times. Private anonymous mapping: real 0m0.949s user 0m0.116s sys 0m0.348s Mapping of a /dev/shm/file: real 0m3.831s user 0m0.180s sys 0m3.212s The difference is rather substantial, so the next patch will reduce the cost for shared or read-only mappings. In a less controlled experiment, I've gathered pids of processes on my desktop that have either '/dev/shm/*' or 'SYSV*' in smaps. This included the Chrome browser and some KDE processes. Again, I've run cat /proc/pid/smaps on each 100 times. Before this patch: real 0m9.050s user 0m0.518s sys 0m8.066s After this patch: real 0m9.221s user 0m0.541s sys 0m8.187s This suggests low impact on average systems. Note that this patch doesn't attempt to adjust the SwapPss field for shmem mappings, which would need extra work to determine who else could have the pages mapped. Thus the value stays zero except for COWed swapped out pages in a shmem mapping, which are accounted as usual. Signed-off-by: Vlastimil Babka <vbabka@suse.cz> Acked-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru> Acked-by: Jerome Marchand <jmarchan@redhat.com> Acked-by: Michal Hocko <mhocko@suse.com> Cc: Hugh Dickins <hughd@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc')
-rw-r--r--fs/proc/task_mmu.c51
1 files changed, 51 insertions, 0 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 187b3b5f242e..85ef60fdf2c0 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -451,6 +451,7 @@ struct mem_size_stats {
451 unsigned long private_hugetlb; 451 unsigned long private_hugetlb;
452 u64 pss; 452 u64 pss;
453 u64 swap_pss; 453 u64 swap_pss;
454 bool check_shmem_swap;
454}; 455};
455 456
456static void smaps_account(struct mem_size_stats *mss, struct page *page, 457static void smaps_account(struct mem_size_stats *mss, struct page *page,
@@ -485,6 +486,45 @@ static void smaps_account(struct mem_size_stats *mss, struct page *page,
485 } 486 }
486} 487}
487 488
489#ifdef CONFIG_SHMEM
490static unsigned long smaps_shmem_swap(struct vm_area_struct *vma,
491 unsigned long addr)
492{
493 struct page *page;
494
495 page = find_get_entry(vma->vm_file->f_mapping,
496 linear_page_index(vma, addr));
497 if (!page)
498 return 0;
499
500 if (radix_tree_exceptional_entry(page))
501 return PAGE_SIZE;
502
503 page_cache_release(page);
504 return 0;
505
506}
507
508static int smaps_pte_hole(unsigned long addr, unsigned long end,
509 struct mm_walk *walk)
510{
511 struct mem_size_stats *mss = walk->private;
512
513 while (addr < end) {
514 mss->swap += smaps_shmem_swap(walk->vma, addr);
515 addr += PAGE_SIZE;
516 }
517
518 return 0;
519}
520#else
521static unsigned long smaps_shmem_swap(struct vm_area_struct *vma,
522 unsigned long addr)
523{
524 return 0;
525}
526#endif
527
488static void smaps_pte_entry(pte_t *pte, unsigned long addr, 528static void smaps_pte_entry(pte_t *pte, unsigned long addr,
489 struct mm_walk *walk) 529 struct mm_walk *walk)
490{ 530{
@@ -512,6 +552,9 @@ static void smaps_pte_entry(pte_t *pte, unsigned long addr,
512 } 552 }
513 } else if (is_migration_entry(swpent)) 553 } else if (is_migration_entry(swpent))
514 page = migration_entry_to_page(swpent); 554 page = migration_entry_to_page(swpent);
555 } else if (unlikely(IS_ENABLED(CONFIG_SHMEM) && mss->check_shmem_swap
556 && pte_none(*pte))) {
557 mss->swap += smaps_shmem_swap(vma, addr);
515 } 558 }
516 559
517 if (!page) 560 if (!page)
@@ -671,6 +714,14 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
671 }; 714 };
672 715
673 memset(&mss, 0, sizeof mss); 716 memset(&mss, 0, sizeof mss);
717
718#ifdef CONFIG_SHMEM
719 if (vma->vm_file && shmem_mapping(vma->vm_file->f_mapping)) {
720 mss.check_shmem_swap = true;
721 smaps_walk.pte_hole = smaps_pte_hole;
722 }
723#endif
724
674 /* mmap_sem is held in m_start */ 725 /* mmap_sem is held in m_start */
675 walk_page_vma(vma, &smaps_walk); 726 walk_page_vma(vma, &smaps_walk);
676 727