162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat 462306a36Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 862306a36Sopenharmony_ci#include <linux/vmalloc.h> 962306a36Sopenharmony_ci#include <linux/spinlock.h> 1062306a36Sopenharmony_ci#include <linux/shmem_fs.h> 1162306a36Sopenharmony_ci#include <linux/dma-buf.h> 1262306a36Sopenharmony_ci#include <linux/pfn_t.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <drm/drm_prime.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "msm_drv.h" 1762306a36Sopenharmony_ci#include "msm_fence.h" 1862306a36Sopenharmony_ci#include "msm_gem.h" 1962306a36Sopenharmony_ci#include "msm_gpu.h" 2062306a36Sopenharmony_ci#include "msm_mmu.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic dma_addr_t physaddr(struct drm_gem_object *obj) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 2562306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 2662306a36Sopenharmony_ci return (((dma_addr_t)msm_obj->vram_node->start) << PAGE_SHIFT) + 2762306a36Sopenharmony_ci priv->vram.paddr; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic bool use_pages(struct drm_gem_object *obj) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 3362306a36Sopenharmony_ci return !msm_obj->vram_node; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * Cache sync.. this is a bit over-complicated, to fit dma-mapping 3862306a36Sopenharmony_ci * API. Really GPU cache is out of scope here (handled on cmdstream) 3962306a36Sopenharmony_ci * and all we need to do is invalidate newly allocated pages before 4062306a36Sopenharmony_ci * mapping to CPU as uncached/writecombine. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * On top of this, we have the added headache, that depending on 4362306a36Sopenharmony_ci * display generation, the display's iommu may be wired up to either 4462306a36Sopenharmony_ci * the toplevel drm device (mdss), or to the mdp sub-node, meaning 4562306a36Sopenharmony_ci * that here we either have dma-direct or iommu ops. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Let this be a cautionary tail of abstraction gone wrong. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void sync_for_device(struct msm_gem_object *msm_obj) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct device *dev = msm_obj->base.dev->dev; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci dma_map_sgtable(dev, msm_obj->sgt, DMA_BIDIRECTIONAL, 0); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void sync_for_cpu(struct msm_gem_object *msm_obj) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct device *dev = msm_obj->base.dev->dev; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci dma_unmap_sgtable(dev, msm_obj->sgt, DMA_BIDIRECTIONAL, 0); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void update_lru_active(struct drm_gem_object *obj) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 6762306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci GEM_WARN_ON(!msm_obj->pages); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (msm_obj->pin_count) { 7262306a36Sopenharmony_ci drm_gem_lru_move_tail_locked(&priv->lru.pinned, obj); 7362306a36Sopenharmony_ci } else if (msm_obj->madv == MSM_MADV_WILLNEED) { 7462306a36Sopenharmony_ci drm_gem_lru_move_tail_locked(&priv->lru.willneed, obj); 7562306a36Sopenharmony_ci } else { 7662306a36Sopenharmony_ci GEM_WARN_ON(msm_obj->madv != MSM_MADV_DONTNEED); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci drm_gem_lru_move_tail_locked(&priv->lru.dontneed, obj); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void update_lru_locked(struct drm_gem_object *obj) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 8562306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci msm_gem_assert_locked(&msm_obj->base); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!msm_obj->pages) { 9062306a36Sopenharmony_ci GEM_WARN_ON(msm_obj->pin_count); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci drm_gem_lru_move_tail_locked(&priv->lru.unbacked, obj); 9362306a36Sopenharmony_ci } else { 9462306a36Sopenharmony_ci update_lru_active(obj); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void update_lru(struct drm_gem_object *obj) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci mutex_lock(&priv->lru.lock); 10362306a36Sopenharmony_ci update_lru_locked(obj); 10462306a36Sopenharmony_ci mutex_unlock(&priv->lru.lock); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* allocate pages from VRAM carveout, used when no IOMMU: */ 10862306a36Sopenharmony_cistatic struct page **get_pages_vram(struct drm_gem_object *obj, int npages) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 11162306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 11262306a36Sopenharmony_ci dma_addr_t paddr; 11362306a36Sopenharmony_ci struct page **p; 11462306a36Sopenharmony_ci int ret, i; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci p = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); 11762306a36Sopenharmony_ci if (!p) 11862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci spin_lock(&priv->vram.lock); 12162306a36Sopenharmony_ci ret = drm_mm_insert_node(&priv->vram.mm, msm_obj->vram_node, npages); 12262306a36Sopenharmony_ci spin_unlock(&priv->vram.lock); 12362306a36Sopenharmony_ci if (ret) { 12462306a36Sopenharmony_ci kvfree(p); 12562306a36Sopenharmony_ci return ERR_PTR(ret); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci paddr = physaddr(obj); 12962306a36Sopenharmony_ci for (i = 0; i < npages; i++) { 13062306a36Sopenharmony_ci p[i] = pfn_to_page(__phys_to_pfn(paddr)); 13162306a36Sopenharmony_ci paddr += PAGE_SIZE; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return p; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic struct page **get_pages(struct drm_gem_object *obj) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci msm_gem_assert_locked(obj); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!msm_obj->pages) { 14462306a36Sopenharmony_ci struct drm_device *dev = obj->dev; 14562306a36Sopenharmony_ci struct page **p; 14662306a36Sopenharmony_ci int npages = obj->size >> PAGE_SHIFT; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (use_pages(obj)) 14962306a36Sopenharmony_ci p = drm_gem_get_pages(obj); 15062306a36Sopenharmony_ci else 15162306a36Sopenharmony_ci p = get_pages_vram(obj, npages); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (IS_ERR(p)) { 15462306a36Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "could not get pages: %ld\n", 15562306a36Sopenharmony_ci PTR_ERR(p)); 15662306a36Sopenharmony_ci return p; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci msm_obj->pages = p; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci msm_obj->sgt = drm_prime_pages_to_sg(obj->dev, p, npages); 16262306a36Sopenharmony_ci if (IS_ERR(msm_obj->sgt)) { 16362306a36Sopenharmony_ci void *ptr = ERR_CAST(msm_obj->sgt); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "failed to allocate sgt\n"); 16662306a36Sopenharmony_ci msm_obj->sgt = NULL; 16762306a36Sopenharmony_ci return ptr; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* For non-cached buffers, ensure the new pages are clean 17162306a36Sopenharmony_ci * because display controller, GPU, etc. are not coherent: 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci if (msm_obj->flags & MSM_BO_WC) 17462306a36Sopenharmony_ci sync_for_device(msm_obj); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci update_lru(obj); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return msm_obj->pages; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void put_pages_vram(struct drm_gem_object *obj) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 18562306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci spin_lock(&priv->vram.lock); 18862306a36Sopenharmony_ci drm_mm_remove_node(msm_obj->vram_node); 18962306a36Sopenharmony_ci spin_unlock(&priv->vram.lock); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci kvfree(msm_obj->pages); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void put_pages(struct drm_gem_object *obj) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (msm_obj->pages) { 19962306a36Sopenharmony_ci if (msm_obj->sgt) { 20062306a36Sopenharmony_ci /* For non-cached buffers, ensure the new 20162306a36Sopenharmony_ci * pages are clean because display controller, 20262306a36Sopenharmony_ci * GPU, etc. are not coherent: 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci if (msm_obj->flags & MSM_BO_WC) 20562306a36Sopenharmony_ci sync_for_cpu(msm_obj); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci sg_free_table(msm_obj->sgt); 20862306a36Sopenharmony_ci kfree(msm_obj->sgt); 20962306a36Sopenharmony_ci msm_obj->sgt = NULL; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (use_pages(obj)) 21362306a36Sopenharmony_ci drm_gem_put_pages(obj, msm_obj->pages, true, false); 21462306a36Sopenharmony_ci else 21562306a36Sopenharmony_ci put_pages_vram(obj); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci msm_obj->pages = NULL; 21862306a36Sopenharmony_ci update_lru(obj); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic struct page **msm_gem_pin_pages_locked(struct drm_gem_object *obj, 22362306a36Sopenharmony_ci unsigned madv) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci msm_gem_assert_locked(obj); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (GEM_WARN_ON(msm_obj->madv > madv)) { 23062306a36Sopenharmony_ci DRM_DEV_ERROR(obj->dev->dev, "Invalid madv state: %u vs %u\n", 23162306a36Sopenharmony_ci msm_obj->madv, madv); 23262306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return get_pages(obj); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* 23962306a36Sopenharmony_ci * Update the pin count of the object, call under lru.lock 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_civoid msm_gem_pin_obj_locked(struct drm_gem_object *obj) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci msm_gem_assert_locked(obj); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci to_msm_bo(obj)->pin_count++; 24862306a36Sopenharmony_ci drm_gem_lru_move_tail_locked(&priv->lru.pinned, obj); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void pin_obj_locked(struct drm_gem_object *obj) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci mutex_lock(&priv->lru.lock); 25662306a36Sopenharmony_ci msm_gem_pin_obj_locked(obj); 25762306a36Sopenharmony_ci mutex_unlock(&priv->lru.lock); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistruct page **msm_gem_pin_pages(struct drm_gem_object *obj) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct page **p; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci msm_gem_lock(obj); 26562306a36Sopenharmony_ci p = msm_gem_pin_pages_locked(obj, MSM_MADV_WILLNEED); 26662306a36Sopenharmony_ci if (!IS_ERR(p)) 26762306a36Sopenharmony_ci pin_obj_locked(obj); 26862306a36Sopenharmony_ci msm_gem_unlock(obj); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return p; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_civoid msm_gem_unpin_pages(struct drm_gem_object *obj) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci msm_gem_lock(obj); 27662306a36Sopenharmony_ci msm_gem_unpin_locked(obj); 27762306a36Sopenharmony_ci msm_gem_unlock(obj); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic pgprot_t msm_gem_pgprot(struct msm_gem_object *msm_obj, pgprot_t prot) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci if (msm_obj->flags & MSM_BO_WC) 28362306a36Sopenharmony_ci return pgprot_writecombine(prot); 28462306a36Sopenharmony_ci return prot; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic vm_fault_t msm_gem_fault(struct vm_fault *vmf) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 29062306a36Sopenharmony_ci struct drm_gem_object *obj = vma->vm_private_data; 29162306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 29262306a36Sopenharmony_ci struct page **pages; 29362306a36Sopenharmony_ci unsigned long pfn; 29462306a36Sopenharmony_ci pgoff_t pgoff; 29562306a36Sopenharmony_ci int err; 29662306a36Sopenharmony_ci vm_fault_t ret; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * vm_ops.open/drm_gem_mmap_obj and close get and put 30062306a36Sopenharmony_ci * a reference on obj. So, we dont need to hold one here. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci err = msm_gem_lock_interruptible(obj); 30362306a36Sopenharmony_ci if (err) { 30462306a36Sopenharmony_ci ret = VM_FAULT_NOPAGE; 30562306a36Sopenharmony_ci goto out; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (GEM_WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED)) { 30962306a36Sopenharmony_ci msm_gem_unlock(obj); 31062306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* make sure we have pages attached now */ 31462306a36Sopenharmony_ci pages = get_pages(obj); 31562306a36Sopenharmony_ci if (IS_ERR(pages)) { 31662306a36Sopenharmony_ci ret = vmf_error(PTR_ERR(pages)); 31762306a36Sopenharmony_ci goto out_unlock; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* We don't use vmf->pgoff since that has the fake offset: */ 32162306a36Sopenharmony_ci pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci pfn = page_to_pfn(pages[pgoff]); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci VERB("Inserting %p pfn %lx, pa %lx", (void *)vmf->address, 32662306a36Sopenharmony_ci pfn, pfn << PAGE_SHIFT); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci ret = vmf_insert_pfn(vma, vmf->address, pfn); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ciout_unlock: 33162306a36Sopenharmony_ci msm_gem_unlock(obj); 33262306a36Sopenharmony_ciout: 33362306a36Sopenharmony_ci return ret; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/** get mmap offset */ 33762306a36Sopenharmony_cistatic uint64_t mmap_offset(struct drm_gem_object *obj) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct drm_device *dev = obj->dev; 34062306a36Sopenharmony_ci int ret; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci msm_gem_assert_locked(obj); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Make it mmapable */ 34562306a36Sopenharmony_ci ret = drm_gem_create_mmap_offset(obj); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (ret) { 34862306a36Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "could not allocate mmap offset\n"); 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return drm_vma_node_offset_addr(&obj->vma_node); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ciuint64_t msm_gem_mmap_offset(struct drm_gem_object *obj) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci uint64_t offset; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci msm_gem_lock(obj); 36062306a36Sopenharmony_ci offset = mmap_offset(obj); 36162306a36Sopenharmony_ci msm_gem_unlock(obj); 36262306a36Sopenharmony_ci return offset; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic struct msm_gem_vma *add_vma(struct drm_gem_object *obj, 36662306a36Sopenharmony_ci struct msm_gem_address_space *aspace) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 36962306a36Sopenharmony_ci struct msm_gem_vma *vma; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci msm_gem_assert_locked(obj); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci vma = msm_gem_vma_new(aspace); 37462306a36Sopenharmony_ci if (!vma) 37562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci list_add_tail(&vma->list, &msm_obj->vmas); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return vma; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic struct msm_gem_vma *lookup_vma(struct drm_gem_object *obj, 38362306a36Sopenharmony_ci struct msm_gem_address_space *aspace) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 38662306a36Sopenharmony_ci struct msm_gem_vma *vma; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci msm_gem_assert_locked(obj); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci list_for_each_entry(vma, &msm_obj->vmas, list) { 39162306a36Sopenharmony_ci if (vma->aspace == aspace) 39262306a36Sopenharmony_ci return vma; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return NULL; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic void del_vma(struct msm_gem_vma *vma) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci if (!vma) 40162306a36Sopenharmony_ci return; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci list_del(&vma->list); 40462306a36Sopenharmony_ci kfree(vma); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* 40862306a36Sopenharmony_ci * If close is true, this also closes the VMA (releasing the allocated 40962306a36Sopenharmony_ci * iova range) in addition to removing the iommu mapping. In the eviction 41062306a36Sopenharmony_ci * case (!close), we keep the iova allocated, but only remove the iommu 41162306a36Sopenharmony_ci * mapping. 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_cistatic void 41462306a36Sopenharmony_ciput_iova_spaces(struct drm_gem_object *obj, bool close) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 41762306a36Sopenharmony_ci struct msm_gem_vma *vma; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci msm_gem_assert_locked(obj); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci list_for_each_entry(vma, &msm_obj->vmas, list) { 42262306a36Sopenharmony_ci if (vma->aspace) { 42362306a36Sopenharmony_ci msm_gem_vma_purge(vma); 42462306a36Sopenharmony_ci if (close) 42562306a36Sopenharmony_ci msm_gem_vma_close(vma); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci/* Called with msm_obj locked */ 43162306a36Sopenharmony_cistatic void 43262306a36Sopenharmony_ciput_iova_vmas(struct drm_gem_object *obj) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 43562306a36Sopenharmony_ci struct msm_gem_vma *vma, *tmp; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci msm_gem_assert_locked(obj); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci list_for_each_entry_safe(vma, tmp, &msm_obj->vmas, list) { 44062306a36Sopenharmony_ci del_vma(vma); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic struct msm_gem_vma *get_vma_locked(struct drm_gem_object *obj, 44562306a36Sopenharmony_ci struct msm_gem_address_space *aspace, 44662306a36Sopenharmony_ci u64 range_start, u64 range_end) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct msm_gem_vma *vma; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci msm_gem_assert_locked(obj); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci vma = lookup_vma(obj, aspace); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (!vma) { 45562306a36Sopenharmony_ci int ret; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci vma = add_vma(obj, aspace); 45862306a36Sopenharmony_ci if (IS_ERR(vma)) 45962306a36Sopenharmony_ci return vma; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ret = msm_gem_vma_init(vma, obj->size, 46262306a36Sopenharmony_ci range_start, range_end); 46362306a36Sopenharmony_ci if (ret) { 46462306a36Sopenharmony_ci del_vma(vma); 46562306a36Sopenharmony_ci return ERR_PTR(ret); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci } else { 46862306a36Sopenharmony_ci GEM_WARN_ON(vma->iova < range_start); 46962306a36Sopenharmony_ci GEM_WARN_ON((vma->iova + obj->size) > range_end); 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return vma; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ciint msm_gem_pin_vma_locked(struct drm_gem_object *obj, struct msm_gem_vma *vma) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 47862306a36Sopenharmony_ci struct page **pages; 47962306a36Sopenharmony_ci int prot = IOMMU_READ; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (!(msm_obj->flags & MSM_BO_GPU_READONLY)) 48262306a36Sopenharmony_ci prot |= IOMMU_WRITE; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (msm_obj->flags & MSM_BO_MAP_PRIV) 48562306a36Sopenharmony_ci prot |= IOMMU_PRIV; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (msm_obj->flags & MSM_BO_CACHED_COHERENT) 48862306a36Sopenharmony_ci prot |= IOMMU_CACHE; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci msm_gem_assert_locked(obj); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci pages = msm_gem_pin_pages_locked(obj, MSM_MADV_WILLNEED); 49362306a36Sopenharmony_ci if (IS_ERR(pages)) 49462306a36Sopenharmony_ci return PTR_ERR(pages); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return msm_gem_vma_map(vma, prot, msm_obj->sgt, obj->size); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_civoid msm_gem_unpin_locked(struct drm_gem_object *obj) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 50262306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci msm_gem_assert_locked(obj); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci mutex_lock(&priv->lru.lock); 50762306a36Sopenharmony_ci msm_obj->pin_count--; 50862306a36Sopenharmony_ci GEM_WARN_ON(msm_obj->pin_count < 0); 50962306a36Sopenharmony_ci update_lru_locked(obj); 51062306a36Sopenharmony_ci mutex_unlock(&priv->lru.lock); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci/* Special unpin path for use in fence-signaling path, avoiding the need 51462306a36Sopenharmony_ci * to hold the obj lock by only depending on things that a protected by 51562306a36Sopenharmony_ci * the LRU lock. In particular we know that that we already have backing 51662306a36Sopenharmony_ci * and and that the object's dma_resv has the fence for the current 51762306a36Sopenharmony_ci * submit/job which will prevent us racing against page eviction. 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_civoid msm_gem_unpin_active(struct drm_gem_object *obj) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci msm_obj->pin_count--; 52462306a36Sopenharmony_ci GEM_WARN_ON(msm_obj->pin_count < 0); 52562306a36Sopenharmony_ci update_lru_active(obj); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistruct msm_gem_vma *msm_gem_get_vma_locked(struct drm_gem_object *obj, 52962306a36Sopenharmony_ci struct msm_gem_address_space *aspace) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci return get_vma_locked(obj, aspace, 0, U64_MAX); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int get_and_pin_iova_range_locked(struct drm_gem_object *obj, 53562306a36Sopenharmony_ci struct msm_gem_address_space *aspace, uint64_t *iova, 53662306a36Sopenharmony_ci u64 range_start, u64 range_end) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct msm_gem_vma *vma; 53962306a36Sopenharmony_ci int ret; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci msm_gem_assert_locked(obj); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci vma = get_vma_locked(obj, aspace, range_start, range_end); 54462306a36Sopenharmony_ci if (IS_ERR(vma)) 54562306a36Sopenharmony_ci return PTR_ERR(vma); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ret = msm_gem_pin_vma_locked(obj, vma); 54862306a36Sopenharmony_ci if (!ret) { 54962306a36Sopenharmony_ci *iova = vma->iova; 55062306a36Sopenharmony_ci pin_obj_locked(obj); 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci/* 55762306a36Sopenharmony_ci * get iova and pin it. Should have a matching put 55862306a36Sopenharmony_ci * limits iova to specified range (in pages) 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ciint msm_gem_get_and_pin_iova_range(struct drm_gem_object *obj, 56162306a36Sopenharmony_ci struct msm_gem_address_space *aspace, uint64_t *iova, 56262306a36Sopenharmony_ci u64 range_start, u64 range_end) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci int ret; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci msm_gem_lock(obj); 56762306a36Sopenharmony_ci ret = get_and_pin_iova_range_locked(obj, aspace, iova, range_start, range_end); 56862306a36Sopenharmony_ci msm_gem_unlock(obj); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* get iova and pin it. Should have a matching put */ 57462306a36Sopenharmony_ciint msm_gem_get_and_pin_iova(struct drm_gem_object *obj, 57562306a36Sopenharmony_ci struct msm_gem_address_space *aspace, uint64_t *iova) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci return msm_gem_get_and_pin_iova_range(obj, aspace, iova, 0, U64_MAX); 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci/* 58162306a36Sopenharmony_ci * Get an iova but don't pin it. Doesn't need a put because iovas are currently 58262306a36Sopenharmony_ci * valid for the life of the object 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ciint msm_gem_get_iova(struct drm_gem_object *obj, 58562306a36Sopenharmony_ci struct msm_gem_address_space *aspace, uint64_t *iova) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct msm_gem_vma *vma; 58862306a36Sopenharmony_ci int ret = 0; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci msm_gem_lock(obj); 59162306a36Sopenharmony_ci vma = get_vma_locked(obj, aspace, 0, U64_MAX); 59262306a36Sopenharmony_ci if (IS_ERR(vma)) { 59362306a36Sopenharmony_ci ret = PTR_ERR(vma); 59462306a36Sopenharmony_ci } else { 59562306a36Sopenharmony_ci *iova = vma->iova; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci msm_gem_unlock(obj); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return ret; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int clear_iova(struct drm_gem_object *obj, 60362306a36Sopenharmony_ci struct msm_gem_address_space *aspace) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct msm_gem_vma *vma = lookup_vma(obj, aspace); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (!vma) 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci msm_gem_vma_purge(vma); 61162306a36Sopenharmony_ci msm_gem_vma_close(vma); 61262306a36Sopenharmony_ci del_vma(vma); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/* 61862306a36Sopenharmony_ci * Get the requested iova but don't pin it. Fails if the requested iova is 61962306a36Sopenharmony_ci * not available. Doesn't need a put because iovas are currently valid for 62062306a36Sopenharmony_ci * the life of the object. 62162306a36Sopenharmony_ci * 62262306a36Sopenharmony_ci * Setting an iova of zero will clear the vma. 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_ciint msm_gem_set_iova(struct drm_gem_object *obj, 62562306a36Sopenharmony_ci struct msm_gem_address_space *aspace, uint64_t iova) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci int ret = 0; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci msm_gem_lock(obj); 63062306a36Sopenharmony_ci if (!iova) { 63162306a36Sopenharmony_ci ret = clear_iova(obj, aspace); 63262306a36Sopenharmony_ci } else { 63362306a36Sopenharmony_ci struct msm_gem_vma *vma; 63462306a36Sopenharmony_ci vma = get_vma_locked(obj, aspace, iova, iova + obj->size); 63562306a36Sopenharmony_ci if (IS_ERR(vma)) { 63662306a36Sopenharmony_ci ret = PTR_ERR(vma); 63762306a36Sopenharmony_ci } else if (GEM_WARN_ON(vma->iova != iova)) { 63862306a36Sopenharmony_ci clear_iova(obj, aspace); 63962306a36Sopenharmony_ci ret = -EBUSY; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci msm_gem_unlock(obj); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return ret; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/* 64862306a36Sopenharmony_ci * Unpin a iova by updating the reference counts. The memory isn't actually 64962306a36Sopenharmony_ci * purged until something else (shrinker, mm_notifier, destroy, etc) decides 65062306a36Sopenharmony_ci * to get rid of it 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_civoid msm_gem_unpin_iova(struct drm_gem_object *obj, 65362306a36Sopenharmony_ci struct msm_gem_address_space *aspace) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct msm_gem_vma *vma; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci msm_gem_lock(obj); 65862306a36Sopenharmony_ci vma = lookup_vma(obj, aspace); 65962306a36Sopenharmony_ci if (!GEM_WARN_ON(!vma)) { 66062306a36Sopenharmony_ci msm_gem_unpin_locked(obj); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci msm_gem_unlock(obj); 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ciint msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev, 66662306a36Sopenharmony_ci struct drm_mode_create_dumb *args) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci args->pitch = align_pitch(args->width, args->bpp); 66962306a36Sopenharmony_ci args->size = PAGE_ALIGN(args->pitch * args->height); 67062306a36Sopenharmony_ci return msm_gem_new_handle(dev, file, args->size, 67162306a36Sopenharmony_ci MSM_BO_SCANOUT | MSM_BO_WC, &args->handle, "dumb"); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ciint msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, 67562306a36Sopenharmony_ci uint32_t handle, uint64_t *offset) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct drm_gem_object *obj; 67862306a36Sopenharmony_ci int ret = 0; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* GEM does all our handle to object mapping */ 68162306a36Sopenharmony_ci obj = drm_gem_object_lookup(file, handle); 68262306a36Sopenharmony_ci if (obj == NULL) { 68362306a36Sopenharmony_ci ret = -ENOENT; 68462306a36Sopenharmony_ci goto fail; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci *offset = msm_gem_mmap_offset(obj); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci drm_gem_object_put(obj); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cifail: 69262306a36Sopenharmony_ci return ret; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic void *get_vaddr(struct drm_gem_object *obj, unsigned madv) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 69862306a36Sopenharmony_ci struct page **pages; 69962306a36Sopenharmony_ci int ret = 0; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci msm_gem_assert_locked(obj); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (obj->import_attach) 70462306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci pages = msm_gem_pin_pages_locked(obj, madv); 70762306a36Sopenharmony_ci if (IS_ERR(pages)) 70862306a36Sopenharmony_ci return ERR_CAST(pages); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci pin_obj_locked(obj); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* increment vmap_count *before* vmap() call, so shrinker can 71362306a36Sopenharmony_ci * check vmap_count (is_vunmapable()) outside of msm_obj lock. 71462306a36Sopenharmony_ci * This guarantees that we won't try to msm_gem_vunmap() this 71562306a36Sopenharmony_ci * same object from within the vmap() call (while we already 71662306a36Sopenharmony_ci * hold msm_obj lock) 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_ci msm_obj->vmap_count++; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (!msm_obj->vaddr) { 72162306a36Sopenharmony_ci msm_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT, 72262306a36Sopenharmony_ci VM_MAP, msm_gem_pgprot(msm_obj, PAGE_KERNEL)); 72362306a36Sopenharmony_ci if (msm_obj->vaddr == NULL) { 72462306a36Sopenharmony_ci ret = -ENOMEM; 72562306a36Sopenharmony_ci goto fail; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return msm_obj->vaddr; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cifail: 73262306a36Sopenharmony_ci msm_obj->vmap_count--; 73362306a36Sopenharmony_ci msm_gem_unpin_locked(obj); 73462306a36Sopenharmony_ci return ERR_PTR(ret); 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_civoid *msm_gem_get_vaddr_locked(struct drm_gem_object *obj) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci return get_vaddr(obj, MSM_MADV_WILLNEED); 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_civoid *msm_gem_get_vaddr(struct drm_gem_object *obj) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci void *ret; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci msm_gem_lock(obj); 74762306a36Sopenharmony_ci ret = msm_gem_get_vaddr_locked(obj); 74862306a36Sopenharmony_ci msm_gem_unlock(obj); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci return ret; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci/* 75462306a36Sopenharmony_ci * Don't use this! It is for the very special case of dumping 75562306a36Sopenharmony_ci * submits from GPU hangs or faults, were the bo may already 75662306a36Sopenharmony_ci * be MSM_MADV_DONTNEED, but we know the buffer is still on the 75762306a36Sopenharmony_ci * active list. 75862306a36Sopenharmony_ci */ 75962306a36Sopenharmony_civoid *msm_gem_get_vaddr_active(struct drm_gem_object *obj) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci return get_vaddr(obj, __MSM_MADV_PURGED); 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_civoid msm_gem_put_vaddr_locked(struct drm_gem_object *obj) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci msm_gem_assert_locked(obj); 76962306a36Sopenharmony_ci GEM_WARN_ON(msm_obj->vmap_count < 1); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci msm_obj->vmap_count--; 77262306a36Sopenharmony_ci msm_gem_unpin_locked(obj); 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_civoid msm_gem_put_vaddr(struct drm_gem_object *obj) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci msm_gem_lock(obj); 77862306a36Sopenharmony_ci msm_gem_put_vaddr_locked(obj); 77962306a36Sopenharmony_ci msm_gem_unlock(obj); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci/* Update madvise status, returns true if not purged, else 78362306a36Sopenharmony_ci * false or -errno. 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ciint msm_gem_madvise(struct drm_gem_object *obj, unsigned madv) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 78862306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci msm_gem_lock(obj); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci mutex_lock(&priv->lru.lock); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (msm_obj->madv != __MSM_MADV_PURGED) 79562306a36Sopenharmony_ci msm_obj->madv = madv; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci madv = msm_obj->madv; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* If the obj is inactive, we might need to move it 80062306a36Sopenharmony_ci * between inactive lists 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci update_lru_locked(obj); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci mutex_unlock(&priv->lru.lock); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci msm_gem_unlock(obj); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return (madv != __MSM_MADV_PURGED); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_civoid msm_gem_purge(struct drm_gem_object *obj) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct drm_device *dev = obj->dev; 81462306a36Sopenharmony_ci struct msm_drm_private *priv = obj->dev->dev_private; 81562306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci msm_gem_assert_locked(obj); 81862306a36Sopenharmony_ci GEM_WARN_ON(!is_purgeable(msm_obj)); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* Get rid of any iommu mapping(s): */ 82162306a36Sopenharmony_ci put_iova_spaces(obj, true); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci msm_gem_vunmap(obj); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci put_pages(obj); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci put_iova_vmas(obj); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci mutex_lock(&priv->lru.lock); 83262306a36Sopenharmony_ci /* A one-way transition: */ 83362306a36Sopenharmony_ci msm_obj->madv = __MSM_MADV_PURGED; 83462306a36Sopenharmony_ci mutex_unlock(&priv->lru.lock); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci drm_gem_free_mmap_offset(obj); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Our goal here is to return as much of the memory as 83962306a36Sopenharmony_ci * is possible back to the system as we are called from OOM. 84062306a36Sopenharmony_ci * To do this we must instruct the shmfs to drop all of its 84162306a36Sopenharmony_ci * backing pages, *now*. 84262306a36Sopenharmony_ci */ 84362306a36Sopenharmony_ci shmem_truncate_range(file_inode(obj->filp), 0, (loff_t)-1); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci invalidate_mapping_pages(file_inode(obj->filp)->i_mapping, 84662306a36Sopenharmony_ci 0, (loff_t)-1); 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci/* 85062306a36Sopenharmony_ci * Unpin the backing pages and make them available to be swapped out. 85162306a36Sopenharmony_ci */ 85262306a36Sopenharmony_civoid msm_gem_evict(struct drm_gem_object *obj) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct drm_device *dev = obj->dev; 85562306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci msm_gem_assert_locked(obj); 85862306a36Sopenharmony_ci GEM_WARN_ON(is_unevictable(msm_obj)); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* Get rid of any iommu mapping(s): */ 86162306a36Sopenharmony_ci put_iova_spaces(obj, false); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci put_pages(obj); 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_civoid msm_gem_vunmap(struct drm_gem_object *obj) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci msm_gem_assert_locked(obj); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (!msm_obj->vaddr || GEM_WARN_ON(!is_vunmapable(msm_obj))) 87562306a36Sopenharmony_ci return; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci vunmap(msm_obj->vaddr); 87862306a36Sopenharmony_ci msm_obj->vaddr = NULL; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cibool msm_gem_active(struct drm_gem_object *obj) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci msm_gem_assert_locked(obj); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (to_msm_bo(obj)->pin_count) 88662306a36Sopenharmony_ci return true; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return !dma_resv_test_signaled(obj->resv, dma_resv_usage_rw(true)); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ciint msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci bool write = !!(op & MSM_PREP_WRITE); 89462306a36Sopenharmony_ci unsigned long remain = 89562306a36Sopenharmony_ci op & MSM_PREP_NOSYNC ? 0 : timeout_to_jiffies(timeout); 89662306a36Sopenharmony_ci long ret; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (op & MSM_PREP_BOOST) { 89962306a36Sopenharmony_ci dma_resv_set_deadline(obj->resv, dma_resv_usage_rw(write), 90062306a36Sopenharmony_ci ktime_get()); 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(write), 90462306a36Sopenharmony_ci true, remain); 90562306a36Sopenharmony_ci if (ret == 0) 90662306a36Sopenharmony_ci return remain == 0 ? -EBUSY : -ETIMEDOUT; 90762306a36Sopenharmony_ci else if (ret < 0) 90862306a36Sopenharmony_ci return ret; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* TODO cache maintenance */ 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return 0; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ciint msm_gem_cpu_fini(struct drm_gem_object *obj) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci /* TODO cache maintenance */ 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 92262306a36Sopenharmony_civoid msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m, 92362306a36Sopenharmony_ci struct msm_gem_stats *stats) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 92662306a36Sopenharmony_ci struct dma_resv *robj = obj->resv; 92762306a36Sopenharmony_ci struct msm_gem_vma *vma; 92862306a36Sopenharmony_ci uint64_t off = drm_vma_node_start(&obj->vma_node); 92962306a36Sopenharmony_ci const char *madv; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci msm_gem_lock(obj); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci stats->all.count++; 93462306a36Sopenharmony_ci stats->all.size += obj->size; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (msm_gem_active(obj)) { 93762306a36Sopenharmony_ci stats->active.count++; 93862306a36Sopenharmony_ci stats->active.size += obj->size; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (msm_obj->pages) { 94262306a36Sopenharmony_ci stats->resident.count++; 94362306a36Sopenharmony_ci stats->resident.size += obj->size; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci switch (msm_obj->madv) { 94762306a36Sopenharmony_ci case __MSM_MADV_PURGED: 94862306a36Sopenharmony_ci stats->purged.count++; 94962306a36Sopenharmony_ci stats->purged.size += obj->size; 95062306a36Sopenharmony_ci madv = " purged"; 95162306a36Sopenharmony_ci break; 95262306a36Sopenharmony_ci case MSM_MADV_DONTNEED: 95362306a36Sopenharmony_ci stats->purgeable.count++; 95462306a36Sopenharmony_ci stats->purgeable.size += obj->size; 95562306a36Sopenharmony_ci madv = " purgeable"; 95662306a36Sopenharmony_ci break; 95762306a36Sopenharmony_ci case MSM_MADV_WILLNEED: 95862306a36Sopenharmony_ci default: 95962306a36Sopenharmony_ci madv = ""; 96062306a36Sopenharmony_ci break; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci seq_printf(m, "%08x: %c %2d (%2d) %08llx %p", 96462306a36Sopenharmony_ci msm_obj->flags, msm_gem_active(obj) ? 'A' : 'I', 96562306a36Sopenharmony_ci obj->name, kref_read(&obj->refcount), 96662306a36Sopenharmony_ci off, msm_obj->vaddr); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci seq_printf(m, " %08zu %9s %-32s\n", obj->size, madv, msm_obj->name); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (!list_empty(&msm_obj->vmas)) { 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci seq_puts(m, " vmas:"); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci list_for_each_entry(vma, &msm_obj->vmas, list) { 97562306a36Sopenharmony_ci const char *name, *comm; 97662306a36Sopenharmony_ci if (vma->aspace) { 97762306a36Sopenharmony_ci struct msm_gem_address_space *aspace = vma->aspace; 97862306a36Sopenharmony_ci struct task_struct *task = 97962306a36Sopenharmony_ci get_pid_task(aspace->pid, PIDTYPE_PID); 98062306a36Sopenharmony_ci if (task) { 98162306a36Sopenharmony_ci comm = kstrdup(task->comm, GFP_KERNEL); 98262306a36Sopenharmony_ci put_task_struct(task); 98362306a36Sopenharmony_ci } else { 98462306a36Sopenharmony_ci comm = NULL; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci name = aspace->name; 98762306a36Sopenharmony_ci } else { 98862306a36Sopenharmony_ci name = comm = NULL; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci seq_printf(m, " [%s%s%s: aspace=%p, %08llx,%s]", 99162306a36Sopenharmony_ci name, comm ? ":" : "", comm ? comm : "", 99262306a36Sopenharmony_ci vma->aspace, vma->iova, 99362306a36Sopenharmony_ci vma->mapped ? "mapped" : "unmapped"); 99462306a36Sopenharmony_ci kfree(comm); 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci seq_puts(m, "\n"); 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci dma_resv_describe(robj, m); 100162306a36Sopenharmony_ci msm_gem_unlock(obj); 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_civoid msm_gem_describe_objects(struct list_head *list, struct seq_file *m) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct msm_gem_stats stats = {}; 100762306a36Sopenharmony_ci struct msm_gem_object *msm_obj; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci seq_puts(m, " flags id ref offset kaddr size madv name\n"); 101062306a36Sopenharmony_ci list_for_each_entry(msm_obj, list, node) { 101162306a36Sopenharmony_ci struct drm_gem_object *obj = &msm_obj->base; 101262306a36Sopenharmony_ci seq_puts(m, " "); 101362306a36Sopenharmony_ci msm_gem_describe(obj, m, &stats); 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci seq_printf(m, "Total: %4d objects, %9zu bytes\n", 101762306a36Sopenharmony_ci stats.all.count, stats.all.size); 101862306a36Sopenharmony_ci seq_printf(m, "Active: %4d objects, %9zu bytes\n", 101962306a36Sopenharmony_ci stats.active.count, stats.active.size); 102062306a36Sopenharmony_ci seq_printf(m, "Resident: %4d objects, %9zu bytes\n", 102162306a36Sopenharmony_ci stats.resident.count, stats.resident.size); 102262306a36Sopenharmony_ci seq_printf(m, "Purgeable: %4d objects, %9zu bytes\n", 102362306a36Sopenharmony_ci stats.purgeable.count, stats.purgeable.size); 102462306a36Sopenharmony_ci seq_printf(m, "Purged: %4d objects, %9zu bytes\n", 102562306a36Sopenharmony_ci stats.purged.count, stats.purged.size); 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci#endif 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci/* don't call directly! Use drm_gem_object_put() */ 103062306a36Sopenharmony_cistatic void msm_gem_free_object(struct drm_gem_object *obj) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 103362306a36Sopenharmony_ci struct drm_device *dev = obj->dev; 103462306a36Sopenharmony_ci struct msm_drm_private *priv = dev->dev_private; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci mutex_lock(&priv->obj_lock); 103762306a36Sopenharmony_ci list_del(&msm_obj->node); 103862306a36Sopenharmony_ci mutex_unlock(&priv->obj_lock); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci put_iova_spaces(obj, true); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci if (obj->import_attach) { 104362306a36Sopenharmony_ci GEM_WARN_ON(msm_obj->vaddr); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci /* Don't drop the pages for imported dmabuf, as they are not 104662306a36Sopenharmony_ci * ours, just free the array we allocated: 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_ci kvfree(msm_obj->pages); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci put_iova_vmas(obj); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci drm_prime_gem_destroy(obj, msm_obj->sgt); 105362306a36Sopenharmony_ci } else { 105462306a36Sopenharmony_ci msm_gem_vunmap(obj); 105562306a36Sopenharmony_ci put_pages(obj); 105662306a36Sopenharmony_ci put_iova_vmas(obj); 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci drm_gem_object_release(obj); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci kfree(msm_obj); 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic int msm_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP); 106962306a36Sopenharmony_ci vma->vm_page_prot = msm_gem_pgprot(msm_obj, vm_get_page_prot(vma->vm_flags)); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return 0; 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci/* convenience method to construct a GEM buffer object, and userspace handle */ 107562306a36Sopenharmony_ciint msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, 107662306a36Sopenharmony_ci uint32_t size, uint32_t flags, uint32_t *handle, 107762306a36Sopenharmony_ci char *name) 107862306a36Sopenharmony_ci{ 107962306a36Sopenharmony_ci struct drm_gem_object *obj; 108062306a36Sopenharmony_ci int ret; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci obj = msm_gem_new(dev, size, flags); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (IS_ERR(obj)) 108562306a36Sopenharmony_ci return PTR_ERR(obj); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (name) 108862306a36Sopenharmony_ci msm_gem_object_set_name(obj, "%s", name); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci ret = drm_gem_handle_create(file, obj, handle); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* drop reference from allocate - handle holds it now */ 109362306a36Sopenharmony_ci drm_gem_object_put(obj); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci return ret; 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_cistatic enum drm_gem_object_status msm_gem_status(struct drm_gem_object *obj) 109962306a36Sopenharmony_ci{ 110062306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(obj); 110162306a36Sopenharmony_ci enum drm_gem_object_status status = 0; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (msm_obj->pages) 110462306a36Sopenharmony_ci status |= DRM_GEM_OBJECT_RESIDENT; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci if (msm_obj->madv == MSM_MADV_DONTNEED) 110762306a36Sopenharmony_ci status |= DRM_GEM_OBJECT_PURGEABLE; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci return status; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic const struct vm_operations_struct vm_ops = { 111362306a36Sopenharmony_ci .fault = msm_gem_fault, 111462306a36Sopenharmony_ci .open = drm_gem_vm_open, 111562306a36Sopenharmony_ci .close = drm_gem_vm_close, 111662306a36Sopenharmony_ci}; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic const struct drm_gem_object_funcs msm_gem_object_funcs = { 111962306a36Sopenharmony_ci .free = msm_gem_free_object, 112062306a36Sopenharmony_ci .pin = msm_gem_prime_pin, 112162306a36Sopenharmony_ci .unpin = msm_gem_prime_unpin, 112262306a36Sopenharmony_ci .get_sg_table = msm_gem_prime_get_sg_table, 112362306a36Sopenharmony_ci .vmap = msm_gem_prime_vmap, 112462306a36Sopenharmony_ci .vunmap = msm_gem_prime_vunmap, 112562306a36Sopenharmony_ci .mmap = msm_gem_object_mmap, 112662306a36Sopenharmony_ci .status = msm_gem_status, 112762306a36Sopenharmony_ci .vm_ops = &vm_ops, 112862306a36Sopenharmony_ci}; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic int msm_gem_new_impl(struct drm_device *dev, 113162306a36Sopenharmony_ci uint32_t size, uint32_t flags, 113262306a36Sopenharmony_ci struct drm_gem_object **obj) 113362306a36Sopenharmony_ci{ 113462306a36Sopenharmony_ci struct msm_drm_private *priv = dev->dev_private; 113562306a36Sopenharmony_ci struct msm_gem_object *msm_obj; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci switch (flags & MSM_BO_CACHE_MASK) { 113862306a36Sopenharmony_ci case MSM_BO_CACHED: 113962306a36Sopenharmony_ci case MSM_BO_WC: 114062306a36Sopenharmony_ci break; 114162306a36Sopenharmony_ci case MSM_BO_CACHED_COHERENT: 114262306a36Sopenharmony_ci if (priv->has_cached_coherent) 114362306a36Sopenharmony_ci break; 114462306a36Sopenharmony_ci fallthrough; 114562306a36Sopenharmony_ci default: 114662306a36Sopenharmony_ci DRM_DEV_DEBUG(dev->dev, "invalid cache flag: %x\n", 114762306a36Sopenharmony_ci (flags & MSM_BO_CACHE_MASK)); 114862306a36Sopenharmony_ci return -EINVAL; 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL); 115262306a36Sopenharmony_ci if (!msm_obj) 115362306a36Sopenharmony_ci return -ENOMEM; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci msm_obj->flags = flags; 115662306a36Sopenharmony_ci msm_obj->madv = MSM_MADV_WILLNEED; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci INIT_LIST_HEAD(&msm_obj->node); 115962306a36Sopenharmony_ci INIT_LIST_HEAD(&msm_obj->vmas); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci *obj = &msm_obj->base; 116262306a36Sopenharmony_ci (*obj)->funcs = &msm_gem_object_funcs; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci return 0; 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistruct drm_gem_object *msm_gem_new(struct drm_device *dev, uint32_t size, uint32_t flags) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci struct msm_drm_private *priv = dev->dev_private; 117062306a36Sopenharmony_ci struct msm_gem_object *msm_obj; 117162306a36Sopenharmony_ci struct drm_gem_object *obj = NULL; 117262306a36Sopenharmony_ci bool use_vram = false; 117362306a36Sopenharmony_ci int ret; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci size = PAGE_ALIGN(size); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (!msm_use_mmu(dev)) 117862306a36Sopenharmony_ci use_vram = true; 117962306a36Sopenharmony_ci else if ((flags & (MSM_BO_STOLEN | MSM_BO_SCANOUT)) && priv->vram.size) 118062306a36Sopenharmony_ci use_vram = true; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (GEM_WARN_ON(use_vram && !priv->vram.size)) 118362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* Disallow zero sized objects as they make the underlying 118662306a36Sopenharmony_ci * infrastructure grumpy 118762306a36Sopenharmony_ci */ 118862306a36Sopenharmony_ci if (size == 0) 118962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci ret = msm_gem_new_impl(dev, size, flags, &obj); 119262306a36Sopenharmony_ci if (ret) 119362306a36Sopenharmony_ci return ERR_PTR(ret); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci msm_obj = to_msm_bo(obj); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (use_vram) { 119862306a36Sopenharmony_ci struct msm_gem_vma *vma; 119962306a36Sopenharmony_ci struct page **pages; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci drm_gem_private_object_init(dev, obj, size); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci msm_gem_lock(obj); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci vma = add_vma(obj, NULL); 120662306a36Sopenharmony_ci msm_gem_unlock(obj); 120762306a36Sopenharmony_ci if (IS_ERR(vma)) { 120862306a36Sopenharmony_ci ret = PTR_ERR(vma); 120962306a36Sopenharmony_ci goto fail; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci to_msm_bo(obj)->vram_node = &vma->node; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci msm_gem_lock(obj); 121562306a36Sopenharmony_ci pages = get_pages(obj); 121662306a36Sopenharmony_ci msm_gem_unlock(obj); 121762306a36Sopenharmony_ci if (IS_ERR(pages)) { 121862306a36Sopenharmony_ci ret = PTR_ERR(pages); 121962306a36Sopenharmony_ci goto fail; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci vma->iova = physaddr(obj); 122362306a36Sopenharmony_ci } else { 122462306a36Sopenharmony_ci ret = drm_gem_object_init(dev, obj, size); 122562306a36Sopenharmony_ci if (ret) 122662306a36Sopenharmony_ci goto fail; 122762306a36Sopenharmony_ci /* 122862306a36Sopenharmony_ci * Our buffers are kept pinned, so allocating them from the 122962306a36Sopenharmony_ci * MOVABLE zone is a really bad idea, and conflicts with CMA. 123062306a36Sopenharmony_ci * See comments above new_inode() why this is required _and_ 123162306a36Sopenharmony_ci * expected if you're going to pin these pages. 123262306a36Sopenharmony_ci */ 123362306a36Sopenharmony_ci mapping_set_gfp_mask(obj->filp->f_mapping, GFP_HIGHUSER); 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci drm_gem_lru_move_tail(&priv->lru.unbacked, obj); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci mutex_lock(&priv->obj_lock); 123962306a36Sopenharmony_ci list_add_tail(&msm_obj->node, &priv->objects); 124062306a36Sopenharmony_ci mutex_unlock(&priv->obj_lock); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci ret = drm_gem_create_mmap_offset(obj); 124362306a36Sopenharmony_ci if (ret) 124462306a36Sopenharmony_ci goto fail; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci return obj; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cifail: 124962306a36Sopenharmony_ci drm_gem_object_put(obj); 125062306a36Sopenharmony_ci return ERR_PTR(ret); 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_cistruct drm_gem_object *msm_gem_import(struct drm_device *dev, 125462306a36Sopenharmony_ci struct dma_buf *dmabuf, struct sg_table *sgt) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci struct msm_drm_private *priv = dev->dev_private; 125762306a36Sopenharmony_ci struct msm_gem_object *msm_obj; 125862306a36Sopenharmony_ci struct drm_gem_object *obj; 125962306a36Sopenharmony_ci uint32_t size; 126062306a36Sopenharmony_ci int ret, npages; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci /* if we don't have IOMMU, don't bother pretending we can import: */ 126362306a36Sopenharmony_ci if (!msm_use_mmu(dev)) { 126462306a36Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "cannot import without IOMMU\n"); 126562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci size = PAGE_ALIGN(dmabuf->size); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj); 127162306a36Sopenharmony_ci if (ret) 127262306a36Sopenharmony_ci return ERR_PTR(ret); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci drm_gem_private_object_init(dev, obj, size); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci npages = size / PAGE_SIZE; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci msm_obj = to_msm_bo(obj); 127962306a36Sopenharmony_ci msm_gem_lock(obj); 128062306a36Sopenharmony_ci msm_obj->sgt = sgt; 128162306a36Sopenharmony_ci msm_obj->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); 128262306a36Sopenharmony_ci if (!msm_obj->pages) { 128362306a36Sopenharmony_ci msm_gem_unlock(obj); 128462306a36Sopenharmony_ci ret = -ENOMEM; 128562306a36Sopenharmony_ci goto fail; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci ret = drm_prime_sg_to_page_array(sgt, msm_obj->pages, npages); 128962306a36Sopenharmony_ci if (ret) { 129062306a36Sopenharmony_ci msm_gem_unlock(obj); 129162306a36Sopenharmony_ci goto fail; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci msm_gem_unlock(obj); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci drm_gem_lru_move_tail(&priv->lru.pinned, obj); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci mutex_lock(&priv->obj_lock); 129962306a36Sopenharmony_ci list_add_tail(&msm_obj->node, &priv->objects); 130062306a36Sopenharmony_ci mutex_unlock(&priv->obj_lock); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci ret = drm_gem_create_mmap_offset(obj); 130362306a36Sopenharmony_ci if (ret) 130462306a36Sopenharmony_ci goto fail; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci return obj; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_cifail: 130962306a36Sopenharmony_ci drm_gem_object_put(obj); 131062306a36Sopenharmony_ci return ERR_PTR(ret); 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_civoid *msm_gem_kernel_new(struct drm_device *dev, uint32_t size, 131462306a36Sopenharmony_ci uint32_t flags, struct msm_gem_address_space *aspace, 131562306a36Sopenharmony_ci struct drm_gem_object **bo, uint64_t *iova) 131662306a36Sopenharmony_ci{ 131762306a36Sopenharmony_ci void *vaddr; 131862306a36Sopenharmony_ci struct drm_gem_object *obj = msm_gem_new(dev, size, flags); 131962306a36Sopenharmony_ci int ret; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (IS_ERR(obj)) 132262306a36Sopenharmony_ci return ERR_CAST(obj); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (iova) { 132562306a36Sopenharmony_ci ret = msm_gem_get_and_pin_iova(obj, aspace, iova); 132662306a36Sopenharmony_ci if (ret) 132762306a36Sopenharmony_ci goto err; 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci vaddr = msm_gem_get_vaddr(obj); 133162306a36Sopenharmony_ci if (IS_ERR(vaddr)) { 133262306a36Sopenharmony_ci msm_gem_unpin_iova(obj, aspace); 133362306a36Sopenharmony_ci ret = PTR_ERR(vaddr); 133462306a36Sopenharmony_ci goto err; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci if (bo) 133862306a36Sopenharmony_ci *bo = obj; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci return vaddr; 134162306a36Sopenharmony_cierr: 134262306a36Sopenharmony_ci drm_gem_object_put(obj); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci return ERR_PTR(ret); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_civoid msm_gem_kernel_put(struct drm_gem_object *bo, 134962306a36Sopenharmony_ci struct msm_gem_address_space *aspace) 135062306a36Sopenharmony_ci{ 135162306a36Sopenharmony_ci if (IS_ERR_OR_NULL(bo)) 135262306a36Sopenharmony_ci return; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci msm_gem_put_vaddr(bo); 135562306a36Sopenharmony_ci msm_gem_unpin_iova(bo, aspace); 135662306a36Sopenharmony_ci drm_gem_object_put(bo); 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_civoid msm_gem_object_set_name(struct drm_gem_object *bo, const char *fmt, ...) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci struct msm_gem_object *msm_obj = to_msm_bo(bo); 136262306a36Sopenharmony_ci va_list ap; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci if (!fmt) 136562306a36Sopenharmony_ci return; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci va_start(ap, fmt); 136862306a36Sopenharmony_ci vsnprintf(msm_obj->name, sizeof(msm_obj->name), fmt, ap); 136962306a36Sopenharmony_ci va_end(ap); 137062306a36Sopenharmony_ci} 1371