summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNaoya Horiguchi <n-horiguchi@ah.jp.nec.com>2014-04-16 10:54:44 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2014-04-17 09:52:35 +1000
commit7dfb01f768901c2fed8848aea83538db179b31dc (patch)
treec1ec59b54b73db04b36c17989593c579a9fbbb3b
parent0cc58d7c5d985900f7a054954ff97f8d3bfc424e (diff)
mm, hugetlbfs: fix rmapping for anonymous hugepages with page_pgoff()
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>
-rw-r--r--include/linux/pagemap.h13
-rw-r--r--mm/huge_memory.c2
-rw-r--r--mm/hugetlb.c5
-rw-r--r--mm/memory-failure.c4
-rw-r--r--mm/rmap.c8
5 files changed, 23 insertions, 9 deletions
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 45598f1e9aa3..f7c896cef20e 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -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);
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 64635f5278ff..7577c40f2ad7 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1800,7 +1800,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));
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 246192929a2d..fe1e67cef1b6 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -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;
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 35ef28acf137..5d85a4afb22c 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -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;
diff --git a/mm/rmap.c b/mm/rmap.c
index 9c3e77396d1a..9916787c1843 100644
--- 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;