diff options
-rw-r--r-- | fs/proc/task_mmu.c | 47 |
1 files changed, 31 insertions, 16 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index a353b4c6e86e..b74e7dec37dd 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -466,9 +466,10 @@ struct mem_size_stats { | |||
466 | }; | 466 | }; |
467 | 467 | ||
468 | static void smaps_account(struct mem_size_stats *mss, struct page *page, | 468 | static void smaps_account(struct mem_size_stats *mss, struct page *page, |
469 | unsigned long size, bool young, bool dirty) | 469 | bool compound, bool young, bool dirty) |
470 | { | 470 | { |
471 | int mapcount; | 471 | int i, nr = compound ? HPAGE_PMD_NR : 1; |
472 | unsigned long size = nr * PAGE_SIZE; | ||
472 | 473 | ||
473 | if (PageAnon(page)) | 474 | if (PageAnon(page)) |
474 | mss->anonymous += size; | 475 | mss->anonymous += size; |
@@ -477,23 +478,37 @@ static void smaps_account(struct mem_size_stats *mss, struct page *page, | |||
477 | /* Accumulate the size in pages that have been accessed. */ | 478 | /* Accumulate the size in pages that have been accessed. */ |
478 | if (young || page_is_young(page) || PageReferenced(page)) | 479 | if (young || page_is_young(page) || PageReferenced(page)) |
479 | mss->referenced += size; | 480 | mss->referenced += size; |
480 | mapcount = page_mapcount(page); | ||
481 | if (mapcount >= 2) { | ||
482 | u64 pss_delta; | ||
483 | 481 | ||
484 | if (dirty || PageDirty(page)) | 482 | /* |
485 | mss->shared_dirty += size; | 483 | * page_count(page) == 1 guarantees the page is mapped exactly once. |
486 | else | 484 | * If any subpage of the compound page mapped with PTE it would elevate |
487 | mss->shared_clean += size; | 485 | * page_count(). |
488 | pss_delta = (u64)size << PSS_SHIFT; | 486 | */ |
489 | do_div(pss_delta, mapcount); | 487 | if (page_count(page) == 1) { |
490 | mss->pss += pss_delta; | ||
491 | } else { | ||
492 | if (dirty || PageDirty(page)) | 488 | if (dirty || PageDirty(page)) |
493 | mss->private_dirty += size; | 489 | mss->private_dirty += size; |
494 | else | 490 | else |
495 | mss->private_clean += size; | 491 | mss->private_clean += size; |
496 | mss->pss += (u64)size << PSS_SHIFT; | 492 | mss->pss += (u64)size << PSS_SHIFT; |
493 | return; | ||
494 | } | ||
495 | |||
496 | for (i = 0; i < nr; i++, page++) { | ||
497 | int mapcount = page_mapcount(page); | ||
498 | |||
499 | if (mapcount >= 2) { | ||
500 | if (dirty || PageDirty(page)) | ||
501 | mss->shared_dirty += PAGE_SIZE; | ||
502 | else | ||
503 | mss->shared_clean += PAGE_SIZE; | ||
504 | mss->pss += (PAGE_SIZE << PSS_SHIFT) / mapcount; | ||
505 | } else { | ||
506 | if (dirty || PageDirty(page)) | ||
507 | mss->private_dirty += PAGE_SIZE; | ||
508 | else | ||
509 | mss->private_clean += PAGE_SIZE; | ||
510 | mss->pss += PAGE_SIZE << PSS_SHIFT; | ||
511 | } | ||
497 | } | 512 | } |
498 | } | 513 | } |
499 | 514 | ||
@@ -554,7 +569,8 @@ static void smaps_pte_entry(pte_t *pte, unsigned long addr, | |||
554 | 569 | ||
555 | if (!page) | 570 | if (!page) |
556 | return; | 571 | return; |
557 | smaps_account(mss, page, PAGE_SIZE, pte_young(*pte), pte_dirty(*pte)); | 572 | |
573 | smaps_account(mss, page, false, pte_young(*pte), pte_dirty(*pte)); | ||
558 | } | 574 | } |
559 | 575 | ||
560 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 576 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
@@ -570,8 +586,7 @@ static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr, | |||
570 | if (IS_ERR_OR_NULL(page)) | 586 | if (IS_ERR_OR_NULL(page)) |
571 | return; | 587 | return; |
572 | mss->anonymous_thp += HPAGE_PMD_SIZE; | 588 | mss->anonymous_thp += HPAGE_PMD_SIZE; |
573 | smaps_account(mss, page, HPAGE_PMD_SIZE, | 589 | smaps_account(mss, page, true, pmd_young(*pmd), pmd_dirty(*pmd)); |
574 | pmd_young(*pmd), pmd_dirty(*pmd)); | ||
575 | } | 590 | } |
576 | #else | 591 | #else |
577 | static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr, | 592 | static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr, |