summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJérôme Glisse <jglisse@redhat.com>2017-08-11 12:58:09 -0400
committerJérôme Glisse <jglisse@redhat.com>2017-09-05 14:52:37 -0400
commitb6a4536b546061090dbf449c54e84295f46e46d9 (patch)
tree1cae67517ef9cdca83ec290d64bdf93696d4709b
parent115d9394d40fdbae29f28e35cd964ccc52a5616d (diff)
drm/nouveau/compote: add page fault handler
Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/compote/compote-pfault.c123
1 files changed, 122 insertions, 1 deletions
diff --git a/drivers/gpu/drm/nouveau/compote/compote-pfault.c b/drivers/gpu/drm/nouveau/compote/compote-pfault.c
index f46c13fad44d..41c4becceb53 100644
--- a/drivers/gpu/drm/nouveau/compote/compote-pfault.c
+++ b/drivers/gpu/drm/nouveau/compote/compote-pfault.c
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/slab.h>
+#include <linux/hmm.h>
#include <linux/fs.h>
#include <uapi/drm/compote-uapi.h>
#include "compote.h"
@@ -82,10 +83,130 @@ static inline void compote_file_pfault_read(struct compote_file *cfile,
pfault->info = nvif_rd32(&cfile->nvpfault_buffer, index + 0x1c);
}
+static bool compote_nvkm_vma_unref(struct nvkm_vma *nvvma)
+{
+ mutex_lock(&nvvma->mutex);
+ if ((--nvvma->refcount) <= 0) {
+ nvkm_vm_put(nvvma);
+ kfree(nvvma->dmas);
+ kfree(nvvma->pfns);
+ mutex_unlock(&nvvma->mutex);
+ kfree(nvvma);
+ return false;
+ }
+ mutex_unlock(&nvvma->mutex);
+ return true;
+}
+
static int compote_pfault_handler(struct compote_file *cfile,
const struct compote_pfault *pfault)
{
- return -EINVAL;
+ bool write = compote_pfault_access(pfault) == 1;
+ struct mm_struct *mm = cfile->mm;
+ struct vm_area_struct *vma;
+ unsigned long addr, end;
+ struct hmm_range range;
+ struct nvkm_vma *nvvma;
+ struct nvkm_vm *nvvm;
+ uint32_t access;
+ int ret;
+
+ addr = pfault->addr;
+ end = addr + PAGE_SIZE;
+
+again:
+ down_read(&mm->mmap_sem);
+ vma = find_vma_intersection(mm, addr, end);
+ if (!vma) {
+ up_read(&mm->mmap_sem);
+ return -EFAULT;
+ }
+
+ if (!(vma->vm_flags & VM_READ)) {
+ up_read(&mm->mmap_sem);
+ return -EFAULT;
+ }
+ access = NV_MEM_ACCESS_RO;
+ if (write && !(vma->vm_flags & VM_WRITE)) {
+ up_read(&mm->mmap_sem);
+ return -EFAULT;
+ } else {
+ access |= NV_MEM_ACCESS_WO;
+ }
+
+ nvvm = cfile->nvclient.vm;
+ access = NV_MEM_ACCESS_RW;
+ nvvma = nvkm_vm_find(nvvm, addr, end);
+ if (nvvma == NULL) {
+ nvvma = kzalloc(sizeof(*nvvma), GFP_KERNEL);
+ if (nvvma == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ mutex_init(&nvvma->mutex);
+ nvvma->dmas = kzalloc(sizeof(dma_addr_t), GFP_KERNEL);
+ if (nvvma->dmas == NULL) {
+ kfree(nvvma);
+ ret = -ENOMEM;
+ goto error;
+ }
+ nvvma->pfns = kzalloc(sizeof(long), GFP_KERNEL);
+ if (nvvma->pfns == NULL) {
+ kfree(nvvma->dmas);
+ kfree(nvvma);
+ ret = -ENOMEM;
+ goto error;
+ }
+ nvvma->refcount = 1;
+ ret = nvkm_vm_get_fix(nvvm, addr, PAGE_SIZE, 12, access, nvvma);
+ if (ret) {
+ compote_nvkm_vma_unref(nvvma);
+ goto error;
+ }
+ }
+
+ if (!nvvma || !nvvma->pfns) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if ((nvvma->pfns[0] & HMM_PFN_VALID)) {
+ if (!compote_nvkm_vma_unref(nvvma)) {
+ up_read(&mm->mmap_sem);
+ goto again;
+ }
+ nvvma->pfns[0] = 0;
+ }
+ ret = hmm_vma_fault(vma, &range, addr, end, nvvma->pfns, write, false);
+ if (ret == -EAGAIN) {
+ compote_nvkm_vma_unref(nvvma);
+ up_read(&mm->mmap_sem);
+ goto again;
+ }
+ if (ret) {
+ compote_nvkm_vma_unref(nvvma);
+ goto error;
+ }
+
+ mutex_lock(&nvvma->mutex);
+ if (!hmm_vma_range_done(vma, &range)) {
+ nvvma->pfns[0] = 0;
+ mutex_unlock(&nvvma->mutex);
+ up_read(&mm->mmap_sem);
+ goto again;
+ }
+ if (!(nvvma->pfns[0] & HMM_PFN_VALID)) {
+ mutex_unlock(&nvvma->mutex);
+ up_read(&mm->mmap_sem);
+ goto again;
+ }
+ nvvma->refcount++;
+ ret = compote_nvkm_vma_map(cfile, nvvma, addr, PAGE_SIZE);
+ mutex_unlock(&nvvma->mutex);
+
+error:
+ up_read(&mm->mmap_sem);
+ return ret;
}
static int compote_file_pfault_process(struct nvif_notify *notify)