aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Kravetz <mike.kravetz@oracle.com>2015-06-24 19:57:58 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-24 20:49:44 -0400
commit33039678c8da8133e30ea3250d10ae14701dae2b (patch)
treef4193b8b309d314fa2c09fca917efc3e685f0f74
parentcf3ad20bfeadda693e408d85684790714fc29b08 (diff)
mm/hugetlb: handle races in alloc_huge_page and hugetlb_reserve_pages
alloc_huge_page and hugetlb_reserve_pages use region_chg to calculate the number of pages which will be added to the reserve map. Subpool and global reserve counts are adjusted based on the output of region_chg. Before the pages are actually added to the reserve map, these routines could race and add fewer pages than expected. If this happens, the subpool and global reserve counts are not correct. Compare the number of pages actually added (region_add) to those expected to added (region_chg). If fewer pages are actually added, this indicates a race and adjust counters accordingly. Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com> Reviewed-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Reviewed-by: Davidlohr Bueso <dave@stgolabs.net> Cc: David Rientjes <rientjes@google.com> Cc: Luiz Capitulino <lcapitulino@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/hugetlb.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index cd3fc4194733..75c0eef52c5d 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1542,7 +1542,7 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
1542 struct hugepage_subpool *spool = subpool_vma(vma); 1542 struct hugepage_subpool *spool = subpool_vma(vma);
1543 struct hstate *h = hstate_vma(vma); 1543 struct hstate *h = hstate_vma(vma);
1544 struct page *page; 1544 struct page *page;
1545 long chg; 1545 long chg, commit;
1546 int ret, idx; 1546 int ret, idx;
1547 struct hugetlb_cgroup *h_cg; 1547 struct hugetlb_cgroup *h_cg;
1548 1548
@@ -1583,7 +1583,22 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
1583 1583
1584 set_page_private(page, (unsigned long)spool); 1584 set_page_private(page, (unsigned long)spool);
1585 1585
1586 vma_commit_reservation(h, vma, addr); 1586 commit = vma_commit_reservation(h, vma, addr);
1587 if (unlikely(chg > commit)) {
1588 /*
1589 * The page was added to the reservation map between
1590 * vma_needs_reservation and vma_commit_reservation.
1591 * This indicates a race with hugetlb_reserve_pages.
1592 * Adjust for the subpool count incremented above AND
1593 * in hugetlb_reserve_pages for the same page. Also,
1594 * the reservation count added in hugetlb_reserve_pages
1595 * no longer applies.
1596 */
1597 long rsv_adjust;
1598
1599 rsv_adjust = hugepage_subpool_put_pages(spool, 1);
1600 hugetlb_acct_memory(h, -rsv_adjust);
1601 }
1587 return page; 1602 return page;
1588 1603
1589out_uncharge_cgroup: 1604out_uncharge_cgroup:
@@ -3701,8 +3716,24 @@ int hugetlb_reserve_pages(struct inode *inode,
3701 * consumed reservations are stored in the map. Hence, nothing 3716 * consumed reservations are stored in the map. Hence, nothing
3702 * else has to be done for private mappings here 3717 * else has to be done for private mappings here
3703 */ 3718 */
3704 if (!vma || vma->vm_flags & VM_MAYSHARE) 3719 if (!vma || vma->vm_flags & VM_MAYSHARE) {
3705 region_add(resv_map, from, to); 3720 long add = region_add(resv_map, from, to);
3721
3722 if (unlikely(chg > add)) {
3723 /*
3724 * pages in this range were added to the reserve
3725 * map between region_chg and region_add. This
3726 * indicates a race with alloc_huge_page. Adjust
3727 * the subpool and reserve counts modified above
3728 * based on the difference.
3729 */
3730 long rsv_adjust;
3731
3732 rsv_adjust = hugepage_subpool_put_pages(spool,
3733 chg - add);
3734 hugetlb_acct_memory(h, -rsv_adjust);
3735 }
3736 }
3706 return 0; 3737 return 0;
3707out_err: 3738out_err:
3708 if (vma && is_vma_resv_set(vma, HPAGE_RESV_OWNER)) 3739 if (vma && is_vma_resv_set(vma, HPAGE_RESV_OWNER))