aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/mm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/mm')
-rw-r--r--arch/s390/mm/pgtable.c160
1 files changed, 91 insertions, 69 deletions
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 2accf7113d13..bd954e96f51c 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -454,12 +454,11 @@ unsigned long gmap_translate(unsigned long address, struct gmap *gmap)
454} 454}
455EXPORT_SYMBOL_GPL(gmap_translate); 455EXPORT_SYMBOL_GPL(gmap_translate);
456 456
457/* 457static int gmap_connect_pgtable(unsigned long segment,
458 * this function is assumed to be called with mmap_sem held 458 unsigned long *segment_ptr,
459 */ 459 struct gmap *gmap)
460unsigned long __gmap_fault(unsigned long address, struct gmap *gmap)
461{ 460{
462 unsigned long *segment_ptr, vmaddr, segment; 461 unsigned long vmaddr;
463 struct vm_area_struct *vma; 462 struct vm_area_struct *vma;
464 struct gmap_pgtable *mp; 463 struct gmap_pgtable *mp;
465 struct gmap_rmap *rmap; 464 struct gmap_rmap *rmap;
@@ -469,48 +468,94 @@ unsigned long __gmap_fault(unsigned long address, struct gmap *gmap)
469 pud_t *pud; 468 pud_t *pud;
470 pmd_t *pmd; 469 pmd_t *pmd;
471 470
471 mm = gmap->mm;
472 vmaddr = segment & _SEGMENT_ENTRY_ORIGIN;
473 vma = find_vma(mm, vmaddr);
474 if (!vma || vma->vm_start > vmaddr)
475 return -EFAULT;
476 /* Walk the parent mm page table */
477 pgd = pgd_offset(mm, vmaddr);
478 pud = pud_alloc(mm, pgd, vmaddr);
479 if (!pud)
480 return -ENOMEM;
481 pmd = pmd_alloc(mm, pud, vmaddr);
482 if (!pmd)
483 return -ENOMEM;
484 if (!pmd_present(*pmd) &&
485 __pte_alloc(mm, vma, pmd, vmaddr))
486 return -ENOMEM;
487 /* pmd now points to a valid segment table entry. */
488 rmap = kmalloc(sizeof(*rmap), GFP_KERNEL|__GFP_REPEAT);
489 if (!rmap)
490 return -ENOMEM;
491 /* Link gmap segment table entry location to page table. */
492 page = pmd_page(*pmd);
493 mp = (struct gmap_pgtable *) page->index;
494 rmap->entry = segment_ptr;
495 spin_lock(&mm->page_table_lock);
496 if (*segment_ptr == segment) {
497 list_add(&rmap->list, &mp->mapper);
498 /* Set gmap segment table entry to page table. */
499 *segment_ptr = pmd_val(*pmd) & PAGE_MASK;
500 rmap = NULL;
501 }
502 spin_unlock(&mm->page_table_lock);
503 kfree(rmap);
504 return 0;
505}
506
507static void gmap_disconnect_pgtable(struct mm_struct *mm, unsigned long *table)
508{
509 struct gmap_rmap *rmap, *next;
510 struct gmap_pgtable *mp;
511 struct page *page;
512 int flush;
513
514 flush = 0;
515 spin_lock(&mm->page_table_lock);
516 page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
517 mp = (struct gmap_pgtable *) page->index;
518 list_for_each_entry_safe(rmap, next, &mp->mapper, list) {
519 *rmap->entry =
520 _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO | mp->vmaddr;
521 list_del(&rmap->list);
522 kfree(rmap);
523 flush = 1;
524 }
525 spin_unlock(&mm->page_table_lock);
526 if (flush)
527 __tlb_flush_global();
528}
529
530/*
531 * this function is assumed to be called with mmap_sem held
532 */
533unsigned long __gmap_fault(unsigned long address, struct gmap *gmap)
534{
535 unsigned long *segment_ptr, segment;
536 struct gmap_pgtable *mp;
537 struct page *page;
538 int rc;
539
472 current->thread.gmap_addr = address; 540 current->thread.gmap_addr = address;
473 segment_ptr = gmap_table_walk(address, gmap); 541 segment_ptr = gmap_table_walk(address, gmap);
474 if (IS_ERR(segment_ptr)) 542 if (IS_ERR(segment_ptr))
475 return -EFAULT; 543 return -EFAULT;
476 /* Convert the gmap address to an mm address. */ 544 /* Convert the gmap address to an mm address. */
477 segment = *segment_ptr; 545 while (1) {
478 if (!(segment & _SEGMENT_ENTRY_INV)) { 546 segment = *segment_ptr;
479 page = pfn_to_page(segment >> PAGE_SHIFT); 547 if (!(segment & _SEGMENT_ENTRY_INV)) {
480 mp = (struct gmap_pgtable *) page->index; 548 /* Page table is present */
481 return mp->vmaddr | (address & ~PMD_MASK); 549 page = pfn_to_page(segment >> PAGE_SHIFT);
482 } else if (segment & _SEGMENT_ENTRY_RO) { 550 mp = (struct gmap_pgtable *) page->index;
483 mm = gmap->mm; 551 return mp->vmaddr | (address & ~PMD_MASK);
484 vmaddr = segment & _SEGMENT_ENTRY_ORIGIN; 552 }
485 vma = find_vma(mm, vmaddr); 553 if (!(segment & _SEGMENT_ENTRY_RO))
486 if (!vma || vma->vm_start > vmaddr) 554 /* Nothing mapped in the gmap address space. */
487 return -EFAULT; 555 break;
488 556 rc = gmap_connect_pgtable(segment, segment_ptr, gmap);
489 /* Walk the parent mm page table */ 557 if (rc)
490 pgd = pgd_offset(mm, vmaddr); 558 return rc;
491 pud = pud_alloc(mm, pgd, vmaddr);
492 if (!pud)
493 return -ENOMEM;
494 pmd = pmd_alloc(mm, pud, vmaddr);
495 if (!pmd)
496 return -ENOMEM;
497 if (!pmd_present(*pmd) &&
498 __pte_alloc(mm, vma, pmd, vmaddr))
499 return -ENOMEM;
500 /* pmd now points to a valid segment table entry. */
501 rmap = kmalloc(sizeof(*rmap), GFP_KERNEL|__GFP_REPEAT);
502 if (!rmap)
503 return -ENOMEM;
504 /* Link gmap segment table entry location to page table. */
505 page = pmd_page(*pmd);
506 mp = (struct gmap_pgtable *) page->index;
507 rmap->entry = segment_ptr;
508 spin_lock(&mm->page_table_lock);
509 list_add(&rmap->list, &mp->mapper);
510 spin_unlock(&mm->page_table_lock);
511 /* Set gmap segment table entry to page table. */
512 *segment_ptr = pmd_val(*pmd) & PAGE_MASK;
513 return vmaddr | (address & ~PMD_MASK);
514 } 559 }
515 return -EFAULT; 560 return -EFAULT;
516} 561}
@@ -574,29 +619,6 @@ void gmap_discard(unsigned long from, unsigned long to, struct gmap *gmap)
574} 619}
575EXPORT_SYMBOL_GPL(gmap_discard); 620EXPORT_SYMBOL_GPL(gmap_discard);
576 621
577void gmap_unmap_notifier(struct mm_struct *mm, unsigned long *table)
578{
579 struct gmap_rmap *rmap, *next;
580 struct gmap_pgtable *mp;
581 struct page *page;
582 int flush;
583
584 flush = 0;
585 spin_lock(&mm->page_table_lock);
586 page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
587 mp = (struct gmap_pgtable *) page->index;
588 list_for_each_entry_safe(rmap, next, &mp->mapper, list) {
589 *rmap->entry =
590 _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO | mp->vmaddr;
591 list_del(&rmap->list);
592 kfree(rmap);
593 flush = 1;
594 }
595 spin_unlock(&mm->page_table_lock);
596 if (flush)
597 __tlb_flush_global();
598}
599
600static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm, 622static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
601 unsigned long vmaddr) 623 unsigned long vmaddr)
602{ 624{
@@ -649,8 +671,8 @@ static inline void page_table_free_pgste(unsigned long *table)
649{ 671{
650} 672}
651 673
652static inline void gmap_unmap_notifier(struct mm_struct *mm, 674static inline void gmap_disconnect_pgtable(struct mm_struct *mm,
653 unsigned long *table) 675 unsigned long *table)
654{ 676{
655} 677}
656 678
@@ -716,7 +738,7 @@ void page_table_free(struct mm_struct *mm, unsigned long *table)
716 unsigned int bit, mask; 738 unsigned int bit, mask;
717 739
718 if (mm_has_pgste(mm)) { 740 if (mm_has_pgste(mm)) {
719 gmap_unmap_notifier(mm, table); 741 gmap_disconnect_pgtable(mm, table);
720 return page_table_free_pgste(table); 742 return page_table_free_pgste(table);
721 } 743 }
722 /* Free 1K/2K page table fragment of a 4K page */ 744 /* Free 1K/2K page table fragment of a 4K page */
@@ -759,7 +781,7 @@ void page_table_free_rcu(struct mmu_gather *tlb, unsigned long *table)
759 781
760 mm = tlb->mm; 782 mm = tlb->mm;
761 if (mm_has_pgste(mm)) { 783 if (mm_has_pgste(mm)) {
762 gmap_unmap_notifier(mm, table); 784 gmap_disconnect_pgtable(mm, table);
763 table = (unsigned long *) (__pa(table) | FRAG_MASK); 785 table = (unsigned long *) (__pa(table) | FRAG_MASK);
764 tlb_remove_table(tlb, table); 786 tlb_remove_table(tlb, table);
765 return; 787 return;