162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2020 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "gem/i915_gem_lmem.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "i915_trace.h" 1162306a36Sopenharmony_ci#include "intel_gt.h" 1262306a36Sopenharmony_ci#include "intel_gtt.h" 1362306a36Sopenharmony_ci#include "gen6_ppgtt.h" 1462306a36Sopenharmony_ci#include "gen8_ppgtt.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct i915_page_table *alloc_pt(struct i915_address_space *vm, int sz) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct i915_page_table *pt; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci pt = kmalloc(sizeof(*pt), I915_GFP_ALLOW_FAIL); 2162306a36Sopenharmony_ci if (unlikely(!pt)) 2262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci pt->base = vm->alloc_pt_dma(vm, sz); 2562306a36Sopenharmony_ci if (IS_ERR(pt->base)) { 2662306a36Sopenharmony_ci kfree(pt); 2762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 2862306a36Sopenharmony_ci } 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci pt->is_compact = false; 3162306a36Sopenharmony_ci atomic_set(&pt->used, 0); 3262306a36Sopenharmony_ci return pt; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct i915_page_directory *__alloc_pd(int count) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct i915_page_directory *pd; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci pd = kzalloc(sizeof(*pd), I915_GFP_ALLOW_FAIL); 4062306a36Sopenharmony_ci if (unlikely(!pd)) 4162306a36Sopenharmony_ci return NULL; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci pd->entry = kcalloc(count, sizeof(*pd->entry), I915_GFP_ALLOW_FAIL); 4462306a36Sopenharmony_ci if (unlikely(!pd->entry)) { 4562306a36Sopenharmony_ci kfree(pd); 4662306a36Sopenharmony_ci return NULL; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci spin_lock_init(&pd->lock); 5062306a36Sopenharmony_ci return pd; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct i915_page_directory *alloc_pd(struct i915_address_space *vm) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct i915_page_directory *pd; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci pd = __alloc_pd(I915_PDES); 5862306a36Sopenharmony_ci if (unlikely(!pd)) 5962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci pd->pt.base = vm->alloc_pt_dma(vm, I915_GTT_PAGE_SIZE_4K); 6262306a36Sopenharmony_ci if (IS_ERR(pd->pt.base)) { 6362306a36Sopenharmony_ci kfree(pd->entry); 6462306a36Sopenharmony_ci kfree(pd); 6562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return pd; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_civoid free_px(struct i915_address_space *vm, struct i915_page_table *pt, int lvl) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct i915_page_directory, pt)); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (lvl) { 7662306a36Sopenharmony_ci struct i915_page_directory *pd = 7762306a36Sopenharmony_ci container_of(pt, typeof(*pd), pt); 7862306a36Sopenharmony_ci kfree(pd->entry); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (pt->base) 8262306a36Sopenharmony_ci i915_gem_object_put(pt->base); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci kfree(pt); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void 8862306a36Sopenharmony_ciwrite_dma_entry(struct drm_i915_gem_object * const pdma, 8962306a36Sopenharmony_ci const unsigned short idx, 9062306a36Sopenharmony_ci const u64 encoded_entry) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci u64 * const vaddr = __px_vaddr(pdma); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci vaddr[idx] = encoded_entry; 9562306a36Sopenharmony_ci drm_clflush_virt_range(&vaddr[idx], sizeof(u64)); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_civoid 9962306a36Sopenharmony_ci__set_pd_entry(struct i915_page_directory * const pd, 10062306a36Sopenharmony_ci const unsigned short idx, 10162306a36Sopenharmony_ci struct i915_page_table * const to, 10262306a36Sopenharmony_ci u64 (*encode)(const dma_addr_t, const enum i915_cache_level)) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci /* Each thread pre-pins the pd, and we may have a thread per pde. */ 10562306a36Sopenharmony_ci GEM_BUG_ON(atomic_read(px_used(pd)) > NALLOC * I915_PDES); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci atomic_inc(px_used(pd)); 10862306a36Sopenharmony_ci pd->entry[idx] = to; 10962306a36Sopenharmony_ci write_dma_entry(px_base(pd), idx, encode(px_dma(to), I915_CACHE_LLC)); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_civoid 11362306a36Sopenharmony_ciclear_pd_entry(struct i915_page_directory * const pd, 11462306a36Sopenharmony_ci const unsigned short idx, 11562306a36Sopenharmony_ci const struct drm_i915_gem_object * const scratch) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci GEM_BUG_ON(atomic_read(px_used(pd)) == 0); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci write_dma_entry(px_base(pd), idx, scratch->encode); 12062306a36Sopenharmony_ci pd->entry[idx] = NULL; 12162306a36Sopenharmony_ci atomic_dec(px_used(pd)); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cibool 12562306a36Sopenharmony_cirelease_pd_entry(struct i915_page_directory * const pd, 12662306a36Sopenharmony_ci const unsigned short idx, 12762306a36Sopenharmony_ci struct i915_page_table * const pt, 12862306a36Sopenharmony_ci const struct drm_i915_gem_object * const scratch) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci bool free = false; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (atomic_add_unless(&pt->used, -1, 1)) 13362306a36Sopenharmony_ci return false; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci spin_lock(&pd->lock); 13662306a36Sopenharmony_ci if (atomic_dec_and_test(&pt->used)) { 13762306a36Sopenharmony_ci clear_pd_entry(pd, idx, scratch); 13862306a36Sopenharmony_ci free = true; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci spin_unlock(&pd->lock); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return free; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ciint i915_ppgtt_init_hw(struct intel_gt *gt) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci gtt_write_workarounds(gt); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (GRAPHICS_VER(i915) == 6) 15262306a36Sopenharmony_ci gen6_ppgtt_enable(gt); 15362306a36Sopenharmony_ci else if (GRAPHICS_VER(i915) == 7) 15462306a36Sopenharmony_ci gen7_ppgtt_enable(gt); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic struct i915_ppgtt * 16062306a36Sopenharmony_ci__ppgtt_create(struct intel_gt *gt, unsigned long lmem_pt_obj_flags) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci if (GRAPHICS_VER(gt->i915) < 8) 16362306a36Sopenharmony_ci return gen6_ppgtt_create(gt); 16462306a36Sopenharmony_ci else 16562306a36Sopenharmony_ci return gen8_ppgtt_create(gt, lmem_pt_obj_flags); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistruct i915_ppgtt *i915_ppgtt_create(struct intel_gt *gt, 16962306a36Sopenharmony_ci unsigned long lmem_pt_obj_flags) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct i915_ppgtt *ppgtt; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ppgtt = __ppgtt_create(gt, lmem_pt_obj_flags); 17462306a36Sopenharmony_ci if (IS_ERR(ppgtt)) 17562306a36Sopenharmony_ci return ppgtt; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci trace_i915_ppgtt_create(&ppgtt->vm); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return ppgtt; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_civoid ppgtt_bind_vma(struct i915_address_space *vm, 18362306a36Sopenharmony_ci struct i915_vm_pt_stash *stash, 18462306a36Sopenharmony_ci struct i915_vma_resource *vma_res, 18562306a36Sopenharmony_ci unsigned int pat_index, 18662306a36Sopenharmony_ci u32 flags) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci u32 pte_flags; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (!vma_res->allocated) { 19162306a36Sopenharmony_ci vm->allocate_va_range(vm, stash, vma_res->start, 19262306a36Sopenharmony_ci vma_res->vma_size); 19362306a36Sopenharmony_ci vma_res->allocated = true; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Applicable to VLV, and gen8+ */ 19762306a36Sopenharmony_ci pte_flags = 0; 19862306a36Sopenharmony_ci if (vma_res->bi.readonly) 19962306a36Sopenharmony_ci pte_flags |= PTE_READ_ONLY; 20062306a36Sopenharmony_ci if (vma_res->bi.lmem) 20162306a36Sopenharmony_ci pte_flags |= PTE_LM; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci vm->insert_entries(vm, vma_res, pat_index, pte_flags); 20462306a36Sopenharmony_ci wmb(); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_civoid ppgtt_unbind_vma(struct i915_address_space *vm, 20862306a36Sopenharmony_ci struct i915_vma_resource *vma_res) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci if (!vma_res->allocated) 21162306a36Sopenharmony_ci return; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci vm->clear_range(vm, vma_res->start, vma_res->vma_size); 21462306a36Sopenharmony_ci vma_invalidate_tlb(vm, vma_res->tlb); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic unsigned long pd_count(u64 size, int shift) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci /* Beware later misalignment */ 22062306a36Sopenharmony_ci return (size + 2 * (BIT_ULL(shift) - 1)) >> shift; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ciint i915_vm_alloc_pt_stash(struct i915_address_space *vm, 22462306a36Sopenharmony_ci struct i915_vm_pt_stash *stash, 22562306a36Sopenharmony_ci u64 size) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci unsigned long count; 22862306a36Sopenharmony_ci int shift, n, pt_sz; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci shift = vm->pd_shift; 23162306a36Sopenharmony_ci if (!shift) 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci pt_sz = stash->pt_sz; 23562306a36Sopenharmony_ci if (!pt_sz) 23662306a36Sopenharmony_ci pt_sz = I915_GTT_PAGE_SIZE_4K; 23762306a36Sopenharmony_ci else 23862306a36Sopenharmony_ci GEM_BUG_ON(!IS_DGFX(vm->i915)); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci GEM_BUG_ON(!is_power_of_2(pt_sz)); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci count = pd_count(size, shift); 24362306a36Sopenharmony_ci while (count--) { 24462306a36Sopenharmony_ci struct i915_page_table *pt; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci pt = alloc_pt(vm, pt_sz); 24762306a36Sopenharmony_ci if (IS_ERR(pt)) { 24862306a36Sopenharmony_ci i915_vm_free_pt_stash(vm, stash); 24962306a36Sopenharmony_ci return PTR_ERR(pt); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci pt->stash = stash->pt[0]; 25362306a36Sopenharmony_ci stash->pt[0] = pt; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci for (n = 1; n < vm->top; n++) { 25762306a36Sopenharmony_ci shift += ilog2(I915_PDES); /* Each PD holds 512 entries */ 25862306a36Sopenharmony_ci count = pd_count(size, shift); 25962306a36Sopenharmony_ci while (count--) { 26062306a36Sopenharmony_ci struct i915_page_directory *pd; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci pd = alloc_pd(vm); 26362306a36Sopenharmony_ci if (IS_ERR(pd)) { 26462306a36Sopenharmony_ci i915_vm_free_pt_stash(vm, stash); 26562306a36Sopenharmony_ci return PTR_ERR(pd); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci pd->pt.stash = stash->pt[1]; 26962306a36Sopenharmony_ci stash->pt[1] = &pd->pt; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ciint i915_vm_map_pt_stash(struct i915_address_space *vm, 27762306a36Sopenharmony_ci struct i915_vm_pt_stash *stash) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct i915_page_table *pt; 28062306a36Sopenharmony_ci int n, err; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci for (n = 0; n < ARRAY_SIZE(stash->pt); n++) { 28362306a36Sopenharmony_ci for (pt = stash->pt[n]; pt; pt = pt->stash) { 28462306a36Sopenharmony_ci err = map_pt_dma_locked(vm, pt->base); 28562306a36Sopenharmony_ci if (err) 28662306a36Sopenharmony_ci return err; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_civoid i915_vm_free_pt_stash(struct i915_address_space *vm, 29462306a36Sopenharmony_ci struct i915_vm_pt_stash *stash) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct i915_page_table *pt; 29762306a36Sopenharmony_ci int n; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci for (n = 0; n < ARRAY_SIZE(stash->pt); n++) { 30062306a36Sopenharmony_ci while ((pt = stash->pt[n])) { 30162306a36Sopenharmony_ci stash->pt[n] = pt->stash; 30262306a36Sopenharmony_ci free_px(vm, pt, n); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_civoid ppgtt_init(struct i915_ppgtt *ppgtt, struct intel_gt *gt, 30862306a36Sopenharmony_ci unsigned long lmem_pt_obj_flags) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ppgtt->vm.gt = gt; 31362306a36Sopenharmony_ci ppgtt->vm.i915 = i915; 31462306a36Sopenharmony_ci ppgtt->vm.dma = i915->drm.dev; 31562306a36Sopenharmony_ci ppgtt->vm.total = BIT_ULL(RUNTIME_INFO(i915)->ppgtt_size); 31662306a36Sopenharmony_ci ppgtt->vm.lmem_pt_obj_flags = lmem_pt_obj_flags; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci dma_resv_init(&ppgtt->vm._resv); 31962306a36Sopenharmony_ci i915_address_space_init(&ppgtt->vm, VM_CLASS_PPGTT); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ppgtt->vm.vma_ops.bind_vma = ppgtt_bind_vma; 32262306a36Sopenharmony_ci ppgtt->vm.vma_ops.unbind_vma = ppgtt_unbind_vma; 32362306a36Sopenharmony_ci} 324