]> 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, 22 May 2014 00:42:38 +0000 (10:42 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 22 May 2014 00:42:38 +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 45598f1e9aa331225eee538ed3c5c54277c1f01a..f7c896cef20e3523c867d9e31497a69c1c437f97 100644 (file)
@@ -316,6 +316,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 b4b1feba64724234dee1b66a482c79a0cd3c0f95..ce90eaebf1af5aca842951082e764cf9db5b4cae 100644 (file)
@@ -1807,7 +1807,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 c82290b9c1fcdb1b1dfdffcad699d6eb83ee8a43..eedb6e4ef51c41d222ccc1f345a719105e137f1f 100644 (file)
@@ -765,6 +765,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 9ccef39a9de261c96f4e5775d7dca48b63d4d133..3715443f550dbf1c9df192b37ae44941649b2dbf 100644 (file)
@@ -404,7 +404,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;
@@ -437,7 +437,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);
 
                if (!task_early_kill(tsk))
                        continue;
index 9c3e77396d1a118edb8f2de7d16ff835ed214f0a..9916787c1843952526fb23400ec8813cfe8bb668 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -515,11 +515,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);
 }
 
@@ -1609,7 +1605,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;