summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2017-07-26 20:17:24 +1000
committerJérôme Glisse <jglisse@redhat.com>2017-08-09 18:13:31 -0400
commit729f884cb7ca962300740ede2690833181fdbbe6 (patch)
tree5bef3ff5dab5df745de454ec66c57f4c11142858
parentba82ac325c20d86a73d3a997622d63d47f2b1569 (diff)
mmu: implement base for new vm management
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/os.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c49
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c112
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h27
7 files changed, 188 insertions, 20 deletions
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 <nvif/os.h>
+#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 <core/gpuobj.h>
#include <subdev/fb.h>
@@ -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 <subdev/mmu.h>
+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