提交 527e30b4 编写于 作者: M Martin Schwidefsky 提交者: Christian Borntraeger

KVM: s390/mm: use radix trees for guest to host mappings

Store the target address for the gmap segments in a radix tree
instead of using invalid segment table entries. gmap_translate
becomes a simple radix_tree_lookup, gmap_fault is split into the
address translation with gmap_translate and the part that does
the linking of the gmap shadow page table with the process page
table.
A second radix tree is used to keep the pointers to the segment
table entries for segments that are mapped in the guest address
space. On unmap of a segment the pointer is retrieved from the
radix tree and is used to carry out the segment invalidation in
the gmap shadow page table. As the radix tree can only store one
pointer, each host segment may only be mapped to exactly one
guest location.
Signed-off-by: NMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: NChristian Borntraeger <borntraeger@de.ibm.com>
上级 6e0a0431
...@@ -18,9 +18,9 @@ ...@@ -18,9 +18,9 @@
unsigned long *crst_table_alloc(struct mm_struct *); unsigned long *crst_table_alloc(struct mm_struct *);
void crst_table_free(struct mm_struct *, unsigned long *); void crst_table_free(struct mm_struct *, unsigned long *);
unsigned long *page_table_alloc(struct mm_struct *, unsigned long); unsigned long *page_table_alloc(struct mm_struct *);
void page_table_free(struct mm_struct *, unsigned long *); void page_table_free(struct mm_struct *, unsigned long *);
void page_table_free_rcu(struct mmu_gather *, unsigned long *); void page_table_free_rcu(struct mmu_gather *, unsigned long *, unsigned long);
void page_table_reset_pgste(struct mm_struct *, unsigned long, unsigned long, void page_table_reset_pgste(struct mm_struct *, unsigned long, unsigned long,
bool init_skey); bool init_skey);
...@@ -145,8 +145,8 @@ static inline void pmd_populate(struct mm_struct *mm, ...@@ -145,8 +145,8 @@ static inline void pmd_populate(struct mm_struct *mm,
/* /*
* page table entry allocation/free routines. * page table entry allocation/free routines.
*/ */
#define pte_alloc_one_kernel(mm, vmaddr) ((pte_t *) page_table_alloc(mm, vmaddr)) #define pte_alloc_one_kernel(mm, vmaddr) ((pte_t *) page_table_alloc(mm))
#define pte_alloc_one(mm, vmaddr) ((pte_t *) page_table_alloc(mm, vmaddr)) #define pte_alloc_one(mm, vmaddr) ((pte_t *) page_table_alloc(mm))
#define pte_free_kernel(mm, pte) page_table_free(mm, (unsigned long *) pte) #define pte_free_kernel(mm, pte) page_table_free(mm, (unsigned long *) pte)
#define pte_free(mm, pte) page_table_free(mm, (unsigned long *) pte) #define pte_free(mm, pte) page_table_free(mm, (unsigned long *) pte)
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/mm_types.h> #include <linux/mm_types.h>
#include <linux/page-flags.h> #include <linux/page-flags.h>
#include <linux/radix-tree.h>
#include <asm/bug.h> #include <asm/bug.h>
#include <asm/page.h> #include <asm/page.h>
...@@ -789,19 +790,25 @@ static inline pgste_t pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry) ...@@ -789,19 +790,25 @@ static inline pgste_t pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry)
/** /**
* struct gmap_struct - guest address space * struct gmap_struct - guest address space
* @crst_list: list of all crst tables used in the guest address space
* @mm: pointer to the parent mm_struct * @mm: pointer to the parent mm_struct
* @guest_to_host: radix tree with guest to host address translation
* @host_to_guest: radix tree with pointer to segment table entries
* @guest_table_lock: spinlock to protect all entries in the guest page table
* @table: pointer to the page directory * @table: pointer to the page directory
* @asce: address space control element for gmap page table * @asce: address space control element for gmap page table
* @crst_list: list of all crst tables used in the guest address space
* @pfault_enabled: defines if pfaults are applicable for the guest * @pfault_enabled: defines if pfaults are applicable for the guest
*/ */
struct gmap { struct gmap {
struct list_head list; struct list_head list;
struct list_head crst_list;
struct mm_struct *mm; struct mm_struct *mm;
struct radix_tree_root guest_to_host;
struct radix_tree_root host_to_guest;
spinlock_t guest_table_lock;
unsigned long *table; unsigned long *table;
unsigned long asce; unsigned long asce;
void *private; void *private;
struct list_head crst_list;
bool pfault_enabled; bool pfault_enabled;
}; };
...@@ -846,8 +853,8 @@ int gmap_map_segment(struct gmap *gmap, unsigned long from, ...@@ -846,8 +853,8 @@ int gmap_map_segment(struct gmap *gmap, unsigned long from,
int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len); int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len);
unsigned long __gmap_translate(struct gmap *, unsigned long gaddr); unsigned long __gmap_translate(struct gmap *, unsigned long gaddr);
unsigned long gmap_translate(struct gmap *, unsigned long gaddr); unsigned long gmap_translate(struct gmap *, unsigned long gaddr);
unsigned long __gmap_fault(struct gmap *, unsigned long gaddr); int __gmap_link(struct gmap *gmap, unsigned long gaddr, unsigned long vmaddr);
unsigned long gmap_fault(struct gmap *, unsigned long gaddr); int gmap_fault(struct gmap *, unsigned long gaddr, unsigned int fault_flags);
void gmap_discard(struct gmap *, unsigned long from, unsigned long to); void gmap_discard(struct gmap *, unsigned long from, unsigned long to);
void __gmap_zap(struct gmap *, unsigned long gaddr); void __gmap_zap(struct gmap *, unsigned long gaddr);
bool gmap_test_and_clear_dirty(unsigned long address, struct gmap *); bool gmap_test_and_clear_dirty(unsigned long address, struct gmap *);
......
...@@ -105,7 +105,7 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) ...@@ -105,7 +105,7 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
unsigned long address) unsigned long address)
{ {
page_table_free_rcu(tlb, (unsigned long *) pte); page_table_free_rcu(tlb, (unsigned long *) pte, address);
} }
/* /*
......
...@@ -1092,18 +1092,8 @@ static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu) ...@@ -1092,18 +1092,8 @@ static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
*/ */
long kvm_arch_fault_in_page(struct kvm_vcpu *vcpu, gpa_t gpa, int writable) long kvm_arch_fault_in_page(struct kvm_vcpu *vcpu, gpa_t gpa, int writable)
{ {
struct mm_struct *mm = current->mm; return gmap_fault(vcpu->arch.gmap, gpa,
hva_t hva; writable ? FAULT_FLAG_WRITE : 0);
long rc;
hva = gmap_fault(vcpu->arch.gmap, gpa);
if (IS_ERR_VALUE(hva))
return (long)hva;
down_read(&mm->mmap_sem);
rc = get_user_pages(current, mm, hva, 1, writable, 0, NULL, NULL);
up_read(&mm->mmap_sem);
return rc < 0 ? rc : 0;
} }
static void __kvm_inject_pfault_token(struct kvm_vcpu *vcpu, bool start_token, static void __kvm_inject_pfault_token(struct kvm_vcpu *vcpu, bool start_token,
...@@ -1683,9 +1673,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, ...@@ -1683,9 +1673,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
} }
#endif #endif
case KVM_S390_VCPU_FAULT: { case KVM_S390_VCPU_FAULT: {
r = gmap_fault(vcpu->arch.gmap, arg); r = gmap_fault(vcpu->arch.gmap, arg, 0);
if (!IS_ERR_VALUE(r))
r = 0;
break; break;
} }
case KVM_ENABLE_CAP: case KVM_ENABLE_CAP:
......
...@@ -442,18 +442,15 @@ static inline int do_exception(struct pt_regs *regs, int access) ...@@ -442,18 +442,15 @@ static inline int do_exception(struct pt_regs *regs, int access)
down_read(&mm->mmap_sem); down_read(&mm->mmap_sem);
#ifdef CONFIG_PGSTE #ifdef CONFIG_PGSTE
gmap = (struct gmap *) gmap = (current->flags & PF_VCPU) ?
((current->flags & PF_VCPU) ? S390_lowcore.gmap : 0); (struct gmap *) S390_lowcore.gmap : NULL;
if (gmap) { if (gmap) {
address = __gmap_fault(gmap, address); current->thread.gmap_addr = address;
address = __gmap_translate(gmap, address);
if (address == -EFAULT) { if (address == -EFAULT) {
fault = VM_FAULT_BADMAP; fault = VM_FAULT_BADMAP;
goto out_up; goto out_up;
} }
if (address == -ENOMEM) {
fault = VM_FAULT_OOM;
goto out_up;
}
if (gmap->pfault_enabled) if (gmap->pfault_enabled)
flags |= FAULT_FLAG_RETRY_NOWAIT; flags |= FAULT_FLAG_RETRY_NOWAIT;
} }
...@@ -530,6 +527,20 @@ static inline int do_exception(struct pt_regs *regs, int access) ...@@ -530,6 +527,20 @@ static inline int do_exception(struct pt_regs *regs, int access)
goto retry; goto retry;
} }
} }
#ifdef CONFIG_PGSTE
if (gmap) {
address = __gmap_link(gmap, current->thread.gmap_addr,
address);
if (address == -EFAULT) {
fault = VM_FAULT_BADMAP;
goto out_up;
}
if (address == -ENOMEM) {
fault = VM_FAULT_OOM;
goto out_up;
}
}
#endif
fault = 0; fault = 0;
out_up: out_up:
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
......
此差异已折叠。
...@@ -65,7 +65,7 @@ static pte_t __ref *vmem_pte_alloc(unsigned long address) ...@@ -65,7 +65,7 @@ static pte_t __ref *vmem_pte_alloc(unsigned long address)
pte_t *pte; pte_t *pte;
if (slab_is_available()) if (slab_is_available())
pte = (pte_t *) page_table_alloc(&init_mm, address); pte = (pte_t *) page_table_alloc(&init_mm);
else else
pte = alloc_bootmem_align(PTRS_PER_PTE * sizeof(pte_t), pte = alloc_bootmem_align(PTRS_PER_PTE * sizeof(pte_t),
PTRS_PER_PTE * sizeof(pte_t)); PTRS_PER_PTE * sizeof(pte_t));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册