From 729f884cb7ca962300740ede2690833181fdbbe6 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 26 Jul 2017 20:17:24 +1000 Subject: mmu: implement base for new vm management Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/include/nvkm/core/os.h | 1 + drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h | 12 +++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild | 2 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c | 49 ++++++---- drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h | 5 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c | 112 ++++++++++++++++++++++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h | 27 ++++++ 7 files changed, 188 insertions(+), 20 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/os.h b/drivers/gpu/drm/nouveau/include/nvkm/core/os.h index cd57e238ddd3..cfd9ed361c44 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/os.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/os.h @@ -1,4 +1,5 @@ #ifndef __NVKM_OS_H__ #define __NVKM_OS_H__ #include +#define nvkm_vmm nvkm_vm #endif diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h index 154b20abaf4f..53ffc93d20d6 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h @@ -25,6 +25,7 @@ struct nvkm_vma { }; struct nvkm_vm { + const struct nvkm_vmm_func *func; struct nvkm_mmu *mmu; struct mutex mutex; @@ -38,6 +39,10 @@ struct nvkm_vm { u32 fpde; u32 lpde; + struct kref kref; + u64 start; + u64 limit; + bool bootstrapped; }; @@ -53,6 +58,11 @@ void nvkm_vm_map_at(struct nvkm_vma *, u64 offset, struct nvkm_mem *); void nvkm_vm_unmap(struct nvkm_vma *); void nvkm_vm_unmap_at(struct nvkm_vma *, u64 offset, u64 length); +int nvkm_vmm_new(struct nvkm_mmu *, u64 addr, u64 size, void *argv, u32 argc, + struct lock_class_key *, struct nvkm_vmm **); +struct nvkm_vmm *nvkm_vmm_ref(struct nvkm_vmm *); +void nvkm_vmm_unref(struct nvkm_vmm **); + struct nvkm_mmu { const struct nvkm_mmu_func *func; struct nvkm_subdev subdev; @@ -60,6 +70,8 @@ struct nvkm_mmu { u64 limit; u8 dma_bits; u8 lpg_shift; + + struct nvkm_vmm *vmm; }; int nv04_mmu_new(struct nvkm_device *, int, struct nvkm_mmu **); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild index 3f13560658df..a2e27244dbed 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild @@ -6,3 +6,5 @@ nvkm-y += nvkm/subdev/mmu/nv50.o nvkm-y += nvkm/subdev/mmu/g84.o nvkm-y += nvkm/subdev/mmu/gf100.o nvkm-y += nvkm/subdev/mmu/gp100.o + +nvkm-y += nvkm/subdev/mmu/vmm.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c index 37f45ade7ef1..b6ea293cbe8e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ #include "priv.h" +#include "vmm.h" #include #include @@ -365,32 +366,18 @@ int nvkm_vm_create(struct nvkm_mmu *mmu, u64 offset, u64 length, u64 mm_offset, u32 block, struct lock_class_key *key, struct nvkm_vm **pvm) { - static struct lock_class_key _key; + struct nvkm_vmm_func func = { .page_block = block }; struct nvkm_vm *vm; - u64 mm_length = (offset + length) - mm_offset; int ret; vm = kzalloc(sizeof(*vm), GFP_KERNEL); if (!vm) return -ENOMEM; - __mutex_init(&vm->mutex, "&vm->mutex", key ? key : &_key); - INIT_LIST_HEAD(&vm->pgd_list); - vm->mmu = mmu; - kref_init(&vm->refcount); - vm->fpde = offset >> (mmu->func->pgt_bits + 12); - vm->lpde = (offset + length - 1) >> (mmu->func->pgt_bits + 12); - - vm->pgt = vzalloc((vm->lpde - vm->fpde + 1) * sizeof(*vm->pgt)); - if (!vm->pgt) { - kfree(vm); - return -ENOMEM; - } - - ret = nvkm_mm_init(&vm->mm, mm_offset >> 12, mm_length >> 12, - block >> 12); + ret = nvkm_vmm_ctor(&func, mmu, order_base_2(mmu->limit), + mm_offset, offset + length - mm_offset, key, vm); + vm->func = NULL; if (ret) { - vfree(vm->pgt); kfree(vm); return ret; } @@ -405,6 +392,20 @@ nvkm_vm_new(struct nvkm_device *device, u64 offset, u64 length, u64 mm_offset, struct lock_class_key *key, struct nvkm_vm **pvm) { struct nvkm_mmu *mmu = device->mmu; + + *pvm = NULL; + if (mmu->func->uvmm) { + const struct nvkm_vmm_user *uvmm; + int ret; + + mmu->func->uvmm(mmu, 0, &uvmm); + ret = uvmm->ctor(mmu, mm_offset, offset + length - mm_offset, + NULL, 0, key, pvm); + if (ret) + nvkm_vm_ref(NULL, pvm, NULL); + return ret; + } + if (!mmu->func->create) return -EINVAL; return mmu->func->create(mmu, offset, length, mm_offset, key, pvm); @@ -463,8 +464,7 @@ nvkm_vm_del(struct kref *kref) nvkm_vm_unlink(vm, vpgd->obj); } - nvkm_mm_fini(&vm->mm); - vfree(vm->pgt); + vm = nvkm_vmm_dtor(vm); kfree(vm); } @@ -494,8 +494,16 @@ static int nvkm_mmu_oneinit(struct nvkm_subdev *subdev) { struct nvkm_mmu *mmu = nvkm_mmu(subdev); + + if (mmu->func->vmm_global) { + int ret = nvkm_vmm_new(mmu, 0, 0, NULL, 0, NULL, &mmu->vmm); + if (ret) + return ret; + } + if (mmu->func->oneinit) return mmu->func->oneinit(mmu); + return 0; } @@ -512,6 +520,7 @@ static void * nvkm_mmu_dtor(struct nvkm_subdev *subdev) { struct nvkm_mmu *mmu = nvkm_mmu(subdev); + nvkm_vmm_unref(&mmu->vmm); if (mmu->func->dtor) return mmu->func->dtor(mmu); return mmu; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h index 27cedc60b507..f36b74903427 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h @@ -2,6 +2,7 @@ #define __NVKM_MMU_PRIV_H__ #define nvkm_mmu(p) container_of((p), struct nvkm_mmu, subdev) #include +struct nvkm_vmm_user; void nvkm_mmu_ctor(const struct nvkm_mmu_func *, struct nvkm_device *, int index, struct nvkm_mmu *); @@ -32,6 +33,10 @@ struct nvkm_mmu_func { void (*unmap)(struct nvkm_vma *, struct nvkm_memory *pgt, u32 pte, u32 cnt); void (*flush)(struct nvkm_vm *); + + int (*uvmm)(struct nvkm_mmu *, int, const struct nvkm_vmm_user **); + + bool vmm_global; }; int nvkm_vm_create(struct nvkm_mmu *, u64, u64, u64, u32, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c new file mode 100644 index 000000000000..8fc30e7303b8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c @@ -0,0 +1,112 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +void * +nvkm_vmm_dtor(struct nvkm_vmm *vmm) +{ + void *data = vmm; + if (vmm->func) + data = vmm->func->dtor(vmm); + nvkm_mm_fini(&vmm->mm); + vfree(vmm->pgt); + return data; +} + +int +nvkm_vmm_ctor(const struct nvkm_vmm_func *func, struct nvkm_mmu *mmu, + int vma_bits, u64 addr, u64 size, struct lock_class_key *key, + struct nvkm_vmm *vmm) +{ + static struct lock_class_key _key; + int ret; + + vmm->func = func; + vmm->mmu = mmu; + kref_init(&vmm->kref); + + vmm->start = addr; + vmm->limit = size ? (addr + size) : (1ULL << vma_bits);; + if (vmm->start > vmm->limit || --vmm->limit >= 1ULL << vma_bits) + return -EINVAL; + + __mutex_init(&vmm->mutex, "&vmm->mutex", key ? key : &_key); + INIT_LIST_HEAD(&vmm->pgd_list); + kref_init(&vmm->refcount); + vmm->fpde = vmm->start >> (mmu->func->pgt_bits + 12); + vmm->lpde = vmm->limit >> (mmu->func->pgt_bits + 12); + + vmm->pgt = vzalloc((vmm->lpde - vmm->fpde + 1) * sizeof(*vmm->pgt)); + if (!vmm->pgt) + return -ENOMEM; + + ret = nvkm_mm_init(&vmm->mm, vmm->start >> 12, + (vmm->limit - vmm->start + 1) >> 12, + func->page_block ? func->page_block >> 12 : 1); + if (ret) { + vfree(vmm->pgt); + return ret; + } + + return 0; +} + +static void +nvkm_vmm_del(struct kref *kref) +{ + struct nvkm_vmm *vmm = container_of(kref, typeof(*vmm), kref); + vmm = nvkm_vmm_dtor(vmm); + kfree(vmm); +} + +void +nvkm_vmm_unref(struct nvkm_vmm **pvmm) +{ + struct nvkm_vmm *vmm = *pvmm; + if (vmm) { + kref_put(&vmm->kref, nvkm_vmm_del); + *pvmm = NULL; + } +} + +struct nvkm_vmm * +nvkm_vmm_ref(struct nvkm_vmm *vmm) +{ + if (vmm) + kref_get(&vmm->kref); + return vmm; +} + +int +nvkm_vmm_new(struct nvkm_mmu *mmu, u64 addr, u64 size, void *argv, u32 argc, + struct lock_class_key *key, struct nvkm_vmm **pvmm) +{ + const struct nvkm_vmm_user *uvmm = NULL; + struct nvkm_vmm *vmm = NULL; + int ret; + mmu->func->uvmm(mmu, 0, &uvmm); + ret = uvmm->ctor(mmu, addr, size, argv, argc, key, &vmm); + if (ret) + nvkm_vmm_unref(&vmm); + *pvmm = vmm; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h new file mode 100644 index 000000000000..3222acacb4b3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h @@ -0,0 +1,27 @@ +#ifndef __NVKM_VMM_H__ +#define __NVKM_VMM_H__ +#include "priv.h" + +struct nvkm_vmm_page { + u8 shift; +#define NVKM_VMM_PAGE_COMP 0x01 + u8 type; +}; + +struct nvkm_vmm_func { + void *(*dtor)(struct nvkm_vmm *); + + const struct nvkm_vmm_page *(*page)(struct nvkm_vmm *); + u64 page_block; +}; + +int nvkm_vmm_ctor(const struct nvkm_vmm_func *, struct nvkm_mmu *, + int vma_bits, u64 addr, u64 size, struct lock_class_key *, + struct nvkm_vmm *vmm); +void *nvkm_vmm_dtor(struct nvkm_vmm *); + +struct nvkm_vmm_user { + int (*ctor)(struct nvkm_mmu *, u64 addr, u64 size, void *args, u32 argc, + struct lock_class_key *, struct nvkm_vmm **); +}; +#endif -- cgit v1.2.3