aboutsummaryrefslogtreecommitdiff
path: root/mm
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2014-02-24 22:05:00 -0500
committerSteven Rostedt <rostedt@goodmis.org>2014-02-24 22:05:00 -0500
commit592556d2b938d27adfc83ad55894d2974520e8ed (patch)
treeff28d2c0c55e22d68c5da058ca9b33b27b9fc22b /mm
parentf1fcf5146f16cce6268b096579affe33364a9135 (diff)
parent39716f2c8648f3d26934c44a79c14a4e6298a920 (diff)
Merge tag 'v3.2.55' into v3.2-rt
This is the 3.2.55 stable release Conflicts: mm/swap.c
Diffstat (limited to 'mm')
-rw-r--r--mm/hugetlb.c17
-rw-r--r--mm/memory-failure.c16
-rw-r--r--mm/mmap.c15
-rw-r--r--mm/swap.c84
4 files changed, 117 insertions, 15 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index ddf2128f93fa..3a5aae2d6ee7 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -679,6 +679,23 @@ int PageHuge(struct page *page)
}
EXPORT_SYMBOL_GPL(PageHuge);
+/*
+ * PageHeadHuge() only returns true for hugetlbfs head page, but not for
+ * normal or transparent huge pages.
+ */
+int PageHeadHuge(struct page *page_head)
+{
+ compound_page_dtor *dtor;
+
+ if (!PageHead(page_head))
+ return 0;
+
+ dtor = get_compound_page_dtor(page_head);
+
+ return dtor == free_huge_page;
+}
+EXPORT_SYMBOL_GPL(PageHeadHuge);
+
pgoff_t __basepage_index(struct page *page)
{
struct page *page_head = compound_head(page);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 1b038789a9d6..96c4bcf188b1 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1441,10 +1441,18 @@ static int soft_offline_huge_page(struct page *page, int flags)
return ret;
}
done:
- if (!PageHWPoison(hpage))
- atomic_long_add(1 << compound_trans_order(hpage), &mce_bad_pages);
- set_page_hwpoison_huge_page(hpage);
- dequeue_hwpoisoned_huge_page(hpage);
+ /* overcommit hugetlb page will be freed to buddy */
+ if (PageHuge(hpage)) {
+ if (!PageHWPoison(hpage))
+ atomic_long_add(1 << compound_trans_order(hpage),
+ &mce_bad_pages);
+ set_page_hwpoison_huge_page(hpage);
+ dequeue_hwpoisoned_huge_page(hpage);
+ } else {
+ SetPageHWPoison(page);
+ atomic_long_inc(&mce_bad_pages);
+ }
+
/* keep elevated page count for bad page */
return ret;
}
diff --git a/mm/mmap.c b/mm/mmap.c
index dff37a686230..6182c8a4ea38 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1368,7 +1368,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
struct vm_area_struct *vma;
unsigned long start_addr;
- if (len > TASK_SIZE)
+ if (len > TASK_SIZE - mmap_min_addr)
return -ENOMEM;
if (flags & MAP_FIXED)
@@ -1377,7 +1377,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
if (addr) {
addr = PAGE_ALIGN(addr);
vma = find_vma(mm, addr);
- if (TASK_SIZE - len >= addr &&
+ if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
(!vma || addr + len <= vma->vm_start))
return addr;
}
@@ -1442,9 +1442,10 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
struct vm_area_struct *vma;
struct mm_struct *mm = current->mm;
unsigned long addr = addr0;
+ unsigned long low_limit = max(PAGE_SIZE, mmap_min_addr);
/* requested length too big for entire address space */
- if (len > TASK_SIZE)
+ if (len > TASK_SIZE - mmap_min_addr)
return -ENOMEM;
if (flags & MAP_FIXED)
@@ -1454,7 +1455,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
if (addr) {
addr = PAGE_ALIGN(addr);
vma = find_vma(mm, addr);
- if (TASK_SIZE - len >= addr &&
+ if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
(!vma || addr + len <= vma->vm_start))
return addr;
}
@@ -1469,14 +1470,14 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
addr = mm->free_area_cache;
/* make sure it can fit in the remaining address space */
- if (addr > len) {
+ if (addr >= low_limit + len) {
vma = find_vma(mm, addr-len);
if (!vma || addr <= vma->vm_start)
/* remember the address as a hint for next time */
return (mm->free_area_cache = addr-len);
}
- if (mm->mmap_base < len)
+ if (mm->mmap_base < low_limit + len)
goto bottomup;
addr = mm->mmap_base-len;
@@ -1498,7 +1499,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
/* try just below the current vma->vm_start */
addr = vma->vm_start-len;
- } while (len < vma->vm_start);
+ } while (vma->vm_start >= low_limit + len);
bottomup:
/*
diff --git a/mm/swap.c b/mm/swap.c
index c77da7af29f9..66a38745054c 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -31,6 +31,7 @@
#include <linux/backing-dev.h>
#include <linux/memcontrol.h>
#include <linux/gfp.h>
+#include <linux/hugetlb.h>
#include <linux/locallock.h>
#include "internal.h"
@@ -73,7 +74,8 @@ static void __put_compound_page(struct page *page)
{
compound_page_dtor *dtor;
- __page_cache_release(page);
+ if (!PageHuge(page))
+ __page_cache_release(page);
dtor = get_compound_page_dtor(page);
(*dtor)(page);
}
@@ -87,6 +89,35 @@ static void put_compound_page(struct page *page)
if (likely(page != page_head &&
get_page_unless_zero(page_head))) {
unsigned long flags;
+
+ if (PageHeadHuge(page_head)) {
+ if (likely(PageTail(page))) {
+ /*
+ * __split_huge_page_refcount
+ * cannot race here.
+ */
+ VM_BUG_ON(!PageHead(page_head));
+ atomic_dec(&page->_mapcount);
+ if (put_page_testzero(page_head))
+ VM_BUG_ON(1);
+ if (put_page_testzero(page_head))
+ __put_compound_page(page_head);
+ return;
+ } else {
+ /*
+ * __split_huge_page_refcount
+ * run before us, "page" was a
+ * THP tail. The split
+ * page_head has been freed
+ * and reallocated as slab or
+ * hugetlbfs page of smaller
+ * order (only possible if
+ * reallocated as slab on
+ * x86).
+ */
+ goto skip_lock;
+ }
+ }
/*
* page_head wasn't a dangling pointer but it
* may not be a head page anymore by the time
@@ -98,9 +129,29 @@ static void put_compound_page(struct page *page)
/* __split_huge_page_refcount run before us */
compound_unlock_irqrestore(page_head, flags);
VM_BUG_ON(PageHead(page_head));
- if (put_page_testzero(page_head))
- __put_single_page(page_head);
- out_put_single:
+skip_lock:
+ if (put_page_testzero(page_head)) {
+ /*
+ * The head page may have been
+ * freed and reallocated as a
+ * compound page of smaller
+ * order and then freed again.
+ * All we know is that it
+ * cannot have become: a THP
+ * page, a compound page of
+ * higher order, a tail page.
+ * That is because we still
+ * hold the refcount of the
+ * split THP tail and
+ * page_head was the THP head
+ * before the split.
+ */
+ if (PageHead(page_head))
+ __put_compound_page(page_head);
+ else
+ __put_single_page(page_head);
+ }
+out_put_single:
if (put_page_testzero(page))
__put_single_page(page);
return;
@@ -167,6 +218,31 @@ bool __get_page_tail(struct page *page)
struct page *page_head = compound_trans_head(page);
if (likely(page != page_head && get_page_unless_zero(page_head))) {
+ /* Ref to put_compound_page() comment. */
+ if (PageHeadHuge(page_head)) {
+ if (likely(PageTail(page))) {
+ /*
+ * This is a hugetlbfs
+ * page. __split_huge_page_refcount
+ * cannot race here.
+ */
+ VM_BUG_ON(!PageHead(page_head));
+ __get_page_tail_foll(page, false);
+ return true;
+ } else {
+ /*
+ * __split_huge_page_refcount run
+ * before us, "page" was a THP
+ * tail. The split page_head has been
+ * freed and reallocated as slab or
+ * hugetlbfs page of smaller order
+ * (only possible if reallocated as
+ * slab on x86).
+ */
+ put_page(page_head);
+ return false;
+ }
+ }
/*
* page_head wasn't a dangling pointer but it
* may not be a head page anymore by the time