Commit c6c956b8 authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Christian Borntraeger

KVM: s390/mm: support gmap page tables with less than 5 levels

Add an addressing limit to the gmap address spaces and only allocate
the page table levels that are needed for the given limit. The limit
is fixed and can not be changed after a gmap has been created.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
parent 527e30b4
...@@ -808,6 +808,7 @@ struct gmap { ...@@ -808,6 +808,7 @@ struct gmap {
spinlock_t guest_table_lock; spinlock_t guest_table_lock;
unsigned long *table; unsigned long *table;
unsigned long asce; unsigned long asce;
unsigned long asce_end;
void *private; void *private;
bool pfault_enabled; bool pfault_enabled;
}; };
...@@ -844,7 +845,7 @@ struct gmap_notifier { ...@@ -844,7 +845,7 @@ struct gmap_notifier {
void (*notifier_call)(struct gmap *gmap, unsigned long gaddr); void (*notifier_call)(struct gmap *gmap, unsigned long gaddr);
}; };
struct gmap *gmap_alloc(struct mm_struct *mm); struct gmap *gmap_alloc(struct mm_struct *mm, unsigned long limit);
void gmap_free(struct gmap *gmap); void gmap_free(struct gmap *gmap);
void gmap_enable(struct gmap *gmap); void gmap_enable(struct gmap *gmap);
void gmap_disable(struct gmap *gmap); void gmap_disable(struct gmap *gmap);
......
...@@ -451,7 +451,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) ...@@ -451,7 +451,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
if (type & KVM_VM_S390_UCONTROL) { if (type & KVM_VM_S390_UCONTROL) {
kvm->arch.gmap = NULL; kvm->arch.gmap = NULL;
} else { } else {
kvm->arch.gmap = gmap_alloc(current->mm); kvm->arch.gmap = gmap_alloc(current->mm, -1UL);
if (!kvm->arch.gmap) if (!kvm->arch.gmap)
goto out_nogmap; goto out_nogmap;
kvm->arch.gmap->private = kvm; kvm->arch.gmap->private = kvm;
...@@ -535,7 +535,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) ...@@ -535,7 +535,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID; vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
kvm_clear_async_pf_completion_queue(vcpu); kvm_clear_async_pf_completion_queue(vcpu);
if (kvm_is_ucontrol(vcpu->kvm)) { if (kvm_is_ucontrol(vcpu->kvm)) {
vcpu->arch.gmap = gmap_alloc(current->mm); vcpu->arch.gmap = gmap_alloc(current->mm, -1UL);
if (!vcpu->arch.gmap) if (!vcpu->arch.gmap)
return -ENOMEM; return -ENOMEM;
vcpu->arch.gmap->private = vcpu->kvm; vcpu->arch.gmap->private = vcpu->kvm;
......
...@@ -145,15 +145,34 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit) ...@@ -145,15 +145,34 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
/** /**
* gmap_alloc - allocate a guest address space * gmap_alloc - allocate a guest address space
* @mm: pointer to the parent mm_struct * @mm: pointer to the parent mm_struct
* @limit: maximum size of the gmap address space
* *
* Returns a guest address space structure. * Returns a guest address space structure.
*/ */
struct gmap *gmap_alloc(struct mm_struct *mm) struct gmap *gmap_alloc(struct mm_struct *mm, unsigned long limit)
{ {
struct gmap *gmap; struct gmap *gmap;
struct page *page; struct page *page;
unsigned long *table; unsigned long *table;
unsigned long etype, atype;
if (limit < (1UL << 31)) {
limit = (1UL << 31) - 1;
atype = _ASCE_TYPE_SEGMENT;
etype = _SEGMENT_ENTRY_EMPTY;
} else if (limit < (1UL << 42)) {
limit = (1UL << 42) - 1;
atype = _ASCE_TYPE_REGION3;
etype = _REGION3_ENTRY_EMPTY;
} else if (limit < (1UL << 53)) {
limit = (1UL << 53) - 1;
atype = _ASCE_TYPE_REGION2;
etype = _REGION2_ENTRY_EMPTY;
} else {
limit = -1UL;
atype = _ASCE_TYPE_REGION1;
etype = _REGION1_ENTRY_EMPTY;
}
gmap = kzalloc(sizeof(struct gmap), GFP_KERNEL); gmap = kzalloc(sizeof(struct gmap), GFP_KERNEL);
if (!gmap) if (!gmap)
goto out; goto out;
...@@ -168,10 +187,11 @@ struct gmap *gmap_alloc(struct mm_struct *mm) ...@@ -168,10 +187,11 @@ struct gmap *gmap_alloc(struct mm_struct *mm)
page->index = 0; page->index = 0;
list_add(&page->lru, &gmap->crst_list); list_add(&page->lru, &gmap->crst_list);
table = (unsigned long *) page_to_phys(page); table = (unsigned long *) page_to_phys(page);
crst_table_init(table, _REGION1_ENTRY_EMPTY); crst_table_init(table, etype);
gmap->table = table; gmap->table = table;
gmap->asce = _ASCE_TYPE_REGION1 | _ASCE_TABLE_LENGTH | gmap->asce = atype | _ASCE_TABLE_LENGTH |
_ASCE_USER_BITS | __pa(table); _ASCE_USER_BITS | __pa(table);
gmap->asce_end = limit;
down_write(&mm->mmap_sem); down_write(&mm->mmap_sem);
list_add(&gmap->list, &mm->context.gmap_list); list_add(&gmap->list, &mm->context.gmap_list);
up_write(&mm->mmap_sem); up_write(&mm->mmap_sem);
...@@ -187,8 +207,7 @@ EXPORT_SYMBOL_GPL(gmap_alloc); ...@@ -187,8 +207,7 @@ EXPORT_SYMBOL_GPL(gmap_alloc);
static void gmap_flush_tlb(struct gmap *gmap) static void gmap_flush_tlb(struct gmap *gmap)
{ {
if (MACHINE_HAS_IDTE) if (MACHINE_HAS_IDTE)
__tlb_flush_asce(gmap->mm, (unsigned long) gmap->table | __tlb_flush_asce(gmap->mm, gmap->asce);
_ASCE_TYPE_REGION1);
else else
__tlb_flush_global(); __tlb_flush_global();
} }
...@@ -227,8 +246,7 @@ void gmap_free(struct gmap *gmap) ...@@ -227,8 +246,7 @@ void gmap_free(struct gmap *gmap)
/* Flush tlb. */ /* Flush tlb. */
if (MACHINE_HAS_IDTE) if (MACHINE_HAS_IDTE)
__tlb_flush_asce(gmap->mm, (unsigned long) gmap->table | __tlb_flush_asce(gmap->mm, gmap->asce);
_ASCE_TYPE_REGION1);
else else
__tlb_flush_global(); __tlb_flush_global();
...@@ -394,8 +412,8 @@ int gmap_map_segment(struct gmap *gmap, unsigned long from, ...@@ -394,8 +412,8 @@ int gmap_map_segment(struct gmap *gmap, unsigned long from,
if ((from | to | len) & (PMD_SIZE - 1)) if ((from | to | len) & (PMD_SIZE - 1))
return -EINVAL; return -EINVAL;
if (len == 0 || from + len > TASK_MAX_SIZE || if (len == 0 || from + len < from || to + len < to ||
from + len < from || to + len < to) from + len > TASK_MAX_SIZE || to + len > gmap->asce_end)
return -EINVAL; return -EINVAL;
flush = 0; flush = 0;
...@@ -501,25 +519,32 @@ int __gmap_link(struct gmap *gmap, unsigned long gaddr, unsigned long vmaddr) ...@@ -501,25 +519,32 @@ int __gmap_link(struct gmap *gmap, unsigned long gaddr, unsigned long vmaddr)
int rc; int rc;
/* Create higher level tables in the gmap page table */ /* Create higher level tables in the gmap page table */
table = gmap->table + ((gaddr >> 53) & 0x7ff); table = gmap->table;
if ((*table & _REGION_ENTRY_INVALID) && if ((gmap->asce & _ASCE_TYPE_MASK) >= _ASCE_TYPE_REGION1) {
gmap_alloc_table(gmap, table, _REGION2_ENTRY_EMPTY, table += (gaddr >> 53) & 0x7ff;
gaddr & 0xffe0000000000000)) if ((*table & _REGION_ENTRY_INVALID) &&
return -ENOMEM; gmap_alloc_table(gmap, table, _REGION2_ENTRY_EMPTY,
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN); gaddr & 0xffe0000000000000))
table = table + ((gaddr >> 42) & 0x7ff); return -ENOMEM;
if ((*table & _REGION_ENTRY_INVALID) && table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
gmap_alloc_table(gmap, table, _REGION3_ENTRY_EMPTY, }
gaddr & 0xfffffc0000000000)) if ((gmap->asce & _ASCE_TYPE_MASK) >= _ASCE_TYPE_REGION2) {
return -ENOMEM; table += (gaddr >> 42) & 0x7ff;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN); if ((*table & _REGION_ENTRY_INVALID) &&
table = table + ((gaddr >> 31) & 0x7ff); gmap_alloc_table(gmap, table, _REGION3_ENTRY_EMPTY,
if ((*table & _REGION_ENTRY_INVALID) && gaddr & 0xfffffc0000000000))
gmap_alloc_table(gmap, table, _SEGMENT_ENTRY_EMPTY, return -ENOMEM;
gaddr & 0xffffffff80000000)) table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
return -ENOMEM; }
table = (unsigned long *) (*table & _REGION_ENTRY_ORIGIN); if ((gmap->asce & _ASCE_TYPE_MASK) >= _ASCE_TYPE_REGION3) {
table = table + ((gaddr >> 20) & 0x7ff); table += (gaddr >> 31) & 0x7ff;
if ((*table & _REGION_ENTRY_INVALID) &&
gmap_alloc_table(gmap, table, _SEGMENT_ENTRY_EMPTY,
gaddr & 0xffffffff80000000))
return -ENOMEM;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
}
table += (gaddr >> 20) & 0x7ff;
/* Walk the parent mm page table */ /* Walk the parent mm page table */
mm = gmap->mm; mm = gmap->mm;
pgd = pgd_offset(mm, vmaddr); pgd = pgd_offset(mm, vmaddr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment