diff options
author | Jérôme Glisse <jglisse@redhat.com> | 2017-08-11 12:58:09 -0400 |
---|---|---|
committer | Jérôme Glisse <jglisse@redhat.com> | 2017-09-05 14:52:37 -0400 |
commit | b6a4536b546061090dbf449c54e84295f46e46d9 (patch) | |
tree | 1cae67517ef9cdca83ec290d64bdf93696d4709b /drivers/gpu/drm/nouveau | |
parent | 115d9394d40fdbae29f28e35cd964ccc52a5616d (diff) |
drm/nouveau/compote: add page fault handler
Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau')
-rw-r--r-- | drivers/gpu/drm/nouveau/compote/compote-pfault.c | 123 |
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) |