diff options
Diffstat (limited to 'arch/s390/mm/pgtable.c')
-rw-r--r-- | arch/s390/mm/pgtable.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index b402991e43d7..c8188a18af05 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c | |||
@@ -787,6 +787,30 @@ void tlb_remove_table(struct mmu_gather *tlb, void *table) | |||
787 | tlb_table_flush(tlb); | 787 | tlb_table_flush(tlb); |
788 | } | 788 | } |
789 | 789 | ||
790 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
791 | void thp_split_vma(struct vm_area_struct *vma) | ||
792 | { | ||
793 | unsigned long addr; | ||
794 | struct page *page; | ||
795 | |||
796 | for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) { | ||
797 | page = follow_page(vma, addr, FOLL_SPLIT); | ||
798 | } | ||
799 | } | ||
800 | |||
801 | void thp_split_mm(struct mm_struct *mm) | ||
802 | { | ||
803 | struct vm_area_struct *vma = mm->mmap; | ||
804 | |||
805 | while (vma != NULL) { | ||
806 | thp_split_vma(vma); | ||
807 | vma->vm_flags &= ~VM_HUGEPAGE; | ||
808 | vma->vm_flags |= VM_NOHUGEPAGE; | ||
809 | vma = vma->vm_next; | ||
810 | } | ||
811 | } | ||
812 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ | ||
813 | |||
790 | /* | 814 | /* |
791 | * switch on pgstes for its userspace process (for kvm) | 815 | * switch on pgstes for its userspace process (for kvm) |
792 | */ | 816 | */ |
@@ -824,6 +848,12 @@ int s390_enable_sie(void) | |||
824 | if (!mm) | 848 | if (!mm) |
825 | return -ENOMEM; | 849 | return -ENOMEM; |
826 | 850 | ||
851 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
852 | /* split thp mappings and disable thp for future mappings */ | ||
853 | thp_split_mm(mm); | ||
854 | mm->def_flags |= VM_NOHUGEPAGE; | ||
855 | #endif | ||
856 | |||
827 | /* Now lets check again if something happened */ | 857 | /* Now lets check again if something happened */ |
828 | task_lock(tsk); | 858 | task_lock(tsk); |
829 | if (!tsk->mm || atomic_read(&tsk->mm->mm_users) > 1 || | 859 | if (!tsk->mm || atomic_read(&tsk->mm->mm_users) > 1 || |
@@ -866,3 +896,81 @@ bool kernel_page_present(struct page *page) | |||
866 | return cc == 0; | 896 | return cc == 0; |
867 | } | 897 | } |
868 | #endif /* CONFIG_HIBERNATION && CONFIG_DEBUG_PAGEALLOC */ | 898 | #endif /* CONFIG_HIBERNATION && CONFIG_DEBUG_PAGEALLOC */ |
899 | |||
900 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
901 | int pmdp_clear_flush_young(struct vm_area_struct *vma, unsigned long address, | ||
902 | pmd_t *pmdp) | ||
903 | { | ||
904 | VM_BUG_ON(address & ~HPAGE_PMD_MASK); | ||
905 | /* No need to flush TLB | ||
906 | * On s390 reference bits are in storage key and never in TLB */ | ||
907 | return pmdp_test_and_clear_young(vma, address, pmdp); | ||
908 | } | ||
909 | |||
910 | int pmdp_set_access_flags(struct vm_area_struct *vma, | ||
911 | unsigned long address, pmd_t *pmdp, | ||
912 | pmd_t entry, int dirty) | ||
913 | { | ||
914 | VM_BUG_ON(address & ~HPAGE_PMD_MASK); | ||
915 | |||
916 | if (pmd_same(*pmdp, entry)) | ||
917 | return 0; | ||
918 | pmdp_invalidate(vma, address, pmdp); | ||
919 | set_pmd_at(vma->vm_mm, address, pmdp, entry); | ||
920 | return 1; | ||
921 | } | ||
922 | |||
923 | static void pmdp_splitting_flush_sync(void *arg) | ||
924 | { | ||
925 | /* Simply deliver the interrupt */ | ||
926 | } | ||
927 | |||
928 | void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address, | ||
929 | pmd_t *pmdp) | ||
930 | { | ||
931 | VM_BUG_ON(address & ~HPAGE_PMD_MASK); | ||
932 | if (!test_and_set_bit(_SEGMENT_ENTRY_SPLIT_BIT, | ||
933 | (unsigned long *) pmdp)) { | ||
934 | /* need to serialize against gup-fast (IRQ disabled) */ | ||
935 | smp_call_function(pmdp_splitting_flush_sync, NULL, 1); | ||
936 | } | ||
937 | } | ||
938 | |||
939 | void pgtable_trans_huge_deposit(struct mm_struct *mm, pgtable_t pgtable) | ||
940 | { | ||
941 | struct list_head *lh = (struct list_head *) pgtable; | ||
942 | |||
943 | assert_spin_locked(&mm->page_table_lock); | ||
944 | |||
945 | /* FIFO */ | ||
946 | if (!mm->pmd_huge_pte) | ||
947 | INIT_LIST_HEAD(lh); | ||
948 | else | ||
949 | list_add(lh, (struct list_head *) mm->pmd_huge_pte); | ||
950 | mm->pmd_huge_pte = pgtable; | ||
951 | } | ||
952 | |||
953 | pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm) | ||
954 | { | ||
955 | struct list_head *lh; | ||
956 | pgtable_t pgtable; | ||
957 | pte_t *ptep; | ||
958 | |||
959 | assert_spin_locked(&mm->page_table_lock); | ||
960 | |||
961 | /* FIFO */ | ||
962 | pgtable = mm->pmd_huge_pte; | ||
963 | lh = (struct list_head *) pgtable; | ||
964 | if (list_empty(lh)) | ||
965 | mm->pmd_huge_pte = NULL; | ||
966 | else { | ||
967 | mm->pmd_huge_pte = (pgtable_t) lh->next; | ||
968 | list_del(lh); | ||
969 | } | ||
970 | ptep = (pte_t *) pgtable; | ||
971 | pte_val(*ptep) = _PAGE_TYPE_EMPTY; | ||
972 | ptep++; | ||
973 | pte_val(*ptep) = _PAGE_TYPE_EMPTY; | ||
974 | return pgtable; | ||
975 | } | ||
976 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ | ||