diff options
author | Steven Rostedt (Red Hat) <rostedt@goodmis.org> | 2014-02-24 22:05:00 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2014-02-24 22:05:00 -0500 |
commit | 592556d2b938d27adfc83ad55894d2974520e8ed (patch) | |
tree | ff28d2c0c55e22d68c5da058ca9b33b27b9fc22b /mm | |
parent | f1fcf5146f16cce6268b096579affe33364a9135 (diff) | |
parent | 39716f2c8648f3d26934c44a79c14a4e6298a920 (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.c | 17 | ||||
-rw-r--r-- | mm/memory-failure.c | 16 | ||||
-rw-r--r-- | mm/mmap.c | 15 | ||||
-rw-r--r-- | mm/swap.c | 84 |
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 |