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 | ||
