18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2020 Intel Corporation 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "i915_trace.h" 98c2ecf20Sopenharmony_ci#include "intel_gtt.h" 108c2ecf20Sopenharmony_ci#include "gen6_ppgtt.h" 118c2ecf20Sopenharmony_ci#include "gen8_ppgtt.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistruct i915_page_table *alloc_pt(struct i915_address_space *vm) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci struct i915_page_table *pt; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci pt = kmalloc(sizeof(*pt), I915_GFP_ALLOW_FAIL); 188c2ecf20Sopenharmony_ci if (unlikely(!pt)) 198c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci pt->base = vm->alloc_pt_dma(vm, I915_GTT_PAGE_SIZE_4K); 228c2ecf20Sopenharmony_ci if (IS_ERR(pt->base)) { 238c2ecf20Sopenharmony_ci kfree(pt); 248c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 258c2ecf20Sopenharmony_ci } 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci atomic_set(&pt->used, 0); 288c2ecf20Sopenharmony_ci return pt; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct i915_page_directory *__alloc_pd(int count) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct i915_page_directory *pd; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci pd = kzalloc(sizeof(*pd), I915_GFP_ALLOW_FAIL); 368c2ecf20Sopenharmony_ci if (unlikely(!pd)) 378c2ecf20Sopenharmony_ci return NULL; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci pd->entry = kcalloc(count, sizeof(*pd->entry), I915_GFP_ALLOW_FAIL); 408c2ecf20Sopenharmony_ci if (unlikely(!pd->entry)) { 418c2ecf20Sopenharmony_ci kfree(pd); 428c2ecf20Sopenharmony_ci return NULL; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci spin_lock_init(&pd->lock); 468c2ecf20Sopenharmony_ci return pd; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct i915_page_directory *alloc_pd(struct i915_address_space *vm) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct i915_page_directory *pd; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci pd = __alloc_pd(I915_PDES); 548c2ecf20Sopenharmony_ci if (unlikely(!pd)) 558c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci pd->pt.base = vm->alloc_pt_dma(vm, I915_GTT_PAGE_SIZE_4K); 588c2ecf20Sopenharmony_ci if (IS_ERR(pd->pt.base)) { 598c2ecf20Sopenharmony_ci kfree(pd->entry); 608c2ecf20Sopenharmony_ci kfree(pd); 618c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return pd; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_civoid free_px(struct i915_address_space *vm, struct i915_page_table *pt, int lvl) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct i915_page_directory, pt)); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (lvl) { 728c2ecf20Sopenharmony_ci struct i915_page_directory *pd = 738c2ecf20Sopenharmony_ci container_of(pt, typeof(*pd), pt); 748c2ecf20Sopenharmony_ci kfree(pd->entry); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (pt->base) 788c2ecf20Sopenharmony_ci i915_gem_object_put(pt->base); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci kfree(pt); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic inline void 848c2ecf20Sopenharmony_ciwrite_dma_entry(struct drm_i915_gem_object * const pdma, 858c2ecf20Sopenharmony_ci const unsigned short idx, 868c2ecf20Sopenharmony_ci const u64 encoded_entry) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci u64 * const vaddr = kmap_atomic(__px_page(pdma)); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci vaddr[idx] = encoded_entry; 918c2ecf20Sopenharmony_ci clflush_cache_range(&vaddr[idx], sizeof(u64)); 928c2ecf20Sopenharmony_ci kunmap_atomic(vaddr); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_civoid 968c2ecf20Sopenharmony_ci__set_pd_entry(struct i915_page_directory * const pd, 978c2ecf20Sopenharmony_ci const unsigned short idx, 988c2ecf20Sopenharmony_ci struct i915_page_table * const to, 998c2ecf20Sopenharmony_ci u64 (*encode)(const dma_addr_t, const enum i915_cache_level)) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci /* Each thread pre-pins the pd, and we may have a thread per pde. */ 1028c2ecf20Sopenharmony_ci GEM_BUG_ON(atomic_read(px_used(pd)) > NALLOC * I915_PDES); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci atomic_inc(px_used(pd)); 1058c2ecf20Sopenharmony_ci pd->entry[idx] = to; 1068c2ecf20Sopenharmony_ci write_dma_entry(px_base(pd), idx, encode(px_dma(to), I915_CACHE_LLC)); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_civoid 1108c2ecf20Sopenharmony_ciclear_pd_entry(struct i915_page_directory * const pd, 1118c2ecf20Sopenharmony_ci const unsigned short idx, 1128c2ecf20Sopenharmony_ci const struct drm_i915_gem_object * const scratch) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci GEM_BUG_ON(atomic_read(px_used(pd)) == 0); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci write_dma_entry(px_base(pd), idx, scratch->encode); 1178c2ecf20Sopenharmony_ci pd->entry[idx] = NULL; 1188c2ecf20Sopenharmony_ci atomic_dec(px_used(pd)); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cibool 1228c2ecf20Sopenharmony_cirelease_pd_entry(struct i915_page_directory * const pd, 1238c2ecf20Sopenharmony_ci const unsigned short idx, 1248c2ecf20Sopenharmony_ci struct i915_page_table * const pt, 1258c2ecf20Sopenharmony_ci const struct drm_i915_gem_object * const scratch) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci bool free = false; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (atomic_add_unless(&pt->used, -1, 1)) 1308c2ecf20Sopenharmony_ci return false; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci spin_lock(&pd->lock); 1338c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&pt->used)) { 1348c2ecf20Sopenharmony_ci clear_pd_entry(pd, idx, scratch); 1358c2ecf20Sopenharmony_ci free = true; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci spin_unlock(&pd->lock); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return free; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciint i915_ppgtt_init_hw(struct intel_gt *gt) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci gtt_write_workarounds(gt); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (IS_GEN(i915, 6)) 1498c2ecf20Sopenharmony_ci gen6_ppgtt_enable(gt); 1508c2ecf20Sopenharmony_ci else if (IS_GEN(i915, 7)) 1518c2ecf20Sopenharmony_ci gen7_ppgtt_enable(gt); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic struct i915_ppgtt * 1578c2ecf20Sopenharmony_ci__ppgtt_create(struct intel_gt *gt) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci if (INTEL_GEN(gt->i915) < 8) 1608c2ecf20Sopenharmony_ci return gen6_ppgtt_create(gt); 1618c2ecf20Sopenharmony_ci else 1628c2ecf20Sopenharmony_ci return gen8_ppgtt_create(gt); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistruct i915_ppgtt *i915_ppgtt_create(struct intel_gt *gt) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct i915_ppgtt *ppgtt; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ppgtt = __ppgtt_create(gt); 1708c2ecf20Sopenharmony_ci if (IS_ERR(ppgtt)) 1718c2ecf20Sopenharmony_ci return ppgtt; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci trace_i915_ppgtt_create(&ppgtt->vm); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return ppgtt; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_civoid ppgtt_bind_vma(struct i915_address_space *vm, 1798c2ecf20Sopenharmony_ci struct i915_vm_pt_stash *stash, 1808c2ecf20Sopenharmony_ci struct i915_vma *vma, 1818c2ecf20Sopenharmony_ci enum i915_cache_level cache_level, 1828c2ecf20Sopenharmony_ci u32 flags) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci u32 pte_flags; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!test_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma))) { 1878c2ecf20Sopenharmony_ci vm->allocate_va_range(vm, stash, vma->node.start, vma->size); 1888c2ecf20Sopenharmony_ci set_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Applicable to VLV, and gen8+ */ 1928c2ecf20Sopenharmony_ci pte_flags = 0; 1938c2ecf20Sopenharmony_ci if (i915_gem_object_is_readonly(vma->obj)) 1948c2ecf20Sopenharmony_ci pte_flags |= PTE_READ_ONLY; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci vm->insert_entries(vm, vma, cache_level, pte_flags); 1978c2ecf20Sopenharmony_ci wmb(); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_civoid ppgtt_unbind_vma(struct i915_address_space *vm, struct i915_vma *vma) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma))) 2038c2ecf20Sopenharmony_ci vm->clear_range(vm, vma->node.start, vma->size); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic unsigned long pd_count(u64 size, int shift) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci /* Beware later misalignment */ 2098c2ecf20Sopenharmony_ci return (size + 2 * (BIT_ULL(shift) - 1)) >> shift; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ciint i915_vm_alloc_pt_stash(struct i915_address_space *vm, 2138c2ecf20Sopenharmony_ci struct i915_vm_pt_stash *stash, 2148c2ecf20Sopenharmony_ci u64 size) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci unsigned long count; 2178c2ecf20Sopenharmony_ci int shift, n; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci shift = vm->pd_shift; 2208c2ecf20Sopenharmony_ci if (!shift) 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci count = pd_count(size, shift); 2248c2ecf20Sopenharmony_ci while (count--) { 2258c2ecf20Sopenharmony_ci struct i915_page_table *pt; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci pt = alloc_pt(vm); 2288c2ecf20Sopenharmony_ci if (IS_ERR(pt)) { 2298c2ecf20Sopenharmony_ci i915_vm_free_pt_stash(vm, stash); 2308c2ecf20Sopenharmony_ci return PTR_ERR(pt); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci pt->stash = stash->pt[0]; 2348c2ecf20Sopenharmony_ci stash->pt[0] = pt; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci for (n = 1; n < vm->top; n++) { 2388c2ecf20Sopenharmony_ci shift += ilog2(I915_PDES); /* Each PD holds 512 entries */ 2398c2ecf20Sopenharmony_ci count = pd_count(size, shift); 2408c2ecf20Sopenharmony_ci while (count--) { 2418c2ecf20Sopenharmony_ci struct i915_page_directory *pd; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci pd = alloc_pd(vm); 2448c2ecf20Sopenharmony_ci if (IS_ERR(pd)) { 2458c2ecf20Sopenharmony_ci i915_vm_free_pt_stash(vm, stash); 2468c2ecf20Sopenharmony_ci return PTR_ERR(pd); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci pd->pt.stash = stash->pt[1]; 2508c2ecf20Sopenharmony_ci stash->pt[1] = &pd->pt; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ciint i915_vm_pin_pt_stash(struct i915_address_space *vm, 2588c2ecf20Sopenharmony_ci struct i915_vm_pt_stash *stash) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct i915_page_table *pt; 2618c2ecf20Sopenharmony_ci int n, err; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci for (n = 0; n < ARRAY_SIZE(stash->pt); n++) { 2648c2ecf20Sopenharmony_ci for (pt = stash->pt[n]; pt; pt = pt->stash) { 2658c2ecf20Sopenharmony_ci err = pin_pt_dma(vm, pt->base); 2668c2ecf20Sopenharmony_ci if (err) 2678c2ecf20Sopenharmony_ci return err; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_civoid i915_vm_free_pt_stash(struct i915_address_space *vm, 2758c2ecf20Sopenharmony_ci struct i915_vm_pt_stash *stash) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct i915_page_table *pt; 2788c2ecf20Sopenharmony_ci int n; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci for (n = 0; n < ARRAY_SIZE(stash->pt); n++) { 2818c2ecf20Sopenharmony_ci while ((pt = stash->pt[n])) { 2828c2ecf20Sopenharmony_ci stash->pt[n] = pt->stash; 2838c2ecf20Sopenharmony_ci free_px(vm, pt, n); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ciint ppgtt_set_pages(struct i915_vma *vma) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci GEM_BUG_ON(vma->pages); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci vma->pages = vma->obj->mm.pages; 2938c2ecf20Sopenharmony_ci vma->page_sizes = vma->obj->mm.page_sizes; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_civoid ppgtt_init(struct i915_ppgtt *ppgtt, struct intel_gt *gt) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ppgtt->vm.gt = gt; 3038c2ecf20Sopenharmony_ci ppgtt->vm.i915 = i915; 3048c2ecf20Sopenharmony_ci ppgtt->vm.dma = &i915->drm.pdev->dev; 3058c2ecf20Sopenharmony_ci ppgtt->vm.total = BIT_ULL(INTEL_INFO(i915)->ppgtt_size); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci i915_address_space_init(&ppgtt->vm, VM_CLASS_PPGTT); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ppgtt->vm.vma_ops.bind_vma = ppgtt_bind_vma; 3108c2ecf20Sopenharmony_ci ppgtt->vm.vma_ops.unbind_vma = ppgtt_unbind_vma; 3118c2ecf20Sopenharmony_ci ppgtt->vm.vma_ops.set_pages = ppgtt_set_pages; 3128c2ecf20Sopenharmony_ci ppgtt->vm.vma_ops.clear_pages = clear_pages; 3138c2ecf20Sopenharmony_ci} 314