From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH] VHM: add hugetlb page ept mapping support unlike cma, hugetlb allocates hugepage under user space, so VHM only need take care of ept mapping for these allocated huge pages. this patch add hugepage_map_guest function, it gets huge page struct pointer according to user virtual address input from ioctl IC_SET_MEMSEG, then build all required parameters for recording guest memseg and mapping ept entry through this page struct. Change-Id: I0b333613dc20fce41b9b091c72892bbac6b07735 Signed-off-by: Jason Chen CJ Reviewed-on: --- drivers/char/vhm/vhm_dev.c | 1 + drivers/vhm/Kconfig | 1 + drivers/vhm/vhm_mm.c | 101 +++++++++++++++++++++++++---- include/linux/vhm/vhm_ioctl_defs.h | 27 +++++--- include/linux/vhm/vhm_vm_mngt.h | 1 + 5 files changed, 110 insertions(+), 21 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 8590d69fa4e6..8d083a09bf06 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -129,6 +129,7 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) vm_mutex_lock(&vhm_vm_list_lock); vm->refcnt = 1; + vm->hugetlb_enabled = 0; vm_list_add(&vm->list); vm_mutex_unlock(&vhm_vm_list_lock); filep->private_data = vm; diff --git a/drivers/vhm/Kconfig b/drivers/vhm/Kconfig index e59b9487cd65..4ddb1314709a 100644 --- a/drivers/vhm/Kconfig +++ b/drivers/vhm/Kconfig @@ -3,6 +3,7 @@ config ACRN_VHM depends on ACRN depends on DMA_CMA depends on PCI_MSI + depends on HUGETLBFS depends on !VMAP_STACK default n ---help--- diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index be6a47afad9a..cc08fd9d0965 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -110,31 +110,25 @@ static bool _free_memblk(struct device *dev, u64 vm0_gpa, size_t len) return dma_release_from_contiguous(dev, page, count); } -int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) +static int add_guest_memseg(struct vhm_vm *vm, unsigned long vm0_gpa, + unsigned long guest_gpa, unsigned long len) { struct guest_memseg *seg; - u64 vm0_gpa; int max_gfn; seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); if (seg == NULL) return -ENOMEM; - vm0_gpa = _alloc_memblk(vm->dev, memseg->len); - if (vm0_gpa == 0ULL) { - kfree(seg); - return -ENOMEM; - } - seg->vm0_gpa = vm0_gpa; - seg->len = memseg->len; - seg->gpa = memseg->gpa; + seg->gpa = guest_gpa; + seg->len = len; max_gfn = (seg->gpa + seg->len) >> PAGE_SHIFT; if (vm->max_gfn < max_gfn) vm->max_gfn = max_gfn; - pr_info("VHM: alloc memseg with len=0x%lx, vm0_gpa=0x%llx," + pr_info("VHM: add memseg with len=0x%lx, vm0_gpa=0x%llx," " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", seg->len, seg->vm0_gpa, seg->gpa, vm->max_gfn); @@ -146,6 +140,22 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) return 0; } +int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) +{ + unsigned long vm0_gpa; + int ret; + + vm0_gpa = _alloc_memblk(vm->dev, memseg->len); + if (vm0_gpa == 0ULL) + return -ENOMEM; + + ret = add_guest_memseg(vm, vm0_gpa, memseg->gpa, memseg->len); + if (ret < 0) + _free_memblk(vm->dev, vm0_gpa, memseg->len); + + return ret; +} + static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right, @@ -197,6 +207,61 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, mem_type, mem_access_right, MAP_MEM); } +static int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) +{ + struct page *page; + unsigned long len, guest_gpa, vma; + unsigned int type; + unsigned int mem_type, mem_access_right; + int ret; + + if (vm == NULL || memmap == NULL) + return -EINVAL; + + len = memmap->len; + vma = memmap->vma_base; + guest_gpa = memmap->gpa; + + while (len > 0) { + unsigned long vm0_gpa, pagesize; + + ret = get_user_pages_fast(vma, 1, 1, &page); + if (unlikely(ret != 1) || (page == NULL)) { + pr_err("failed to pin huge page!\n"); + return -ENOMEM; + } + + vm0_gpa = page_to_phys(page); + pagesize = PAGE_SIZE << compound_order(page); + + ret = add_guest_memseg(vm, vm0_gpa, guest_gpa, pagesize); + if (ret < 0) { + pr_err("failed to add memseg for huge page!\n"); + put_page(page); + return ret; + } + + /* TODO: do batch hypercall for multi ept mapping */ + mem_type = MEM_TYPE_WB; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); + type = MAP_MEM; + if (_mem_set_memmap(vm->vmid, guest_gpa, vm0_gpa, pagesize, + mem_type, mem_access_right, type) < 0) { + pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); + put_page(page); + return -EFAULT; + } + + len -= pagesize; + vma += pagesize; + guest_gpa += pagesize; + } + + vm->hugetlb_enabled = 1; + + return 0; +} + int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { struct guest_memseg *seg = NULL; @@ -204,8 +269,13 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) unsigned int mem_type, mem_access_right; unsigned long guest_gpa, host_gpa; + /* hugetlb use vma to do the mapping */ + if (memmap->type == VM_SYSMEM && memmap->using_vma) + return hugepage_map_guest(vm, memmap); + mutex_lock(&vm->seg_lock); + /* cma or mmio */ if (memmap->type == VM_SYSMEM) { list_for_each_entry(seg, &vm->memseg_list, list) { if (seg->gpa == memmap->gpa @@ -249,8 +319,13 @@ void free_guest_mem(struct vhm_vm *vm) while (!list_empty(&vm->memseg_list)) { seg = list_first_entry(&vm->memseg_list, struct guest_memseg, list); - if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) - pr_warn("failed to free memblk\n"); + if (vm->hugetlb_enabled) { + /* just put_page to unpin huge page */ + put_page(pfn_to_page(seg->vm0_gpa >> PAGE_SHIFT)); + } else { + if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) + pr_warn("failed to free memblk\n"); + } list_del(&seg->list); kfree(seg); } diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 5bc7c666f2ea..a0a830dec3fa 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -117,19 +117,30 @@ struct vm_memseg { /** * struct vm_memmap - EPT memory mapping info for guest - * - * @type: memory mapping type - * @gpa: guest physical start address of memory mapping - * @hpa: host physical start address of memory - * @len: the length of memory range mapped - * @prot: memory mapping attribute */ struct vm_memmap { + /** @type: memory mapping type */ uint32_t type; - uint32_t reserved; + /** @using_vma: using vma_base to get vm0_gpa, + * only for type == VM_SYSTEM + */ + uint32_t using_vma; + /** @gpa: user OS guest physical start address of memory mapping */ uint64_t gpa; - uint64_t hpa; /* only for type == VM_MMIO */ + /** union */ + union { + /** @hpa: host physical start address of memory, + * only for type == VM_MMIO + */ + uint64_t hpa; + /** @vma_base: service OS user virtual start address of + * memory, only for type == VM_SYSMEM && using_vma == true + */ + uint64_t vma_base; + }; + /** @len: the length of memory range mapped */ uint64_t len; /* mmap length */ + /** @prot: memory mapping attribute */ uint32_t prot; /* RWX */ }; diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index e7bc8b2372f7..306bd54c4103 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -96,6 +96,7 @@ struct vhm_vm { struct list_head ioreq_client_list; struct vhm_request_buffer *req_buf; struct page *pg; + int hugetlb_enabled; }; /** -- https://clearlinux.org