diff options
| -rw-r--r-- | arch/s390/include/asm/kvm_host.h | 1 | ||||
| -rw-r--r-- | arch/s390/include/asm/pgtable.h | 1 | ||||
| -rw-r--r-- | arch/s390/kvm/diag.c | 32 | ||||
| -rw-r--r-- | arch/s390/kvm/kvm-s390.c | 1 | ||||
| -rw-r--r-- | arch/s390/mm/pgtable.c | 49 |
5 files changed, 82 insertions, 2 deletions
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 00ff00dfb24c..46c0bdaf4792 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h | |||
| @@ -143,6 +143,7 @@ struct kvm_vcpu_stat { | |||
| 143 | u32 instruction_sigp_arch; | 143 | u32 instruction_sigp_arch; |
| 144 | u32 instruction_sigp_prefix; | 144 | u32 instruction_sigp_prefix; |
| 145 | u32 instruction_sigp_restart; | 145 | u32 instruction_sigp_restart; |
| 146 | u32 diagnose_10; | ||
| 146 | u32 diagnose_44; | 147 | u32 diagnose_44; |
| 147 | }; | 148 | }; |
| 148 | 149 | ||
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index bc5f520f6f87..34ede0ea85a9 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
| @@ -698,6 +698,7 @@ int gmap_map_segment(struct gmap *gmap, unsigned long from, | |||
| 698 | int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len); | 698 | int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len); |
| 699 | unsigned long __gmap_fault(unsigned long address, struct gmap *); | 699 | unsigned long __gmap_fault(unsigned long address, struct gmap *); |
| 700 | unsigned long gmap_fault(unsigned long address, struct gmap *); | 700 | unsigned long gmap_fault(unsigned long address, struct gmap *); |
| 701 | void gmap_discard(unsigned long from, unsigned long to, struct gmap *); | ||
| 701 | 702 | ||
| 702 | /* | 703 | /* |
| 703 | * Certain architectures need to do special things when PTEs | 704 | * Certain architectures need to do special things when PTEs |
diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c index 9e4c84187cf5..87cedd61be04 100644 --- a/arch/s390/kvm/diag.c +++ b/arch/s390/kvm/diag.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * diag.c - handling diagnose instructions | 2 | * diag.c - handling diagnose instructions |
| 3 | * | 3 | * |
| 4 | * Copyright IBM Corp. 2008 | 4 | * Copyright IBM Corp. 2008,2011 |
| 5 | * | 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License (version 2 only) | 7 | * it under the terms of the GNU General Public License (version 2 only) |
| @@ -15,6 +15,34 @@ | |||
| 15 | #include <linux/kvm_host.h> | 15 | #include <linux/kvm_host.h> |
| 16 | #include "kvm-s390.h" | 16 | #include "kvm-s390.h" |
| 17 | 17 | ||
| 18 | static int diag_release_pages(struct kvm_vcpu *vcpu) | ||
| 19 | { | ||
| 20 | unsigned long start, end; | ||
| 21 | unsigned long prefix = vcpu->arch.sie_block->prefix; | ||
| 22 | |||
| 23 | start = vcpu->arch.guest_gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; | ||
| 24 | end = vcpu->arch.guest_gprs[vcpu->arch.sie_block->ipa & 0xf] + 4096; | ||
| 25 | |||
| 26 | if (start & ~PAGE_MASK || end & ~PAGE_MASK || start > end | ||
| 27 | || start < 2 * PAGE_SIZE) | ||
| 28 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | ||
| 29 | |||
| 30 | VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end); | ||
| 31 | vcpu->stat.diagnose_10++; | ||
| 32 | |||
| 33 | /* we checked for start > end above */ | ||
| 34 | if (end < prefix || start >= prefix + 2 * PAGE_SIZE) { | ||
| 35 | gmap_discard(start, end, vcpu->arch.gmap); | ||
| 36 | } else { | ||
| 37 | if (start < prefix) | ||
| 38 | gmap_discard(start, prefix, vcpu->arch.gmap); | ||
| 39 | if (end >= prefix) | ||
| 40 | gmap_discard(prefix + 2 * PAGE_SIZE, | ||
| 41 | end, vcpu->arch.gmap); | ||
| 42 | } | ||
| 43 | return 0; | ||
| 44 | } | ||
| 45 | |||
| 18 | static int __diag_time_slice_end(struct kvm_vcpu *vcpu) | 46 | static int __diag_time_slice_end(struct kvm_vcpu *vcpu) |
| 19 | { | 47 | { |
| 20 | VCPU_EVENT(vcpu, 5, "%s", "diag time slice end"); | 48 | VCPU_EVENT(vcpu, 5, "%s", "diag time slice end"); |
| @@ -57,6 +85,8 @@ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu) | |||
| 57 | int code = (vcpu->arch.sie_block->ipb & 0xfff0000) >> 16; | 85 | int code = (vcpu->arch.sie_block->ipb & 0xfff0000) >> 16; |
| 58 | 86 | ||
| 59 | switch (code) { | 87 | switch (code) { |
| 88 | case 0x10: | ||
| 89 | return diag_release_pages(vcpu); | ||
| 60 | case 0x44: | 90 | case 0x44: |
| 61 | return __diag_time_slice_end(vcpu); | 91 | return __diag_time_slice_end(vcpu); |
| 62 | case 0x308: | 92 | case 0x308: |
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index dc2b580e27bc..189d6bdcac08 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
| @@ -69,6 +69,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { | |||
| 69 | { "instruction_sigp_set_arch", VCPU_STAT(instruction_sigp_arch) }, | 69 | { "instruction_sigp_set_arch", VCPU_STAT(instruction_sigp_arch) }, |
| 70 | { "instruction_sigp_set_prefix", VCPU_STAT(instruction_sigp_prefix) }, | 70 | { "instruction_sigp_set_prefix", VCPU_STAT(instruction_sigp_prefix) }, |
| 71 | { "instruction_sigp_restart", VCPU_STAT(instruction_sigp_restart) }, | 71 | { "instruction_sigp_restart", VCPU_STAT(instruction_sigp_restart) }, |
| 72 | { "diagnose_10", VCPU_STAT(diagnose_10) }, | ||
| 72 | { "diagnose_44", VCPU_STAT(diagnose_44) }, | 73 | { "diagnose_44", VCPU_STAT(diagnose_44) }, |
| 73 | { NULL } | 74 | { NULL } |
| 74 | }; | 75 | }; |
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 441d34445d0e..301c84d3b542 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright IBM Corp. 2007,2009 | 2 | * Copyright IBM Corp. 2007,2011 |
| 3 | * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> | 3 | * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> |
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| @@ -478,6 +478,53 @@ unsigned long gmap_fault(unsigned long address, struct gmap *gmap) | |||
| 478 | } | 478 | } |
| 479 | EXPORT_SYMBOL_GPL(gmap_fault); | 479 | EXPORT_SYMBOL_GPL(gmap_fault); |
| 480 | 480 | ||
| 481 | void gmap_discard(unsigned long from, unsigned long to, struct gmap *gmap) | ||
| 482 | { | ||
| 483 | |||
| 484 | unsigned long *table, address, size; | ||
| 485 | struct vm_area_struct *vma; | ||
| 486 | struct gmap_pgtable *mp; | ||
| 487 | struct page *page; | ||
| 488 | |||
| 489 | down_read(&gmap->mm->mmap_sem); | ||
| 490 | address = from; | ||
| 491 | while (address < to) { | ||
| 492 | /* Walk the gmap address space page table */ | ||
| 493 | table = gmap->table + ((address >> 53) & 0x7ff); | ||
| 494 | if (unlikely(*table & _REGION_ENTRY_INV)) { | ||
| 495 | address = (address + PMD_SIZE) & PMD_MASK; | ||
| 496 | continue; | ||
| 497 | } | ||
| 498 | table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN); | ||
| 499 | table = table + ((address >> 42) & 0x7ff); | ||
| 500 | if (unlikely(*table & _REGION_ENTRY_INV)) { | ||
| 501 | address = (address + PMD_SIZE) & PMD_MASK; | ||
| 502 | continue; | ||
| 503 | } | ||
| 504 | table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN); | ||
| 505 | table = table + ((address >> 31) & 0x7ff); | ||
| 506 | if (unlikely(*table & _REGION_ENTRY_INV)) { | ||
| 507 | address = (address + PMD_SIZE) & PMD_MASK; | ||
| 508 | continue; | ||
| 509 | } | ||
| 510 | table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN); | ||
| 511 | table = table + ((address >> 20) & 0x7ff); | ||
| 512 | if (unlikely(*table & _SEGMENT_ENTRY_INV)) { | ||
| 513 | address = (address + PMD_SIZE) & PMD_MASK; | ||
| 514 | continue; | ||
| 515 | } | ||
| 516 | page = pfn_to_page(*table >> PAGE_SHIFT); | ||
| 517 | mp = (struct gmap_pgtable *) page->index; | ||
| 518 | vma = find_vma(gmap->mm, mp->vmaddr); | ||
| 519 | size = min(to - address, PMD_SIZE - (address & ~PMD_MASK)); | ||
| 520 | zap_page_range(vma, mp->vmaddr | (address & ~PMD_MASK), | ||
| 521 | size, NULL); | ||
| 522 | address = (address + PMD_SIZE) & PMD_MASK; | ||
| 523 | } | ||
| 524 | up_read(&gmap->mm->mmap_sem); | ||
| 525 | } | ||
| 526 | EXPORT_SYMBOL_GPL(gmap_discard); | ||
| 527 | |||
| 481 | void gmap_unmap_notifier(struct mm_struct *mm, unsigned long *table) | 528 | void gmap_unmap_notifier(struct mm_struct *mm, unsigned long *table) |
| 482 | { | 529 | { |
| 483 | struct gmap_rmap *rmap, *next; | 530 | struct gmap_rmap *rmap, *next; |
