Path: blob/master/drivers/gpu/drm/nouveau/nouveau_vm.c
15112 views
/*1* Copyright 2010 Red Hat Inc.2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice shall be included in11* all copies or substantial portions of the Software.12*13* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL16* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR17* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,18* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR19* OTHER DEALINGS IN THE SOFTWARE.20*21* Authors: Ben Skeggs22*/2324#include "drmP.h"25#include "nouveau_drv.h"26#include "nouveau_mm.h"27#include "nouveau_vm.h"2829void30nouveau_vm_map_at(struct nouveau_vma *vma, u64 delta, struct nouveau_mem *node)31{32struct nouveau_vm *vm = vma->vm;33struct nouveau_mm_node *r;34int big = vma->node->type != vm->spg_shift;35u32 offset = vma->node->offset + (delta >> 12);36u32 bits = vma->node->type - 12;37u32 pde = (offset >> vm->pgt_bits) - vm->fpde;38u32 pte = (offset & ((1 << vm->pgt_bits) - 1)) >> bits;39u32 max = 1 << (vm->pgt_bits - bits);40u32 end, len;4142delta = 0;43list_for_each_entry(r, &node->regions, rl_entry) {44u64 phys = (u64)r->offset << 12;45u32 num = r->length >> bits;4647while (num) {48struct nouveau_gpuobj *pgt = vm->pgt[pde].obj[big];4950end = (pte + num);51if (unlikely(end >= max))52end = max;53len = end - pte;5455vm->map(vma, pgt, node, pte, len, phys, delta);5657num -= len;58pte += len;59if (unlikely(end >= max)) {60phys += len << (bits + 12);61pde++;62pte = 0;63}6465delta += (u64)len << vma->node->type;66}67}6869vm->flush(vm);70}7172void73nouveau_vm_map(struct nouveau_vma *vma, struct nouveau_mem *node)74{75nouveau_vm_map_at(vma, 0, node);76}7778void79nouveau_vm_map_sg(struct nouveau_vma *vma, u64 delta, u64 length,80struct nouveau_mem *mem, dma_addr_t *list)81{82struct nouveau_vm *vm = vma->vm;83int big = vma->node->type != vm->spg_shift;84u32 offset = vma->node->offset + (delta >> 12);85u32 bits = vma->node->type - 12;86u32 num = length >> vma->node->type;87u32 pde = (offset >> vm->pgt_bits) - vm->fpde;88u32 pte = (offset & ((1 << vm->pgt_bits) - 1)) >> bits;89u32 max = 1 << (vm->pgt_bits - bits);90u32 end, len;9192while (num) {93struct nouveau_gpuobj *pgt = vm->pgt[pde].obj[big];9495end = (pte + num);96if (unlikely(end >= max))97end = max;98len = end - pte;99100vm->map_sg(vma, pgt, mem, pte, len, list);101102num -= len;103pte += len;104list += len;105if (unlikely(end >= max)) {106pde++;107pte = 0;108}109}110111vm->flush(vm);112}113114void115nouveau_vm_unmap_at(struct nouveau_vma *vma, u64 delta, u64 length)116{117struct nouveau_vm *vm = vma->vm;118int big = vma->node->type != vm->spg_shift;119u32 offset = vma->node->offset + (delta >> 12);120u32 bits = vma->node->type - 12;121u32 num = length >> vma->node->type;122u32 pde = (offset >> vm->pgt_bits) - vm->fpde;123u32 pte = (offset & ((1 << vm->pgt_bits) - 1)) >> bits;124u32 max = 1 << (vm->pgt_bits - bits);125u32 end, len;126127while (num) {128struct nouveau_gpuobj *pgt = vm->pgt[pde].obj[big];129130end = (pte + num);131if (unlikely(end >= max))132end = max;133len = end - pte;134135vm->unmap(pgt, pte, len);136137num -= len;138pte += len;139if (unlikely(end >= max)) {140pde++;141pte = 0;142}143}144145vm->flush(vm);146}147148void149nouveau_vm_unmap(struct nouveau_vma *vma)150{151nouveau_vm_unmap_at(vma, 0, (u64)vma->node->length << 12);152}153154static void155nouveau_vm_unmap_pgt(struct nouveau_vm *vm, int big, u32 fpde, u32 lpde)156{157struct nouveau_vm_pgd *vpgd;158struct nouveau_vm_pgt *vpgt;159struct nouveau_gpuobj *pgt;160u32 pde;161162for (pde = fpde; pde <= lpde; pde++) {163vpgt = &vm->pgt[pde - vm->fpde];164if (--vpgt->refcount[big])165continue;166167pgt = vpgt->obj[big];168vpgt->obj[big] = NULL;169170list_for_each_entry(vpgd, &vm->pgd_list, head) {171vm->map_pgt(vpgd->obj, pde, vpgt->obj);172}173174mutex_unlock(&vm->mm->mutex);175nouveau_gpuobj_ref(NULL, &pgt);176mutex_lock(&vm->mm->mutex);177}178}179180static int181nouveau_vm_map_pgt(struct nouveau_vm *vm, u32 pde, u32 type)182{183struct nouveau_vm_pgt *vpgt = &vm->pgt[pde - vm->fpde];184struct nouveau_vm_pgd *vpgd;185struct nouveau_gpuobj *pgt;186int big = (type != vm->spg_shift);187u32 pgt_size;188int ret;189190pgt_size = (1 << (vm->pgt_bits + 12)) >> type;191pgt_size *= 8;192193mutex_unlock(&vm->mm->mutex);194ret = nouveau_gpuobj_new(vm->dev, NULL, pgt_size, 0x1000,195NVOBJ_FLAG_ZERO_ALLOC, &pgt);196mutex_lock(&vm->mm->mutex);197if (unlikely(ret))198return ret;199200/* someone beat us to filling the PDE while we didn't have the lock */201if (unlikely(vpgt->refcount[big]++)) {202mutex_unlock(&vm->mm->mutex);203nouveau_gpuobj_ref(NULL, &pgt);204mutex_lock(&vm->mm->mutex);205return 0;206}207208vpgt->obj[big] = pgt;209list_for_each_entry(vpgd, &vm->pgd_list, head) {210vm->map_pgt(vpgd->obj, pde, vpgt->obj);211}212213return 0;214}215216int217nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,218u32 access, struct nouveau_vma *vma)219{220u32 align = (1 << page_shift) >> 12;221u32 msize = size >> 12;222u32 fpde, lpde, pde;223int ret;224225mutex_lock(&vm->mm->mutex);226ret = nouveau_mm_get(vm->mm, page_shift, msize, 0, align, &vma->node);227if (unlikely(ret != 0)) {228mutex_unlock(&vm->mm->mutex);229return ret;230}231232fpde = (vma->node->offset >> vm->pgt_bits);233lpde = (vma->node->offset + vma->node->length - 1) >> vm->pgt_bits;234for (pde = fpde; pde <= lpde; pde++) {235struct nouveau_vm_pgt *vpgt = &vm->pgt[pde - vm->fpde];236int big = (vma->node->type != vm->spg_shift);237238if (likely(vpgt->refcount[big])) {239vpgt->refcount[big]++;240continue;241}242243ret = nouveau_vm_map_pgt(vm, pde, vma->node->type);244if (ret) {245if (pde != fpde)246nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1);247nouveau_mm_put(vm->mm, vma->node);248mutex_unlock(&vm->mm->mutex);249vma->node = NULL;250return ret;251}252}253mutex_unlock(&vm->mm->mutex);254255vma->vm = vm;256vma->offset = (u64)vma->node->offset << 12;257vma->access = access;258return 0;259}260261void262nouveau_vm_put(struct nouveau_vma *vma)263{264struct nouveau_vm *vm = vma->vm;265u32 fpde, lpde;266267if (unlikely(vma->node == NULL))268return;269fpde = (vma->node->offset >> vm->pgt_bits);270lpde = (vma->node->offset + vma->node->length - 1) >> vm->pgt_bits;271272mutex_lock(&vm->mm->mutex);273nouveau_vm_unmap_pgt(vm, vma->node->type != vm->spg_shift, fpde, lpde);274nouveau_mm_put(vm->mm, vma->node);275vma->node = NULL;276mutex_unlock(&vm->mm->mutex);277}278279int280nouveau_vm_new(struct drm_device *dev, u64 offset, u64 length, u64 mm_offset,281struct nouveau_vm **pvm)282{283struct drm_nouveau_private *dev_priv = dev->dev_private;284struct nouveau_vm *vm;285u64 mm_length = (offset + length) - mm_offset;286u32 block, pgt_bits;287int ret;288289vm = kzalloc(sizeof(*vm), GFP_KERNEL);290if (!vm)291return -ENOMEM;292293if (dev_priv->card_type == NV_50) {294vm->map_pgt = nv50_vm_map_pgt;295vm->map = nv50_vm_map;296vm->map_sg = nv50_vm_map_sg;297vm->unmap = nv50_vm_unmap;298vm->flush = nv50_vm_flush;299vm->spg_shift = 12;300vm->lpg_shift = 16;301302pgt_bits = 29;303block = (1 << pgt_bits);304if (length < block)305block = length;306307} else308if (dev_priv->card_type == NV_C0) {309vm->map_pgt = nvc0_vm_map_pgt;310vm->map = nvc0_vm_map;311vm->map_sg = nvc0_vm_map_sg;312vm->unmap = nvc0_vm_unmap;313vm->flush = nvc0_vm_flush;314vm->spg_shift = 12;315vm->lpg_shift = 17;316pgt_bits = 27;317block = 4096;318} else {319kfree(vm);320return -ENOSYS;321}322323vm->fpde = offset >> pgt_bits;324vm->lpde = (offset + length - 1) >> pgt_bits;325vm->pgt = kcalloc(vm->lpde - vm->fpde + 1, sizeof(*vm->pgt), GFP_KERNEL);326if (!vm->pgt) {327kfree(vm);328return -ENOMEM;329}330331INIT_LIST_HEAD(&vm->pgd_list);332vm->dev = dev;333vm->refcount = 1;334vm->pgt_bits = pgt_bits - 12;335336ret = nouveau_mm_init(&vm->mm, mm_offset >> 12, mm_length >> 12,337block >> 12);338if (ret) {339kfree(vm);340return ret;341}342343*pvm = vm;344return 0;345}346347static int348nouveau_vm_link(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd)349{350struct nouveau_vm_pgd *vpgd;351int i;352353if (!pgd)354return 0;355356vpgd = kzalloc(sizeof(*vpgd), GFP_KERNEL);357if (!vpgd)358return -ENOMEM;359360nouveau_gpuobj_ref(pgd, &vpgd->obj);361362mutex_lock(&vm->mm->mutex);363for (i = vm->fpde; i <= vm->lpde; i++)364vm->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj);365list_add(&vpgd->head, &vm->pgd_list);366mutex_unlock(&vm->mm->mutex);367return 0;368}369370static void371nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd)372{373struct nouveau_vm_pgd *vpgd, *tmp;374375if (!pgd)376return;377378mutex_lock(&vm->mm->mutex);379list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {380if (vpgd->obj != pgd)381continue;382383list_del(&vpgd->head);384nouveau_gpuobj_ref(NULL, &vpgd->obj);385kfree(vpgd);386}387mutex_unlock(&vm->mm->mutex);388}389390static void391nouveau_vm_del(struct nouveau_vm *vm)392{393struct nouveau_vm_pgd *vpgd, *tmp;394395list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {396nouveau_vm_unlink(vm, vpgd->obj);397}398WARN_ON(nouveau_mm_fini(&vm->mm) != 0);399400kfree(vm->pgt);401kfree(vm);402}403404int405nouveau_vm_ref(struct nouveau_vm *ref, struct nouveau_vm **ptr,406struct nouveau_gpuobj *pgd)407{408struct nouveau_vm *vm;409int ret;410411vm = ref;412if (vm) {413ret = nouveau_vm_link(vm, pgd);414if (ret)415return ret;416417vm->refcount++;418}419420vm = *ptr;421*ptr = ref;422423if (vm) {424nouveau_vm_unlink(vm, pgd);425426if (--vm->refcount == 0)427nouveau_vm_del(vm);428}429430return 0;431}432433434