diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/s390/include/asm/pgtable.h | 13 | ||||
-rw-r--r-- | arch/s390/mm/gup.c | 11 | ||||
-rw-r--r-- | arch/s390/mm/pgtable.c | 18 |
3 files changed, 41 insertions, 1 deletions
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 6bd7d7483017..c3f0775e71f1 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
@@ -347,6 +347,8 @@ extern struct page *vmemmap; | |||
347 | 347 | ||
348 | #define _SEGMENT_ENTRY_LARGE 0x400 /* STE-format control, large page */ | 348 | #define _SEGMENT_ENTRY_LARGE 0x400 /* STE-format control, large page */ |
349 | #define _SEGMENT_ENTRY_CO 0x100 /* change-recording override */ | 349 | #define _SEGMENT_ENTRY_CO 0x100 /* change-recording override */ |
350 | #define _SEGMENT_ENTRY_SPLIT_BIT 0 /* THP splitting bit number */ | ||
351 | #define _SEGMENT_ENTRY_SPLIT (1UL << _SEGMENT_ENTRY_SPLIT_BIT) | ||
350 | 352 | ||
351 | /* Page status table bits for virtualization */ | 353 | /* Page status table bits for virtualization */ |
352 | #define RCP_ACC_BITS 0xf000000000000000UL | 354 | #define RCP_ACC_BITS 0xf000000000000000UL |
@@ -506,6 +508,10 @@ static inline int pmd_bad(pmd_t pmd) | |||
506 | return (pmd_val(pmd) & mask) != _SEGMENT_ENTRY; | 508 | return (pmd_val(pmd) & mask) != _SEGMENT_ENTRY; |
507 | } | 509 | } |
508 | 510 | ||
511 | #define __HAVE_ARCH_PMDP_SPLITTING_FLUSH | ||
512 | extern void pmdp_splitting_flush(struct vm_area_struct *vma, | ||
513 | unsigned long addr, pmd_t *pmdp); | ||
514 | |||
509 | static inline int pte_none(pte_t pte) | 515 | static inline int pte_none(pte_t pte) |
510 | { | 516 | { |
511 | return (pte_val(pte) & _PAGE_INVALID) && !(pte_val(pte) & _PAGE_SWT); | 517 | return (pte_val(pte) & _PAGE_INVALID) && !(pte_val(pte) & _PAGE_SWT); |
@@ -1159,6 +1165,13 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address) | |||
1159 | #define pte_offset_map(pmd, address) pte_offset_kernel(pmd, address) | 1165 | #define pte_offset_map(pmd, address) pte_offset_kernel(pmd, address) |
1160 | #define pte_unmap(pte) do { } while (0) | 1166 | #define pte_unmap(pte) do { } while (0) |
1161 | 1167 | ||
1168 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
1169 | static inline int pmd_trans_splitting(pmd_t pmd) | ||
1170 | { | ||
1171 | return pmd_val(pmd) & _SEGMENT_ENTRY_SPLIT; | ||
1172 | } | ||
1173 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ | ||
1174 | |||
1162 | /* | 1175 | /* |
1163 | * 31 bit swap entry format: | 1176 | * 31 bit swap entry format: |
1164 | * A page-table entry has some bits we have to treat in a special way. | 1177 | * A page-table entry has some bits we have to treat in a special way. |
diff --git a/arch/s390/mm/gup.c b/arch/s390/mm/gup.c index eeaf8023851f..60acb93a4680 100644 --- a/arch/s390/mm/gup.c +++ b/arch/s390/mm/gup.c | |||
@@ -115,7 +115,16 @@ static inline int gup_pmd_range(pud_t *pudp, pud_t pud, unsigned long addr, | |||
115 | pmd = *pmdp; | 115 | pmd = *pmdp; |
116 | barrier(); | 116 | barrier(); |
117 | next = pmd_addr_end(addr, end); | 117 | next = pmd_addr_end(addr, end); |
118 | if (pmd_none(pmd)) | 118 | /* |
119 | * The pmd_trans_splitting() check below explains why | ||
120 | * pmdp_splitting_flush() has to serialize with | ||
121 | * smp_call_function() against our disabled IRQs, to stop | ||
122 | * this gup-fast code from running while we set the | ||
123 | * splitting bit in the pmd. Returning zero will take | ||
124 | * the slow path that will call wait_split_huge_page() | ||
125 | * if the pmd is still in splitting state. | ||
126 | */ | ||
127 | if (pmd_none(pmd) || pmd_trans_splitting(pmd)) | ||
119 | return 0; | 128 | return 0; |
120 | if (unlikely(pmd_huge(pmd))) { | 129 | if (unlikely(pmd_huge(pmd))) { |
121 | if (!gup_huge_pmd(pmdp, pmd, addr, next, | 130 | if (!gup_huge_pmd(pmdp, pmd, addr, next, |
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index b402991e43d7..a6131d1fe6c0 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c | |||
@@ -866,3 +866,21 @@ bool kernel_page_present(struct page *page) | |||
866 | return cc == 0; | 866 | return cc == 0; |
867 | } | 867 | } |
868 | #endif /* CONFIG_HIBERNATION && CONFIG_DEBUG_PAGEALLOC */ | 868 | #endif /* CONFIG_HIBERNATION && CONFIG_DEBUG_PAGEALLOC */ |
869 | |||
870 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
871 | static void pmdp_splitting_flush_sync(void *arg) | ||
872 | { | ||
873 | /* Simply deliver the interrupt */ | ||
874 | } | ||
875 | |||
876 | void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address, | ||
877 | pmd_t *pmdp) | ||
878 | { | ||
879 | VM_BUG_ON(address & ~HPAGE_PMD_MASK); | ||
880 | if (!test_and_set_bit(_SEGMENT_ENTRY_SPLIT_BIT, | ||
881 | (unsigned long *) pmdp)) { | ||
882 | /* need to serialize against gup-fast (IRQ disabled) */ | ||
883 | smp_call_function(pmdp_splitting_flush_sync, NULL, 1); | ||
884 | } | ||
885 | } | ||
886 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ | ||