summaryrefslogtreecommitdiffstats
path: root/mm/page_alloc.c
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>2016-01-15 19:53:42 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-01-15 20:56:32 -0500
commit53f9263baba69fc1630e3c780c4d11b72643f962 (patch)
tree7b8c3434d11c5cb6b58ea2463779d86cbff57d5e /mm/page_alloc.c
parent4b471e8898c3d0f5c97a3c73ac32d0549fe01c87 (diff)
mm: rework mapcount accounting to enable 4k mapping of THPs
We're going to allow mapping of individual 4k pages of THP compound. It means we need to track mapcount on per small page basis. Straight-forward approach is to use ->_mapcount in all subpages to track how many time this subpage is mapped with PMDs or PTEs combined. But this is rather expensive: mapping or unmapping of a THP page with PMD would require HPAGE_PMD_NR atomic operations instead of single we have now. The idea is to store separately how many times the page was mapped as whole -- compound_mapcount. This frees up ->_mapcount in subpages to track PTE mapcount. We use the same approach as with compound page destructor and compound order to store compound_mapcount: use space in first tail page, ->mapping this time. Any time we map/unmap whole compound page (THP or hugetlb) -- we increment/decrement compound_mapcount. When we map part of compound page with PTE we operate on ->_mapcount of the subpage. page_mapcount() counts both: PTE and PMD mappings of the page. Basically, we have mapcount for a subpage spread over two counters. It makes tricky to detect when last mapcount for a page goes away. We introduced PageDoubleMap() for this. When we split THP PMD for the first time and there's other PMD mapping left we offset up ->_mapcount in all subpages by one and set PG_double_map on the compound page. These additional references go away with last compound_mapcount. This approach provides a way to detect when last mapcount goes away on per small page basis without introducing new overhead for most common cases. [akpm@linux-foundation.org: fix typo in comment] [mhocko@suse.com: ignore partial THP when moving task] Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Tested-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Acked-by: Jerome Marchand <jmarchan@redhat.com> Cc: Sasha Levin <sasha.levin@oracle.com> Cc: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Cc: Jerome Marchand <jmarchan@redhat.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Hugh Dickins <hughd@google.com> Cc: Dave Hansen <dave.hansen@intel.com> Cc: Mel Gorman <mgorman@suse.de> Cc: Rik van Riel <riel@redhat.com> Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Cc: Steve Capper <steve.capper@linaro.org> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Christoph Lameter <cl@linux.com> Cc: David Rientjes <rientjes@google.com> Signed-off-by: Michal Hocko <mhocko@suse.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/page_alloc.c')
-rw-r--r--mm/page_alloc.c13
1 files changed, 10 insertions, 3 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index d02d6436add0..3221091da513 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -469,6 +469,7 @@ void prep_compound_page(struct page *page, unsigned int order)
469 p->mapping = TAIL_MAPPING; 469 p->mapping = TAIL_MAPPING;
470 set_compound_head(p, page); 470 set_compound_head(p, page);
471 } 471 }
472 atomic_set(compound_mapcount_ptr(page), -1);
472} 473}
473 474
474#ifdef CONFIG_DEBUG_PAGEALLOC 475#ifdef CONFIG_DEBUG_PAGEALLOC
@@ -733,7 +734,7 @@ static inline int free_pages_check(struct page *page)
733 const char *bad_reason = NULL; 734 const char *bad_reason = NULL;
734 unsigned long bad_flags = 0; 735 unsigned long bad_flags = 0;
735 736
736 if (unlikely(page_mapcount(page))) 737 if (unlikely(atomic_read(&page->_mapcount) != -1))
737 bad_reason = "nonzero mapcount"; 738 bad_reason = "nonzero mapcount";
738 if (unlikely(page->mapping != NULL)) 739 if (unlikely(page->mapping != NULL))
739 bad_reason = "non-NULL mapping"; 740 bad_reason = "non-NULL mapping";
@@ -857,7 +858,13 @@ static int free_tail_pages_check(struct page *head_page, struct page *page)
857 ret = 0; 858 ret = 0;
858 goto out; 859 goto out;
859 } 860 }
860 if (page->mapping != TAIL_MAPPING) { 861 /* mapping in first tail page is used for compound_mapcount() */
862 if (page - head_page == 1) {
863 if (unlikely(compound_mapcount(page))) {
864 bad_page(page, "nonzero compound_mapcount", 0);
865 goto out;
866 }
867 } else if (page->mapping != TAIL_MAPPING) {
861 bad_page(page, "corrupted mapping in tail page", 0); 868 bad_page(page, "corrupted mapping in tail page", 0);
862 goto out; 869 goto out;
863 } 870 }
@@ -1335,7 +1342,7 @@ static inline int check_new_page(struct page *page)
1335 const char *bad_reason = NULL; 1342 const char *bad_reason = NULL;
1336 unsigned long bad_flags = 0; 1343 unsigned long bad_flags = 0;
1337 1344
1338 if (unlikely(page_mapcount(page))) 1345 if (unlikely(atomic_read(&page->_mapcount) != -1))
1339 bad_reason = "nonzero mapcount"; 1346 bad_reason = "nonzero mapcount";
1340 if (unlikely(page->mapping != NULL)) 1347 if (unlikely(page->mapping != NULL))
1341 bad_reason = "non-NULL mapping"; 1348 bad_reason = "non-NULL mapping";