diff options
-rw-r--r-- | arch/arm/include/asm/pgtable-2level.h | 7 | ||||
-rw-r--r-- | arch/arm/include/asm/pgtable-3level.h | 3 | ||||
-rw-r--r-- | arch/arm/lib/uaccess_with_memcpy.c | 41 |
3 files changed, 48 insertions, 3 deletions
diff --git a/arch/arm/include/asm/pgtable-2level.h b/arch/arm/include/asm/pgtable-2level.h index f97ee02386ee..86a659a19526 100644 --- a/arch/arm/include/asm/pgtable-2level.h +++ b/arch/arm/include/asm/pgtable-2level.h | |||
@@ -181,6 +181,13 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr) | |||
181 | 181 | ||
182 | #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext) | 182 | #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext) |
183 | 183 | ||
184 | /* | ||
185 | * We don't have huge page support for short descriptors, for the moment | ||
186 | * define empty stubs for use by pin_page_for_write. | ||
187 | */ | ||
188 | #define pmd_hugewillfault(pmd) (0) | ||
189 | #define pmd_thp_or_huge(pmd) (0) | ||
190 | |||
184 | #endif /* __ASSEMBLY__ */ | 191 | #endif /* __ASSEMBLY__ */ |
185 | 192 | ||
186 | #endif /* _ASM_PGTABLE_2LEVEL_H */ | 193 | #endif /* _ASM_PGTABLE_2LEVEL_H */ |
diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h index 5689c18c85f5..39c54cfa03e9 100644 --- a/arch/arm/include/asm/pgtable-3level.h +++ b/arch/arm/include/asm/pgtable-3level.h | |||
@@ -206,6 +206,9 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr) | |||
206 | #define __HAVE_ARCH_PMD_WRITE | 206 | #define __HAVE_ARCH_PMD_WRITE |
207 | #define pmd_write(pmd) (!(pmd_val(pmd) & PMD_SECT_RDONLY)) | 207 | #define pmd_write(pmd) (!(pmd_val(pmd) & PMD_SECT_RDONLY)) |
208 | 208 | ||
209 | #define pmd_hugewillfault(pmd) (!pmd_young(pmd) || !pmd_write(pmd)) | ||
210 | #define pmd_thp_or_huge(pmd) (pmd_huge(pmd) || pmd_trans_huge(pmd)) | ||
211 | |||
209 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 212 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
210 | #define pmd_trans_huge(pmd) (pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT)) | 213 | #define pmd_trans_huge(pmd) (pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT)) |
211 | #define pmd_trans_splitting(pmd) (pmd_val(pmd) & PMD_SECT_SPLITTING) | 214 | #define pmd_trans_splitting(pmd) (pmd_val(pmd) & PMD_SECT_SPLITTING) |
diff --git a/arch/arm/lib/uaccess_with_memcpy.c b/arch/arm/lib/uaccess_with_memcpy.c index 025f742dd4df..3e58d710013c 100644 --- a/arch/arm/lib/uaccess_with_memcpy.c +++ b/arch/arm/lib/uaccess_with_memcpy.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/hardirq.h> /* for in_atomic() */ | 18 | #include <linux/hardirq.h> /* for in_atomic() */ |
19 | #include <linux/gfp.h> | 19 | #include <linux/gfp.h> |
20 | #include <linux/highmem.h> | 20 | #include <linux/highmem.h> |
21 | #include <linux/hugetlb.h> | ||
21 | #include <asm/current.h> | 22 | #include <asm/current.h> |
22 | #include <asm/page.h> | 23 | #include <asm/page.h> |
23 | 24 | ||
@@ -40,7 +41,35 @@ pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp) | |||
40 | return 0; | 41 | return 0; |
41 | 42 | ||
42 | pmd = pmd_offset(pud, addr); | 43 | pmd = pmd_offset(pud, addr); |
43 | if (unlikely(pmd_none(*pmd) || pmd_bad(*pmd))) | 44 | if (unlikely(pmd_none(*pmd))) |
45 | return 0; | ||
46 | |||
47 | /* | ||
48 | * A pmd can be bad if it refers to a HugeTLB or THP page. | ||
49 | * | ||
50 | * Both THP and HugeTLB pages have the same pmd layout | ||
51 | * and should not be manipulated by the pte functions. | ||
52 | * | ||
53 | * Lock the page table for the destination and check | ||
54 | * to see that it's still huge and whether or not we will | ||
55 | * need to fault on write, or if we have a splitting THP. | ||
56 | */ | ||
57 | if (unlikely(pmd_thp_or_huge(*pmd))) { | ||
58 | ptl = ¤t->mm->page_table_lock; | ||
59 | spin_lock(ptl); | ||
60 | if (unlikely(!pmd_thp_or_huge(*pmd) | ||
61 | || pmd_hugewillfault(*pmd) | ||
62 | || pmd_trans_splitting(*pmd))) { | ||
63 | spin_unlock(ptl); | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | *ptep = NULL; | ||
68 | *ptlp = ptl; | ||
69 | return 1; | ||
70 | } | ||
71 | |||
72 | if (unlikely(pmd_bad(*pmd))) | ||
44 | return 0; | 73 | return 0; |
45 | 74 | ||
46 | pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl); | 75 | pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl); |
@@ -94,7 +123,10 @@ __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n) | |||
94 | from += tocopy; | 123 | from += tocopy; |
95 | n -= tocopy; | 124 | n -= tocopy; |
96 | 125 | ||
97 | pte_unmap_unlock(pte, ptl); | 126 | if (pte) |
127 | pte_unmap_unlock(pte, ptl); | ||
128 | else | ||
129 | spin_unlock(ptl); | ||
98 | } | 130 | } |
99 | if (!atomic) | 131 | if (!atomic) |
100 | up_read(¤t->mm->mmap_sem); | 132 | up_read(¤t->mm->mmap_sem); |
@@ -147,7 +179,10 @@ __clear_user_memset(void __user *addr, unsigned long n) | |||
147 | addr += tocopy; | 179 | addr += tocopy; |
148 | n -= tocopy; | 180 | n -= tocopy; |
149 | 181 | ||
150 | pte_unmap_unlock(pte, ptl); | 182 | if (pte) |
183 | pte_unmap_unlock(pte, ptl); | ||
184 | else | ||
185 | spin_unlock(ptl); | ||
151 | } | 186 | } |
152 | up_read(¤t->mm->mmap_sem); | 187 | up_read(¤t->mm->mmap_sem); |
153 | 188 | ||