diff options
-rw-r--r-- | arch/s390/include/asm/pgtable.h | 66 | ||||
-rw-r--r-- | arch/s390/mm/pgtable.c | 121 |
2 files changed, 173 insertions, 14 deletions
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index b4622915bd15..4105b8221fdd 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
@@ -306,6 +306,7 @@ extern unsigned long MODULES_END; | |||
306 | #define RCP_HC_BIT 0x00200000UL | 306 | #define RCP_HC_BIT 0x00200000UL |
307 | #define RCP_GR_BIT 0x00040000UL | 307 | #define RCP_GR_BIT 0x00040000UL |
308 | #define RCP_GC_BIT 0x00020000UL | 308 | #define RCP_GC_BIT 0x00020000UL |
309 | #define RCP_IN_BIT 0x00008000UL /* IPTE notify bit */ | ||
309 | 310 | ||
310 | /* User dirty / referenced bit for KVM's migration feature */ | 311 | /* User dirty / referenced bit for KVM's migration feature */ |
311 | #define KVM_UR_BIT 0x00008000UL | 312 | #define KVM_UR_BIT 0x00008000UL |
@@ -373,6 +374,7 @@ extern unsigned long MODULES_END; | |||
373 | #define RCP_HC_BIT 0x0020000000000000UL | 374 | #define RCP_HC_BIT 0x0020000000000000UL |
374 | #define RCP_GR_BIT 0x0004000000000000UL | 375 | #define RCP_GR_BIT 0x0004000000000000UL |
375 | #define RCP_GC_BIT 0x0002000000000000UL | 376 | #define RCP_GC_BIT 0x0002000000000000UL |
377 | #define RCP_IN_BIT 0x0000800000000000UL /* IPTE notify bit */ | ||
376 | 378 | ||
377 | /* User dirty / referenced bit for KVM's migration feature */ | 379 | /* User dirty / referenced bit for KVM's migration feature */ |
378 | #define KVM_UR_BIT 0x0000800000000000UL | 380 | #define KVM_UR_BIT 0x0000800000000000UL |
@@ -746,30 +748,42 @@ struct gmap { | |||
746 | 748 | ||
747 | /** | 749 | /** |
748 | * struct gmap_rmap - reverse mapping for segment table entries | 750 | * struct gmap_rmap - reverse mapping for segment table entries |
749 | * @next: pointer to the next gmap_rmap structure in the list | 751 | * @gmap: pointer to the gmap_struct |
750 | * @entry: pointer to a segment table entry | 752 | * @entry: pointer to a segment table entry |
753 | * @vmaddr: virtual address in the guest address space | ||
751 | */ | 754 | */ |
752 | struct gmap_rmap { | 755 | struct gmap_rmap { |
753 | struct list_head list; | 756 | struct list_head list; |
757 | struct gmap *gmap; | ||
754 | unsigned long *entry; | 758 | unsigned long *entry; |
759 | unsigned long vmaddr; | ||
755 | }; | 760 | }; |
756 | 761 | ||
757 | /** | 762 | /** |
758 | * struct gmap_pgtable - gmap information attached to a page table | 763 | * struct gmap_pgtable - gmap information attached to a page table |
759 | * @vmaddr: address of the 1MB segment in the process virtual memory | 764 | * @vmaddr: address of the 1MB segment in the process virtual memory |
760 | * @mapper: list of segment table entries maping a page table | 765 | * @mapper: list of segment table entries mapping a page table |
761 | */ | 766 | */ |
762 | struct gmap_pgtable { | 767 | struct gmap_pgtable { |
763 | unsigned long vmaddr; | 768 | unsigned long vmaddr; |
764 | struct list_head mapper; | 769 | struct list_head mapper; |
765 | }; | 770 | }; |
766 | 771 | ||
772 | /** | ||
773 | * struct gmap_notifier - notify function block for page invalidation | ||
774 | * @notifier_call: address of callback function | ||
775 | */ | ||
776 | struct gmap_notifier { | ||
777 | struct list_head list; | ||
778 | void (*notifier_call)(struct gmap *gmap, unsigned long address); | ||
779 | }; | ||
780 | |||
767 | struct gmap *gmap_alloc(struct mm_struct *mm); | 781 | struct gmap *gmap_alloc(struct mm_struct *mm); |
768 | void gmap_free(struct gmap *gmap); | 782 | void gmap_free(struct gmap *gmap); |
769 | void gmap_enable(struct gmap *gmap); | 783 | void gmap_enable(struct gmap *gmap); |
770 | void gmap_disable(struct gmap *gmap); | 784 | void gmap_disable(struct gmap *gmap); |
771 | int gmap_map_segment(struct gmap *gmap, unsigned long from, | 785 | int gmap_map_segment(struct gmap *gmap, unsigned long from, |
772 | unsigned long to, unsigned long length); | 786 | unsigned long to, unsigned long len); |
773 | int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len); | 787 | int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len); |
774 | unsigned long __gmap_translate(unsigned long address, struct gmap *); | 788 | unsigned long __gmap_translate(unsigned long address, struct gmap *); |
775 | unsigned long gmap_translate(unsigned long address, struct gmap *); | 789 | unsigned long gmap_translate(unsigned long address, struct gmap *); |
@@ -777,6 +791,24 @@ unsigned long __gmap_fault(unsigned long address, struct gmap *); | |||
777 | unsigned long gmap_fault(unsigned long address, struct gmap *); | 791 | unsigned long gmap_fault(unsigned long address, struct gmap *); |
778 | void gmap_discard(unsigned long from, unsigned long to, struct gmap *); | 792 | void gmap_discard(unsigned long from, unsigned long to, struct gmap *); |
779 | 793 | ||
794 | void gmap_register_ipte_notifier(struct gmap_notifier *); | ||
795 | void gmap_unregister_ipte_notifier(struct gmap_notifier *); | ||
796 | int gmap_ipte_notify(struct gmap *, unsigned long start, unsigned long len); | ||
797 | void gmap_do_ipte_notify(struct mm_struct *, unsigned long addr, pte_t *); | ||
798 | |||
799 | static inline pgste_t pgste_ipte_notify(struct mm_struct *mm, | ||
800 | unsigned long addr, | ||
801 | pte_t *ptep, pgste_t pgste) | ||
802 | { | ||
803 | #ifdef CONFIG_PGSTE | ||
804 | if (pgste_val(pgste) & RCP_IN_BIT) { | ||
805 | pgste_val(pgste) &= ~RCP_IN_BIT; | ||
806 | gmap_do_ipte_notify(mm, addr, ptep); | ||
807 | } | ||
808 | #endif | ||
809 | return pgste; | ||
810 | } | ||
811 | |||
780 | /* | 812 | /* |
781 | * Certain architectures need to do special things when PTEs | 813 | * Certain architectures need to do special things when PTEs |
782 | * within a page table are directly modified. Thus, the following | 814 | * within a page table are directly modified. Thus, the following |
@@ -1032,8 +1064,10 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, | |||
1032 | pte_t pte; | 1064 | pte_t pte; |
1033 | 1065 | ||
1034 | mm->context.flush_mm = 1; | 1066 | mm->context.flush_mm = 1; |
1035 | if (mm_has_pgste(mm)) | 1067 | if (mm_has_pgste(mm)) { |
1036 | pgste = pgste_get_lock(ptep); | 1068 | pgste = pgste_get_lock(ptep); |
1069 | pgste = pgste_ipte_notify(mm, address, ptep, pgste); | ||
1070 | } | ||
1037 | 1071 | ||
1038 | pte = *ptep; | 1072 | pte = *ptep; |
1039 | if (!mm_exclusive(mm)) | 1073 | if (!mm_exclusive(mm)) |
@@ -1052,11 +1086,14 @@ static inline pte_t ptep_modify_prot_start(struct mm_struct *mm, | |||
1052 | unsigned long address, | 1086 | unsigned long address, |
1053 | pte_t *ptep) | 1087 | pte_t *ptep) |
1054 | { | 1088 | { |
1089 | pgste_t pgste; | ||
1055 | pte_t pte; | 1090 | pte_t pte; |
1056 | 1091 | ||
1057 | mm->context.flush_mm = 1; | 1092 | mm->context.flush_mm = 1; |
1058 | if (mm_has_pgste(mm)) | 1093 | if (mm_has_pgste(mm)) { |
1059 | pgste_get_lock(ptep); | 1094 | pgste = pgste_get_lock(ptep); |
1095 | pgste_ipte_notify(mm, address, ptep, pgste); | ||
1096 | } | ||
1060 | 1097 | ||
1061 | pte = *ptep; | 1098 | pte = *ptep; |
1062 | if (!mm_exclusive(mm)) | 1099 | if (!mm_exclusive(mm)) |
@@ -1082,8 +1119,10 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma, | |||
1082 | pgste_t pgste; | 1119 | pgste_t pgste; |
1083 | pte_t pte; | 1120 | pte_t pte; |
1084 | 1121 | ||
1085 | if (mm_has_pgste(vma->vm_mm)) | 1122 | if (mm_has_pgste(vma->vm_mm)) { |
1086 | pgste = pgste_get_lock(ptep); | 1123 | pgste = pgste_get_lock(ptep); |
1124 | pgste = pgste_ipte_notify(vma->vm_mm, address, ptep, pgste); | ||
1125 | } | ||
1087 | 1126 | ||
1088 | pte = *ptep; | 1127 | pte = *ptep; |
1089 | __ptep_ipte(address, ptep); | 1128 | __ptep_ipte(address, ptep); |
@@ -1111,8 +1150,11 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm, | |||
1111 | pgste_t pgste; | 1150 | pgste_t pgste; |
1112 | pte_t pte; | 1151 | pte_t pte; |
1113 | 1152 | ||
1114 | if (mm_has_pgste(mm)) | 1153 | if (mm_has_pgste(mm)) { |
1115 | pgste = pgste_get_lock(ptep); | 1154 | pgste = pgste_get_lock(ptep); |
1155 | if (!full) | ||
1156 | pgste = pgste_ipte_notify(mm, address, ptep, pgste); | ||
1157 | } | ||
1116 | 1158 | ||
1117 | pte = *ptep; | 1159 | pte = *ptep; |
1118 | if (!full) | 1160 | if (!full) |
@@ -1135,8 +1177,10 @@ static inline pte_t ptep_set_wrprotect(struct mm_struct *mm, | |||
1135 | 1177 | ||
1136 | if (pte_write(pte)) { | 1178 | if (pte_write(pte)) { |
1137 | mm->context.flush_mm = 1; | 1179 | mm->context.flush_mm = 1; |
1138 | if (mm_has_pgste(mm)) | 1180 | if (mm_has_pgste(mm)) { |
1139 | pgste = pgste_get_lock(ptep); | 1181 | pgste = pgste_get_lock(ptep); |
1182 | pgste = pgste_ipte_notify(mm, address, ptep, pgste); | ||
1183 | } | ||
1140 | 1184 | ||
1141 | if (!mm_exclusive(mm)) | 1185 | if (!mm_exclusive(mm)) |
1142 | __ptep_ipte(address, ptep); | 1186 | __ptep_ipte(address, ptep); |
@@ -1160,8 +1204,10 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma, | |||
1160 | 1204 | ||
1161 | if (pte_same(*ptep, entry)) | 1205 | if (pte_same(*ptep, entry)) |
1162 | return 0; | 1206 | return 0; |
1163 | if (mm_has_pgste(vma->vm_mm)) | 1207 | if (mm_has_pgste(vma->vm_mm)) { |
1164 | pgste = pgste_get_lock(ptep); | 1208 | pgste = pgste_get_lock(ptep); |
1209 | pgste = pgste_ipte_notify(vma->vm_mm, address, ptep, pgste); | ||
1210 | } | ||
1165 | 1211 | ||
1166 | __ptep_ipte(address, ptep); | 1212 | __ptep_ipte(address, ptep); |
1167 | 1213 | ||
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index bd954e96f51c..7805ddca833d 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c | |||
@@ -454,9 +454,8 @@ unsigned long gmap_translate(unsigned long address, struct gmap *gmap) | |||
454 | } | 454 | } |
455 | EXPORT_SYMBOL_GPL(gmap_translate); | 455 | EXPORT_SYMBOL_GPL(gmap_translate); |
456 | 456 | ||
457 | static int gmap_connect_pgtable(unsigned long segment, | 457 | static int gmap_connect_pgtable(unsigned long address, unsigned long segment, |
458 | unsigned long *segment_ptr, | 458 | unsigned long *segment_ptr, struct gmap *gmap) |
459 | struct gmap *gmap) | ||
460 | { | 459 | { |
461 | unsigned long vmaddr; | 460 | unsigned long vmaddr; |
462 | struct vm_area_struct *vma; | 461 | struct vm_area_struct *vma; |
@@ -491,7 +490,9 @@ static int gmap_connect_pgtable(unsigned long segment, | |||
491 | /* Link gmap segment table entry location to page table. */ | 490 | /* Link gmap segment table entry location to page table. */ |
492 | page = pmd_page(*pmd); | 491 | page = pmd_page(*pmd); |
493 | mp = (struct gmap_pgtable *) page->index; | 492 | mp = (struct gmap_pgtable *) page->index; |
493 | rmap->gmap = gmap; | ||
494 | rmap->entry = segment_ptr; | 494 | rmap->entry = segment_ptr; |
495 | rmap->vmaddr = address; | ||
495 | spin_lock(&mm->page_table_lock); | 496 | spin_lock(&mm->page_table_lock); |
496 | if (*segment_ptr == segment) { | 497 | if (*segment_ptr == segment) { |
497 | list_add(&rmap->list, &mp->mapper); | 498 | list_add(&rmap->list, &mp->mapper); |
@@ -553,7 +554,7 @@ unsigned long __gmap_fault(unsigned long address, struct gmap *gmap) | |||
553 | if (!(segment & _SEGMENT_ENTRY_RO)) | 554 | if (!(segment & _SEGMENT_ENTRY_RO)) |
554 | /* Nothing mapped in the gmap address space. */ | 555 | /* Nothing mapped in the gmap address space. */ |
555 | break; | 556 | break; |
556 | rc = gmap_connect_pgtable(segment, segment_ptr, gmap); | 557 | rc = gmap_connect_pgtable(address, segment, segment_ptr, gmap); |
557 | if (rc) | 558 | if (rc) |
558 | return rc; | 559 | return rc; |
559 | } | 560 | } |
@@ -619,6 +620,118 @@ void gmap_discard(unsigned long from, unsigned long to, struct gmap *gmap) | |||
619 | } | 620 | } |
620 | EXPORT_SYMBOL_GPL(gmap_discard); | 621 | EXPORT_SYMBOL_GPL(gmap_discard); |
621 | 622 | ||
623 | static LIST_HEAD(gmap_notifier_list); | ||
624 | static DEFINE_SPINLOCK(gmap_notifier_lock); | ||
625 | |||
626 | /** | ||
627 | * gmap_register_ipte_notifier - register a pte invalidation callback | ||
628 | * @nb: pointer to the gmap notifier block | ||
629 | */ | ||
630 | void gmap_register_ipte_notifier(struct gmap_notifier *nb) | ||
631 | { | ||
632 | spin_lock(&gmap_notifier_lock); | ||
633 | list_add(&nb->list, &gmap_notifier_list); | ||
634 | spin_unlock(&gmap_notifier_lock); | ||
635 | } | ||
636 | EXPORT_SYMBOL_GPL(gmap_register_ipte_notifier); | ||
637 | |||
638 | /** | ||
639 | * gmap_unregister_ipte_notifier - remove a pte invalidation callback | ||
640 | * @nb: pointer to the gmap notifier block | ||
641 | */ | ||
642 | void gmap_unregister_ipte_notifier(struct gmap_notifier *nb) | ||
643 | { | ||
644 | spin_lock(&gmap_notifier_lock); | ||
645 | list_del_init(&nb->list); | ||
646 | spin_unlock(&gmap_notifier_lock); | ||
647 | } | ||
648 | EXPORT_SYMBOL_GPL(gmap_unregister_ipte_notifier); | ||
649 | |||
650 | /** | ||
651 | * gmap_ipte_notify - mark a range of ptes for invalidation notification | ||
652 | * @gmap: pointer to guest mapping meta data structure | ||
653 | * @address: virtual address in the guest address space | ||
654 | * @len: size of area | ||
655 | * | ||
656 | * Returns 0 if for each page in the given range a gmap mapping exists and | ||
657 | * the invalidation notification could be set. If the gmap mapping is missing | ||
658 | * for one or more pages -EFAULT is returned. If no memory could be allocated | ||
659 | * -ENOMEM is returned. This function establishes missing page table entries. | ||
660 | */ | ||
661 | int gmap_ipte_notify(struct gmap *gmap, unsigned long start, unsigned long len) | ||
662 | { | ||
663 | unsigned long addr; | ||
664 | spinlock_t *ptl; | ||
665 | pte_t *ptep, entry; | ||
666 | pgste_t pgste; | ||
667 | int rc = 0; | ||
668 | |||
669 | if ((start & ~PAGE_MASK) || (len & ~PAGE_MASK)) | ||
670 | return -EINVAL; | ||
671 | down_read(&gmap->mm->mmap_sem); | ||
672 | while (len) { | ||
673 | /* Convert gmap address and connect the page tables */ | ||
674 | addr = __gmap_fault(start, gmap); | ||
675 | if (IS_ERR_VALUE(addr)) { | ||
676 | rc = addr; | ||
677 | break; | ||
678 | } | ||
679 | /* Get the page mapped */ | ||
680 | if (get_user_pages(current, gmap->mm, addr, 1, 1, 0, | ||
681 | NULL, NULL) != 1) { | ||
682 | rc = -EFAULT; | ||
683 | break; | ||
684 | } | ||
685 | /* Walk the process page table, lock and get pte pointer */ | ||
686 | ptep = get_locked_pte(gmap->mm, addr, &ptl); | ||
687 | if (unlikely(!ptep)) | ||
688 | continue; | ||
689 | /* Set notification bit in the pgste of the pte */ | ||
690 | entry = *ptep; | ||
691 | if ((pte_val(entry) & (_PAGE_INVALID | _PAGE_RO)) == 0) { | ||
692 | pgste = pgste_get_lock(ptep); | ||
693 | pgste_val(pgste) |= RCP_IN_BIT; | ||
694 | pgste_set_unlock(ptep, pgste); | ||
695 | start += PAGE_SIZE; | ||
696 | len -= PAGE_SIZE; | ||
697 | } | ||
698 | spin_unlock(ptl); | ||
699 | } | ||
700 | up_read(&gmap->mm->mmap_sem); | ||
701 | return rc; | ||
702 | } | ||
703 | EXPORT_SYMBOL_GPL(gmap_ipte_notify); | ||
704 | |||
705 | /** | ||
706 | * gmap_do_ipte_notify - call all invalidation callbacks for a specific pte. | ||
707 | * @mm: pointer to the process mm_struct | ||
708 | * @addr: virtual address in the process address space | ||
709 | * @pte: pointer to the page table entry | ||
710 | * | ||
711 | * This function is assumed to be called with the page table lock held | ||
712 | * for the pte to notify. | ||
713 | */ | ||
714 | void gmap_do_ipte_notify(struct mm_struct *mm, unsigned long addr, pte_t *pte) | ||
715 | { | ||
716 | unsigned long segment_offset; | ||
717 | struct gmap_notifier *nb; | ||
718 | struct gmap_pgtable *mp; | ||
719 | struct gmap_rmap *rmap; | ||
720 | struct page *page; | ||
721 | |||
722 | segment_offset = ((unsigned long) pte) & (255 * sizeof(pte_t)); | ||
723 | segment_offset = segment_offset * (4096 / sizeof(pte_t)); | ||
724 | page = pfn_to_page(__pa(pte) >> PAGE_SHIFT); | ||
725 | mp = (struct gmap_pgtable *) page->index; | ||
726 | spin_lock(&gmap_notifier_lock); | ||
727 | list_for_each_entry(rmap, &mp->mapper, list) { | ||
728 | list_for_each_entry(nb, &gmap_notifier_list, list) | ||
729 | nb->notifier_call(rmap->gmap, | ||
730 | rmap->vmaddr + segment_offset); | ||
731 | } | ||
732 | spin_unlock(&gmap_notifier_lock); | ||
733 | } | ||
734 | |||
622 | static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm, | 735 | static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm, |
623 | unsigned long vmaddr) | 736 | unsigned long vmaddr) |
624 | { | 737 | { |