Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: s390: fix mismatch between user and in-kernel guest limit

While the userspace interface requests the maximum size the gmap code
expects to get a maximum address.

This error resulted in bigger page tables than necessary for some guest
sizes, e.g. a 2GB guest used 3 levels instead of 2.

At the same time we introduce KVM_S390_NO_MEM_LIMIT, which allows in a
bright future that a guest spans the complete 64 bit address space.

We also switch to TASK_MAX_SIZE for the initial memory size, this is a
cosmetic change as the previous size also resulted in a 4 level pagetable
creation.

Reported-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Dominik Dingel <dingel@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>

authored by

Dominik Dingel and committed by
Christian Borntraeger
a3a92c31 8335713a

+27 -8
+2 -1
Documentation/virtual/kvm/devices/vm.txt
··· 37 37 Allows userspace to query the actual limit and set a new limit for 38 38 the maximum guest memory size. The limit will be rounded up to 39 39 2048 MB, 4096 GB, 8192 TB respectively, as this limit is governed by 40 - the number of page table levels. 40 + the number of page table levels. In the case that there is no limit we will set 41 + the limit to KVM_S390_NO_MEM_LIMIT (U64_MAX). 41 42 42 43 2. GROUP: KVM_S390_VM_CPU_MODEL 43 44 Architectures: s390
+1
arch/s390/include/asm/kvm_host.h
··· 627 627 struct kvm_s390_float_interrupt float_int; 628 628 struct kvm_device *flic; 629 629 struct gmap *gmap; 630 + unsigned long mem_limit; 630 631 int css_support; 631 632 int use_irqchip; 632 633 int use_cmma;
+2
arch/s390/include/uapi/asm/kvm.h
··· 66 66 #define KVM_S390_VM_MEM_CLR_CMMA 1 67 67 #define KVM_S390_VM_MEM_LIMIT_SIZE 2 68 68 69 + #define KVM_S390_NO_MEM_LIMIT U64_MAX 70 + 69 71 /* kvm attributes for KVM_S390_VM_TOD */ 70 72 #define KVM_S390_VM_TOD_LOW 0 71 73 #define KVM_S390_VM_TOD_HIGH 1
+20 -5
arch/s390/kvm/kvm-s390.c
··· 378 378 case KVM_S390_VM_MEM_LIMIT_SIZE: 379 379 ret = 0; 380 380 VM_EVENT(kvm, 3, "QUERY: max guest memory: %lu bytes", 381 - kvm->arch.gmap->asce_end); 382 - if (put_user(kvm->arch.gmap->asce_end, (u64 __user *)attr->addr)) 381 + kvm->arch.mem_limit); 382 + if (put_user(kvm->arch.mem_limit, (u64 __user *)attr->addr)) 383 383 ret = -EFAULT; 384 384 break; 385 385 default: ··· 431 431 if (get_user(new_limit, (u64 __user *)attr->addr)) 432 432 return -EFAULT; 433 433 434 - if (new_limit > kvm->arch.gmap->asce_end) 434 + if (kvm->arch.mem_limit != KVM_S390_NO_MEM_LIMIT && 435 + new_limit > kvm->arch.mem_limit) 435 436 return -E2BIG; 437 + 438 + if (!new_limit) 439 + return -EINVAL; 440 + 441 + /* gmap_alloc takes last usable address */ 442 + if (new_limit != KVM_S390_NO_MEM_LIMIT) 443 + new_limit -= 1; 436 444 437 445 ret = -EBUSY; 438 446 mutex_lock(&kvm->lock); ··· 458 450 } 459 451 } 460 452 mutex_unlock(&kvm->lock); 461 - VM_EVENT(kvm, 3, "SET: max guest memory: %lu bytes", new_limit); 453 + VM_EVENT(kvm, 3, "SET: max guest address: %lu", new_limit); 454 + VM_EVENT(kvm, 3, "New guest asce: 0x%pK", 455 + (void *) kvm->arch.gmap->asce); 462 456 break; 463 457 } 464 458 default: ··· 1182 1172 1183 1173 if (type & KVM_VM_S390_UCONTROL) { 1184 1174 kvm->arch.gmap = NULL; 1175 + kvm->arch.mem_limit = KVM_S390_NO_MEM_LIMIT; 1185 1176 } else { 1186 - kvm->arch.gmap = gmap_alloc(current->mm, (1UL << 44) - 1); 1177 + kvm->arch.mem_limit = TASK_MAX_SIZE; 1178 + kvm->arch.gmap = gmap_alloc(current->mm, kvm->arch.mem_limit - 1); 1187 1179 if (!kvm->arch.gmap) 1188 1180 goto out_err; 1189 1181 kvm->arch.gmap->private = kvm; ··· 2839 2827 return -EINVAL; 2840 2828 2841 2829 if (mem->memory_size & 0xffffful) 2830 + return -EINVAL; 2831 + 2832 + if (mem->guest_phys_addr + mem->memory_size > kvm->arch.mem_limit) 2842 2833 return -EINVAL; 2843 2834 2844 2835 return 0;
+2 -2
arch/s390/mm/pgtable.c
··· 133 133 /** 134 134 * gmap_alloc - allocate a guest address space 135 135 * @mm: pointer to the parent mm_struct 136 - * @limit: maximum size of the gmap address space 136 + * @limit: maximum address of the gmap address space 137 137 * 138 138 * Returns a guest address space structure. 139 139 */ ··· 402 402 if ((from | to | len) & (PMD_SIZE - 1)) 403 403 return -EINVAL; 404 404 if (len == 0 || from + len < from || to + len < to || 405 - from + len > TASK_MAX_SIZE || to + len > gmap->asce_end) 405 + from + len - 1 > TASK_MAX_SIZE || to + len - 1 > gmap->asce_end) 406 406 return -EINVAL; 407 407 408 408 flush = 0;