diff options
Diffstat (limited to 'mm/hugetlb.c')
-rw-r--r-- | mm/hugetlb.c | 72 |
1 files changed, 50 insertions, 22 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 67a71191136e..421aee99b84a 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <linux/init.h> | 7 | #include <linux/init.h> |
8 | #include <linux/module.h> | 8 | #include <linux/module.h> |
9 | #include <linux/mm.h> | 9 | #include <linux/mm.h> |
10 | #include <linux/seq_file.h> | ||
10 | #include <linux/sysctl.h> | 11 | #include <linux/sysctl.h> |
11 | #include <linux/highmem.h> | 12 | #include <linux/highmem.h> |
12 | #include <linux/mmu_notifier.h> | 13 | #include <linux/mmu_notifier.h> |
@@ -262,7 +263,7 @@ struct resv_map { | |||
262 | struct list_head regions; | 263 | struct list_head regions; |
263 | }; | 264 | }; |
264 | 265 | ||
265 | struct resv_map *resv_map_alloc(void) | 266 | static struct resv_map *resv_map_alloc(void) |
266 | { | 267 | { |
267 | struct resv_map *resv_map = kmalloc(sizeof(*resv_map), GFP_KERNEL); | 268 | struct resv_map *resv_map = kmalloc(sizeof(*resv_map), GFP_KERNEL); |
268 | if (!resv_map) | 269 | if (!resv_map) |
@@ -274,7 +275,7 @@ struct resv_map *resv_map_alloc(void) | |||
274 | return resv_map; | 275 | return resv_map; |
275 | } | 276 | } |
276 | 277 | ||
277 | void resv_map_release(struct kref *ref) | 278 | static void resv_map_release(struct kref *ref) |
278 | { | 279 | { |
279 | struct resv_map *resv_map = container_of(ref, struct resv_map, refs); | 280 | struct resv_map *resv_map = container_of(ref, struct resv_map, refs); |
280 | 281 | ||
@@ -289,7 +290,7 @@ static struct resv_map *vma_resv_map(struct vm_area_struct *vma) | |||
289 | if (!(vma->vm_flags & VM_SHARED)) | 290 | if (!(vma->vm_flags & VM_SHARED)) |
290 | return (struct resv_map *)(get_vma_private_data(vma) & | 291 | return (struct resv_map *)(get_vma_private_data(vma) & |
291 | ~HPAGE_RESV_MASK); | 292 | ~HPAGE_RESV_MASK); |
292 | return 0; | 293 | return NULL; |
293 | } | 294 | } |
294 | 295 | ||
295 | static void set_vma_resv_map(struct vm_area_struct *vma, struct resv_map *map) | 296 | static void set_vma_resv_map(struct vm_area_struct *vma, struct resv_map *map) |
@@ -1455,15 +1456,15 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write, | |||
1455 | 1456 | ||
1456 | #endif /* CONFIG_SYSCTL */ | 1457 | #endif /* CONFIG_SYSCTL */ |
1457 | 1458 | ||
1458 | int hugetlb_report_meminfo(char *buf) | 1459 | void hugetlb_report_meminfo(struct seq_file *m) |
1459 | { | 1460 | { |
1460 | struct hstate *h = &default_hstate; | 1461 | struct hstate *h = &default_hstate; |
1461 | return sprintf(buf, | 1462 | seq_printf(m, |
1462 | "HugePages_Total: %5lu\n" | 1463 | "HugePages_Total: %5lu\n" |
1463 | "HugePages_Free: %5lu\n" | 1464 | "HugePages_Free: %5lu\n" |
1464 | "HugePages_Rsvd: %5lu\n" | 1465 | "HugePages_Rsvd: %5lu\n" |
1465 | "HugePages_Surp: %5lu\n" | 1466 | "HugePages_Surp: %5lu\n" |
1466 | "Hugepagesize: %5lu kB\n", | 1467 | "Hugepagesize: %8lu kB\n", |
1467 | h->nr_huge_pages, | 1468 | h->nr_huge_pages, |
1468 | h->free_huge_pages, | 1469 | h->free_huge_pages, |
1469 | h->resv_huge_pages, | 1470 | h->resv_huge_pages, |
@@ -1747,10 +1748,8 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, | |||
1747 | * from other VMAs and let the children be SIGKILLed if they are faulting the | 1748 | * from other VMAs and let the children be SIGKILLed if they are faulting the |
1748 | * same region. | 1749 | * same region. |
1749 | */ | 1750 | */ |
1750 | int unmap_ref_private(struct mm_struct *mm, | 1751 | static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, |
1751 | struct vm_area_struct *vma, | 1752 | struct page *page, unsigned long address) |
1752 | struct page *page, | ||
1753 | unsigned long address) | ||
1754 | { | 1753 | { |
1755 | struct vm_area_struct *iter_vma; | 1754 | struct vm_area_struct *iter_vma; |
1756 | struct address_space *mapping; | 1755 | struct address_space *mapping; |
@@ -2008,7 +2007,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2008 | entry = huge_ptep_get(ptep); | 2007 | entry = huge_ptep_get(ptep); |
2009 | if (huge_pte_none(entry)) { | 2008 | if (huge_pte_none(entry)) { |
2010 | ret = hugetlb_no_page(mm, vma, address, ptep, write_access); | 2009 | ret = hugetlb_no_page(mm, vma, address, ptep, write_access); |
2011 | goto out_unlock; | 2010 | goto out_mutex; |
2012 | } | 2011 | } |
2013 | 2012 | ||
2014 | ret = 0; | 2013 | ret = 0; |
@@ -2024,7 +2023,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2024 | if (write_access && !pte_write(entry)) { | 2023 | if (write_access && !pte_write(entry)) { |
2025 | if (vma_needs_reservation(h, vma, address) < 0) { | 2024 | if (vma_needs_reservation(h, vma, address) < 0) { |
2026 | ret = VM_FAULT_OOM; | 2025 | ret = VM_FAULT_OOM; |
2027 | goto out_unlock; | 2026 | goto out_mutex; |
2028 | } | 2027 | } |
2029 | 2028 | ||
2030 | if (!(vma->vm_flags & VM_SHARED)) | 2029 | if (!(vma->vm_flags & VM_SHARED)) |
@@ -2034,10 +2033,23 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2034 | 2033 | ||
2035 | spin_lock(&mm->page_table_lock); | 2034 | spin_lock(&mm->page_table_lock); |
2036 | /* Check for a racing update before calling hugetlb_cow */ | 2035 | /* Check for a racing update before calling hugetlb_cow */ |
2037 | if (likely(pte_same(entry, huge_ptep_get(ptep)))) | 2036 | if (unlikely(!pte_same(entry, huge_ptep_get(ptep)))) |
2038 | if (write_access && !pte_write(entry)) | 2037 | goto out_page_table_lock; |
2038 | |||
2039 | |||
2040 | if (write_access) { | ||
2041 | if (!pte_write(entry)) { | ||
2039 | ret = hugetlb_cow(mm, vma, address, ptep, entry, | 2042 | ret = hugetlb_cow(mm, vma, address, ptep, entry, |
2040 | pagecache_page); | 2043 | pagecache_page); |
2044 | goto out_page_table_lock; | ||
2045 | } | ||
2046 | entry = pte_mkdirty(entry); | ||
2047 | } | ||
2048 | entry = pte_mkyoung(entry); | ||
2049 | if (huge_ptep_set_access_flags(vma, address, ptep, entry, write_access)) | ||
2050 | update_mmu_cache(vma, address, entry); | ||
2051 | |||
2052 | out_page_table_lock: | ||
2041 | spin_unlock(&mm->page_table_lock); | 2053 | spin_unlock(&mm->page_table_lock); |
2042 | 2054 | ||
2043 | if (pagecache_page) { | 2055 | if (pagecache_page) { |
@@ -2045,7 +2057,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2045 | put_page(pagecache_page); | 2057 | put_page(pagecache_page); |
2046 | } | 2058 | } |
2047 | 2059 | ||
2048 | out_unlock: | 2060 | out_mutex: |
2049 | mutex_unlock(&hugetlb_instantiation_mutex); | 2061 | mutex_unlock(&hugetlb_instantiation_mutex); |
2050 | 2062 | ||
2051 | return ret; | 2063 | return ret; |
@@ -2060,6 +2072,14 @@ follow_huge_pud(struct mm_struct *mm, unsigned long address, | |||
2060 | return NULL; | 2072 | return NULL; |
2061 | } | 2073 | } |
2062 | 2074 | ||
2075 | static int huge_zeropage_ok(pte_t *ptep, int write, int shared) | ||
2076 | { | ||
2077 | if (!ptep || write || shared) | ||
2078 | return 0; | ||
2079 | else | ||
2080 | return huge_pte_none(huge_ptep_get(ptep)); | ||
2081 | } | ||
2082 | |||
2063 | int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, | 2083 | int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, |
2064 | struct page **pages, struct vm_area_struct **vmas, | 2084 | struct page **pages, struct vm_area_struct **vmas, |
2065 | unsigned long *position, int *length, int i, | 2085 | unsigned long *position, int *length, int i, |
@@ -2069,6 +2089,8 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2069 | unsigned long vaddr = *position; | 2089 | unsigned long vaddr = *position; |
2070 | int remainder = *length; | 2090 | int remainder = *length; |
2071 | struct hstate *h = hstate_vma(vma); | 2091 | struct hstate *h = hstate_vma(vma); |
2092 | int zeropage_ok = 0; | ||
2093 | int shared = vma->vm_flags & VM_SHARED; | ||
2072 | 2094 | ||
2073 | spin_lock(&mm->page_table_lock); | 2095 | spin_lock(&mm->page_table_lock); |
2074 | while (vaddr < vma->vm_end && remainder) { | 2096 | while (vaddr < vma->vm_end && remainder) { |
@@ -2081,8 +2103,11 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2081 | * first, for the page indexing below to work. | 2103 | * first, for the page indexing below to work. |
2082 | */ | 2104 | */ |
2083 | pte = huge_pte_offset(mm, vaddr & huge_page_mask(h)); | 2105 | pte = huge_pte_offset(mm, vaddr & huge_page_mask(h)); |
2106 | if (huge_zeropage_ok(pte, write, shared)) | ||
2107 | zeropage_ok = 1; | ||
2084 | 2108 | ||
2085 | if (!pte || huge_pte_none(huge_ptep_get(pte)) || | 2109 | if (!pte || |
2110 | (huge_pte_none(huge_ptep_get(pte)) && !zeropage_ok) || | ||
2086 | (write && !pte_write(huge_ptep_get(pte)))) { | 2111 | (write && !pte_write(huge_ptep_get(pte)))) { |
2087 | int ret; | 2112 | int ret; |
2088 | 2113 | ||
@@ -2102,8 +2127,11 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2102 | page = pte_page(huge_ptep_get(pte)); | 2127 | page = pte_page(huge_ptep_get(pte)); |
2103 | same_page: | 2128 | same_page: |
2104 | if (pages) { | 2129 | if (pages) { |
2105 | get_page(page); | 2130 | if (zeropage_ok) |
2106 | pages[i] = page + pfn_offset; | 2131 | pages[i] = ZERO_PAGE(0); |
2132 | else | ||
2133 | pages[i] = page + pfn_offset; | ||
2134 | get_page(pages[i]); | ||
2107 | } | 2135 | } |
2108 | 2136 | ||
2109 | if (vmas) | 2137 | if (vmas) |