]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
mm, hugetlbfs: fix rmapping for anonymous hugepages with page_pgoff()
authorNaoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Thu, 26 Jun 2014 00:42:34 +0000 (10:42 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 26 Jun 2014 00:42:34 +0000 (10:42 +1000)
page->index stores pagecache index when the page is mapped into file
mapping region, and the index is in pagecache size unit, so it depends on
the page size.  Some of users of reverse mapping obviously assumes that
page->index is in PAGE_CACHE_SHIFT unit, so they don't work for anonymous
hugepage.

For example, consider that we have 3-hugepage vma and try to mbind the 2nd
hugepage to migrate to another node.  Then the vma is split and
migrate_page() is called for the 2nd hugepage (belonging to the middle
vma.) In migrate operation, rmap_walk_anon() tries to find the relevant
vma to which the target hugepage belongs, but here we miscalculate pgoff.
So anon_vma_interval_tree_foreach() grabs invalid vma, which fires
VM_BUG_ON.

This patch introduces a new API that is usable both for normal page and
hugepage to get PAGE_SIZE offset from page->index.  Users should clearly
distinguish page_index for pagecache index and page_pgoff for page offset.

Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Reported-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: <stable@vger.kernel.org> [3.12+]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/pagemap.h
mm/huge_memory.c
mm/hugetlb.c
mm/memory-failure.c
mm/rmap.c

index 0a97b583ee8d12ae696cd3d33a13ee17c62b518f..4eb3b99a9440bb6e95bd232c6511ddb43f2c9a7b 100644 (file)
@@ -411,6 +411,19 @@ static inline loff_t page_file_offset(struct page *page)
        return ((loff_t)page_file_index(page)) << PAGE_CACHE_SHIFT;
 }
 
+extern pgoff_t hugepage_pgoff(struct page *page);
+
+/*
+ * page->index stores pagecache index whose unit is not always PAGE_SIZE.
+ * This function converts it into PAGE_SIZE offset.
+ */
+#define page_pgoff(page)                                       \
+({                                                             \
+       unlikely(PageHuge(page)) ?                              \
+               hugepage_pgoff(page) :                          \
+               page->index >> (PAGE_CACHE_SHIFT - PAGE_SHIFT); \
+})
+
 extern pgoff_t linear_hugepage_index(struct vm_area_struct *vma,
                                     unsigned long address);
 
index 33514d88fef9b041cef11c74717091eec4805f80..c491854d9b3d51545b705a9cff308ef3a895defd 100644 (file)
@@ -1838,7 +1838,7 @@ static void __split_huge_page(struct page *page,
                              struct list_head *list)
 {
        int mapcount, mapcount2;
-       pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
+       pgoff_t pgoff = page_pgoff(page);
        struct anon_vma_chain *avc;
 
        BUG_ON(!PageHead(page));
index 2024bbd573d2a9ca8a08842cdf0b99d2062cbee1..63dd071296a1c4d1a547c5e48033115c7fae1a05 100644 (file)
@@ -981,6 +981,11 @@ pgoff_t __basepage_index(struct page *page)
        return (index << compound_order(page_head)) + compound_idx;
 }
 
+pgoff_t hugepage_pgoff(struct page *page)
+{
+       return page->index << huge_page_order(page_hstate(page));
+}
+
 static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid)
 {
        struct page *page;
index c6399e32893178b835457388371e8e4f85512361..d148b54f82a23ade323b0ad7b8ced179b5d2df1b 100644 (file)
@@ -435,7 +435,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
        if (av == NULL) /* Not actually mapped anymore */
                return;
 
-       pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
+       pgoff = page_pgoff(page);
        read_lock(&tasklist_lock);
        for_each_process (tsk) {
                struct anon_vma_chain *vmac;
@@ -469,7 +469,7 @@ static void collect_procs_file(struct page *page, struct list_head *to_kill,
        mutex_lock(&mapping->i_mmap_mutex);
        read_lock(&tasklist_lock);
        for_each_process(tsk) {
-               pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
+               pgoff_t pgoff = page_pgoff(page);
                struct task_struct *t = task_early_kill(tsk, force_early);
 
                if (!t)
index b7e94ebbd09e88c3b356e36fe89ed72b89e14474..672ee4cebe809aa873c1dabdfaa03cfcc2f11c78 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -517,11 +517,7 @@ void page_unlock_anon_vma_read(struct anon_vma *anon_vma)
 static inline unsigned long
 __vma_address(struct page *page, struct vm_area_struct *vma)
 {
-       pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
-
-       if (unlikely(is_vm_hugetlb_page(vma)))
-               pgoff = page->index << huge_page_order(page_hstate(page));
-
+       pgoff_t pgoff = page_pgoff(page);
        return vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
 }
 
@@ -1639,7 +1635,7 @@ static struct anon_vma *rmap_walk_anon_lock(struct page *page,
 static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc)
 {
        struct anon_vma *anon_vma;
-       pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
+       pgoff_t pgoff = page_pgoff(page);
        struct anon_vma_chain *avc;
        int ret = SWAP_AGAIN;