diff options
author | Michel Lespinasse <walken@google.com> | 2013-02-22 19:35:56 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-23 20:50:23 -0500 |
commit | 240aadeedc4a89fc44623f8ce4ca46bda73db07e (patch) | |
tree | e12cc254f2e78560837e5d454fdbe9b5d9e05c5c | |
parent | 28a35716d317980ae9bc2ff2f84c33a3cda9e884 (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.h | 13 | ||||
-rw-r--r-- | mm/memory.c | 31 | ||||
-rw-r--r-- | mm/nommu.c | 6 |
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, | |||
1629 | int vm_insert_mixed(struct vm_area_struct *vma, unsigned long addr, | 1629 | int vm_insert_mixed(struct vm_area_struct *vma, unsigned long addr, |
1630 | unsigned long pfn); | 1630 | unsigned long pfn); |
1631 | 1631 | ||
1632 | struct page *follow_page(struct vm_area_struct *, unsigned long address, | 1632 | struct 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 | |||
1636 | static 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, | |||
1462 | EXPORT_SYMBOL_GPL(zap_vma_ptes); | 1462 | EXPORT_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 | */ |
1476 | struct page *follow_page(struct vm_area_struct *vma, unsigned long address, | 1477 | struct 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 | } |
1865 | next_page: | 1875 | next_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 | ||
1822 | struct page *follow_page(struct vm_area_struct *vma, unsigned long address, | 1822 | struct 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 | ||