aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichel Lespinasse <walken@google.com>2013-02-22 19:35:56 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-23 20:50:23 -0500
commit240aadeedc4a89fc44623f8ce4ca46bda73db07e (patch)
treee12cc254f2e78560837e5d454fdbe9b5d9e05c5c
parent28a35716d317980ae9bc2ff2f84c33a3cda9e884 (diff)
mm: accelerate mm_populate() treatment of THP pages
This change adds a follow_page_mask function which is equivalent to follow_page, but with an extra page_mask argument. follow_page_mask sets *page_mask to HPAGE_PMD_NR - 1 when it encounters a THP page, and to 0 in other cases. __get_user_pages() makes use of this in order to accelerate populating THP ranges - that is, when both the pages and vmas arrays are NULL, we don't need to iterate HPAGE_PMD_NR times to cover a single THP page (and we also avoid taking mm->page_table_lock that many times). Signed-off-by: Michel Lespinasse <walken@google.com> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Rik van Riel <riel@redhat.com> Cc: Mel Gorman <mgorman@suse.de> Cc: Hugh Dickins <hughd@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/mm.h13
-rw-r--r--mm/memory.c31
-rw-r--r--mm/nommu.c6
3 files changed, 38 insertions, 12 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 87b0ef253607..6124f1db50fe 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1629,8 +1629,17 @@ int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr,
1629int vm_insert_mixed(struct vm_area_struct *vma, unsigned long addr, 1629int vm_insert_mixed(struct vm_area_struct *vma, unsigned long addr,
1630 unsigned long pfn); 1630 unsigned long pfn);
1631 1631
1632struct page *follow_page(struct vm_area_struct *, unsigned long address, 1632struct page *follow_page_mask(struct vm_area_struct *vma,
1633 unsigned int foll_flags); 1633 unsigned long address, unsigned int foll_flags,
1634 unsigned int *page_mask);
1635
1636static inline struct page *follow_page(struct vm_area_struct *vma,
1637 unsigned long address, unsigned int foll_flags)
1638{
1639 unsigned int unused_page_mask;
1640 return follow_page_mask(vma, address, foll_flags, &unused_page_mask);
1641}
1642
1634#define FOLL_WRITE 0x01 /* check pte is writable */ 1643#define FOLL_WRITE 0x01 /* check pte is writable */
1635#define FOLL_TOUCH 0x02 /* mark page accessed */ 1644#define FOLL_TOUCH 0x02 /* mark page accessed */
1636#define FOLL_GET 0x04 /* do get_page on page */ 1645#define FOLL_GET 0x04 /* do get_page on page */
diff --git a/mm/memory.c b/mm/memory.c
index bc929dbad215..5d2ef1217d0c 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1462,10 +1462,11 @@ int zap_vma_ptes(struct vm_area_struct *vma, unsigned long address,
1462EXPORT_SYMBOL_GPL(zap_vma_ptes); 1462EXPORT_SYMBOL_GPL(zap_vma_ptes);
1463 1463
1464/** 1464/**
1465 * follow_page - look up a page descriptor from a user-virtual address 1465 * follow_page_mask - look up a page descriptor from a user-virtual address
1466 * @vma: vm_area_struct mapping @address 1466 * @vma: vm_area_struct mapping @address
1467 * @address: virtual address to look up 1467 * @address: virtual address to look up
1468 * @flags: flags modifying lookup behaviour 1468 * @flags: flags modifying lookup behaviour
1469 * @page_mask: on output, *page_mask is set according to the size of the page
1469 * 1470 *
1470 * @flags can have FOLL_ flags set, defined in <linux/mm.h> 1471 * @flags can have FOLL_ flags set, defined in <linux/mm.h>
1471 * 1472 *
@@ -1473,8 +1474,9 @@ EXPORT_SYMBOL_GPL(zap_vma_ptes);
1473 * an error pointer if there is a mapping to something not represented 1474 * an error pointer if there is a mapping to something not represented
1474 * by a page descriptor (see also vm_normal_page()). 1475 * by a page descriptor (see also vm_normal_page()).
1475 */ 1476 */
1476struct page *follow_page(struct vm_area_struct *vma, unsigned long address, 1477struct page *follow_page_mask(struct vm_area_struct *vma,
1477 unsigned int flags) 1478 unsigned long address, unsigned int flags,
1479 unsigned int *page_mask)
1478{ 1480{
1479 pgd_t *pgd; 1481 pgd_t *pgd;
1480 pud_t *pud; 1482 pud_t *pud;
@@ -1484,6 +1486,8 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address,
1484 struct page *page; 1486 struct page *page;
1485 struct mm_struct *mm = vma->vm_mm; 1487 struct mm_struct *mm = vma->vm_mm;
1486 1488
1489 *page_mask = 0;
1490
1487 page = follow_huge_addr(mm, address, flags & FOLL_WRITE); 1491 page = follow_huge_addr(mm, address, flags & FOLL_WRITE);
1488 if (!IS_ERR(page)) { 1492 if (!IS_ERR(page)) {
1489 BUG_ON(flags & FOLL_GET); 1493 BUG_ON(flags & FOLL_GET);
@@ -1530,6 +1534,7 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address,
1530 page = follow_trans_huge_pmd(vma, address, 1534 page = follow_trans_huge_pmd(vma, address,
1531 pmd, flags); 1535 pmd, flags);
1532 spin_unlock(&mm->page_table_lock); 1536 spin_unlock(&mm->page_table_lock);
1537 *page_mask = HPAGE_PMD_NR - 1;
1533 goto out; 1538 goto out;
1534 } 1539 }
1535 } else 1540 } else
@@ -1684,6 +1689,7 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
1684{ 1689{
1685 long i; 1690 long i;
1686 unsigned long vm_flags; 1691 unsigned long vm_flags;
1692 unsigned int page_mask;
1687 1693
1688 if (!nr_pages) 1694 if (!nr_pages)
1689 return 0; 1695 return 0;
@@ -1761,6 +1767,7 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
1761 get_page(page); 1767 get_page(page);
1762 } 1768 }
1763 pte_unmap(pte); 1769 pte_unmap(pte);
1770 page_mask = 0;
1764 goto next_page; 1771 goto next_page;
1765 } 1772 }
1766 1773
@@ -1778,6 +1785,7 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
1778 do { 1785 do {
1779 struct page *page; 1786 struct page *page;
1780 unsigned int foll_flags = gup_flags; 1787 unsigned int foll_flags = gup_flags;
1788 unsigned int page_increm;
1781 1789
1782 /* 1790 /*
1783 * If we have a pending SIGKILL, don't keep faulting 1791 * If we have a pending SIGKILL, don't keep faulting
@@ -1787,7 +1795,8 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
1787 return i ? i : -ERESTARTSYS; 1795 return i ? i : -ERESTARTSYS;
1788 1796
1789 cond_resched(); 1797 cond_resched();
1790 while (!(page = follow_page(vma, start, foll_flags))) { 1798 while (!(page = follow_page_mask(vma, start,
1799 foll_flags, &page_mask))) {
1791 int ret; 1800 int ret;
1792 unsigned int fault_flags = 0; 1801 unsigned int fault_flags = 0;
1793 1802
@@ -1861,13 +1870,19 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
1861 1870
1862 flush_anon_page(vma, page, start); 1871 flush_anon_page(vma, page, start);
1863 flush_dcache_page(page); 1872 flush_dcache_page(page);
1873 page_mask = 0;
1864 } 1874 }
1865next_page: 1875next_page:
1866 if (vmas) 1876 if (vmas) {
1867 vmas[i] = vma; 1877 vmas[i] = vma;
1868 i++; 1878 page_mask = 0;
1869 start += PAGE_SIZE; 1879 }
1870 nr_pages--; 1880 page_increm = 1 + (~(start >> PAGE_SHIFT) & page_mask);
1881 if (page_increm > nr_pages)
1882 page_increm = nr_pages;
1883 i += page_increm;
1884 start += page_increm * PAGE_SIZE;
1885 nr_pages -= page_increm;
1871 } while (nr_pages && start < vma->vm_end); 1886 } while (nr_pages && start < vma->vm_end);
1872 } while (nr_pages); 1887 } while (nr_pages);
1873 return i; 1888 return i;
diff --git a/mm/nommu.c b/mm/nommu.c
index 6ab706608492..da0d210fd403 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1819,9 +1819,11 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
1819 return ret; 1819 return ret;
1820} 1820}
1821 1821
1822struct page *follow_page(struct vm_area_struct *vma, unsigned long address, 1822struct page *follow_page_mask(struct vm_area_struct *vma,
1823 unsigned int foll_flags) 1823 unsigned long address, unsigned int flags,
1824 unsigned int *page_mask)
1824{ 1825{
1826 *page_mask = 0;
1825 return NULL; 1827 return NULL;
1826} 1828}
1827 1829