diff options
-rw-r--r-- | Documentation/filesystems/proc.txt | 18 | ||||
-rw-r--r-- | fs/proc/task_mmu.c | 18 | ||||
-rw-r--r-- | include/linux/swap.h | 6 | ||||
-rw-r--r-- | mm/swapfile.c | 42 |
4 files changed, 77 insertions, 7 deletions
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 6f7fafde0884..d411ca63c8b6 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt | |||
@@ -424,6 +424,7 @@ Private_Dirty: 0 kB | |||
424 | Referenced: 892 kB | 424 | Referenced: 892 kB |
425 | Anonymous: 0 kB | 425 | Anonymous: 0 kB |
426 | Swap: 0 kB | 426 | Swap: 0 kB |
427 | SwapPss: 0 kB | ||
427 | KernelPageSize: 4 kB | 428 | KernelPageSize: 4 kB |
428 | MMUPageSize: 4 kB | 429 | MMUPageSize: 4 kB |
429 | Locked: 374 kB | 430 | Locked: 374 kB |
@@ -433,16 +434,23 @@ the first of these lines shows the same information as is displayed for the | |||
433 | mapping in /proc/PID/maps. The remaining lines show the size of the mapping | 434 | mapping in /proc/PID/maps. The remaining lines show the size of the mapping |
434 | (size), the amount of the mapping that is currently resident in RAM (RSS), the | 435 | (size), the amount of the mapping that is currently resident in RAM (RSS), the |
435 | process' proportional share of this mapping (PSS), the number of clean and | 436 | process' proportional share of this mapping (PSS), the number of clean and |
436 | dirty private pages in the mapping. Note that even a page which is part of a | 437 | dirty private pages in the mapping. |
437 | MAP_SHARED mapping, but has only a single pte mapped, i.e. is currently used | 438 | |
438 | by only one process, is accounted as private and not as shared. "Referenced" | 439 | The "proportional set size" (PSS) of a process is the count of pages it has |
439 | indicates the amount of memory currently marked as referenced or accessed. | 440 | in memory, where each page is divided by the number of processes sharing it. |
441 | So if a process has 1000 pages all to itself, and 1000 shared with one other | ||
442 | process, its PSS will be 1500. | ||
443 | Note that even a page which is part of a MAP_SHARED mapping, but has only | ||
444 | a single pte mapped, i.e. is currently used by only one process, is accounted | ||
445 | as private and not as shared. | ||
446 | "Referenced" indicates the amount of memory currently marked as referenced or | ||
447 | accessed. | ||
440 | "Anonymous" shows the amount of memory that does not belong to any file. Even | 448 | "Anonymous" shows the amount of memory that does not belong to any file. Even |
441 | a mapping associated with a file may contain anonymous pages: when MAP_PRIVATE | 449 | a mapping associated with a file may contain anonymous pages: when MAP_PRIVATE |
442 | and a page is modified, the file page is replaced by a private anonymous copy. | 450 | and a page is modified, the file page is replaced by a private anonymous copy. |
443 | "Swap" shows how much would-be-anonymous memory is also used, but out on | 451 | "Swap" shows how much would-be-anonymous memory is also used, but out on |
444 | swap. | 452 | swap. |
445 | 453 | "SwapPss" shows proportional swap share of this mapping. | |
446 | "VmFlags" field deserves a separate description. This member represents the kernel | 454 | "VmFlags" field deserves a separate description. This member represents the kernel |
447 | flags associated with the particular virtual memory area in two letter encoded | 455 | flags associated with the particular virtual memory area in two letter encoded |
448 | manner. The codes are the following: | 456 | manner. The codes are the following: |
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 67c76468a7be..41f1a50c10c9 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -446,6 +446,7 @@ struct mem_size_stats { | |||
446 | unsigned long anonymous_thp; | 446 | unsigned long anonymous_thp; |
447 | unsigned long swap; | 447 | unsigned long swap; |
448 | u64 pss; | 448 | u64 pss; |
449 | u64 swap_pss; | ||
449 | }; | 450 | }; |
450 | 451 | ||
451 | static void smaps_account(struct mem_size_stats *mss, struct page *page, | 452 | static void smaps_account(struct mem_size_stats *mss, struct page *page, |
@@ -492,9 +493,20 @@ static void smaps_pte_entry(pte_t *pte, unsigned long addr, | |||
492 | } else if (is_swap_pte(*pte)) { | 493 | } else if (is_swap_pte(*pte)) { |
493 | swp_entry_t swpent = pte_to_swp_entry(*pte); | 494 | swp_entry_t swpent = pte_to_swp_entry(*pte); |
494 | 495 | ||
495 | if (!non_swap_entry(swpent)) | 496 | if (!non_swap_entry(swpent)) { |
497 | int mapcount; | ||
498 | |||
496 | mss->swap += PAGE_SIZE; | 499 | mss->swap += PAGE_SIZE; |
497 | else if (is_migration_entry(swpent)) | 500 | mapcount = swp_swapcount(swpent); |
501 | if (mapcount >= 2) { | ||
502 | u64 pss_delta = (u64)PAGE_SIZE << PSS_SHIFT; | ||
503 | |||
504 | do_div(pss_delta, mapcount); | ||
505 | mss->swap_pss += pss_delta; | ||
506 | } else { | ||
507 | mss->swap_pss += (u64)PAGE_SIZE << PSS_SHIFT; | ||
508 | } | ||
509 | } else if (is_migration_entry(swpent)) | ||
498 | page = migration_entry_to_page(swpent); | 510 | page = migration_entry_to_page(swpent); |
499 | } | 511 | } |
500 | 512 | ||
@@ -640,6 +652,7 @@ static int show_smap(struct seq_file *m, void *v, int is_pid) | |||
640 | "Anonymous: %8lu kB\n" | 652 | "Anonymous: %8lu kB\n" |
641 | "AnonHugePages: %8lu kB\n" | 653 | "AnonHugePages: %8lu kB\n" |
642 | "Swap: %8lu kB\n" | 654 | "Swap: %8lu kB\n" |
655 | "SwapPss: %8lu kB\n" | ||
643 | "KernelPageSize: %8lu kB\n" | 656 | "KernelPageSize: %8lu kB\n" |
644 | "MMUPageSize: %8lu kB\n" | 657 | "MMUPageSize: %8lu kB\n" |
645 | "Locked: %8lu kB\n", | 658 | "Locked: %8lu kB\n", |
@@ -654,6 +667,7 @@ static int show_smap(struct seq_file *m, void *v, int is_pid) | |||
654 | mss.anonymous >> 10, | 667 | mss.anonymous >> 10, |
655 | mss.anonymous_thp >> 10, | 668 | mss.anonymous_thp >> 10, |
656 | mss.swap >> 10, | 669 | mss.swap >> 10, |
670 | (unsigned long)(mss.swap_pss >> (10 + PSS_SHIFT)), | ||
657 | vma_kernel_pagesize(vma) >> 10, | 671 | vma_kernel_pagesize(vma) >> 10, |
658 | vma_mmu_pagesize(vma) >> 10, | 672 | vma_mmu_pagesize(vma) >> 10, |
659 | (vma->vm_flags & VM_LOCKED) ? | 673 | (vma->vm_flags & VM_LOCKED) ? |
diff --git a/include/linux/swap.h b/include/linux/swap.h index 31496d201fdc..6282f1eb3d6a 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h | |||
@@ -431,6 +431,7 @@ extern unsigned int count_swap_pages(int, int); | |||
431 | extern sector_t map_swap_page(struct page *, struct block_device **); | 431 | extern sector_t map_swap_page(struct page *, struct block_device **); |
432 | extern sector_t swapdev_block(int, pgoff_t); | 432 | extern sector_t swapdev_block(int, pgoff_t); |
433 | extern int page_swapcount(struct page *); | 433 | extern int page_swapcount(struct page *); |
434 | extern int swp_swapcount(swp_entry_t entry); | ||
434 | extern struct swap_info_struct *page_swap_info(struct page *); | 435 | extern struct swap_info_struct *page_swap_info(struct page *); |
435 | extern int reuse_swap_page(struct page *); | 436 | extern int reuse_swap_page(struct page *); |
436 | extern int try_to_free_swap(struct page *); | 437 | extern int try_to_free_swap(struct page *); |
@@ -522,6 +523,11 @@ static inline int page_swapcount(struct page *page) | |||
522 | return 0; | 523 | return 0; |
523 | } | 524 | } |
524 | 525 | ||
526 | static inline int swp_swapcount(swp_entry_t entry) | ||
527 | { | ||
528 | return 0; | ||
529 | } | ||
530 | |||
525 | #define reuse_swap_page(page) (page_mapcount(page) == 1) | 531 | #define reuse_swap_page(page) (page_mapcount(page) == 1) |
526 | 532 | ||
527 | static inline int try_to_free_swap(struct page *page) | 533 | static inline int try_to_free_swap(struct page *page) |
diff --git a/mm/swapfile.c b/mm/swapfile.c index aebc2dd6e649..58877312cf6b 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c | |||
@@ -875,6 +875,48 @@ int page_swapcount(struct page *page) | |||
875 | } | 875 | } |
876 | 876 | ||
877 | /* | 877 | /* |
878 | * How many references to @entry are currently swapped out? | ||
879 | * This considers COUNT_CONTINUED so it returns exact answer. | ||
880 | */ | ||
881 | int swp_swapcount(swp_entry_t entry) | ||
882 | { | ||
883 | int count, tmp_count, n; | ||
884 | struct swap_info_struct *p; | ||
885 | struct page *page; | ||
886 | pgoff_t offset; | ||
887 | unsigned char *map; | ||
888 | |||
889 | p = swap_info_get(entry); | ||
890 | if (!p) | ||
891 | return 0; | ||
892 | |||
893 | count = swap_count(p->swap_map[swp_offset(entry)]); | ||
894 | if (!(count & COUNT_CONTINUED)) | ||
895 | goto out; | ||
896 | |||
897 | count &= ~COUNT_CONTINUED; | ||
898 | n = SWAP_MAP_MAX + 1; | ||
899 | |||
900 | offset = swp_offset(entry); | ||
901 | page = vmalloc_to_page(p->swap_map + offset); | ||
902 | offset &= ~PAGE_MASK; | ||
903 | VM_BUG_ON(page_private(page) != SWP_CONTINUED); | ||
904 | |||
905 | do { | ||
906 | page = list_entry(page->lru.next, struct page, lru); | ||
907 | map = kmap_atomic(page); | ||
908 | tmp_count = map[offset]; | ||
909 | kunmap_atomic(map); | ||
910 | |||
911 | count += (tmp_count & ~COUNT_CONTINUED) * n; | ||
912 | n *= (SWAP_CONT_MAX + 1); | ||
913 | } while (tmp_count & COUNT_CONTINUED); | ||
914 | out: | ||
915 | spin_unlock(&p->lock); | ||
916 | return count; | ||
917 | } | ||
918 | |||
919 | /* | ||
878 | * We can write to an anon page without COW if there are no other references | 920 | * We can write to an anon page without COW if there are no other references |
879 | * to it. And as a side-effect, free up its swap: because the old content | 921 | * to it. And as a side-effect, free up its swap: because the old content |
880 | * on disk will never be read, and seeking back there to write new content | 922 | * on disk will never be read, and seeking back there to write new content |