162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright © 2008-2015 Intel Corporation 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next 1262306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 1362306a36Sopenharmony_ci * Software. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1862306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1962306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2062306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 2162306a36Sopenharmony_ci * IN THE SOFTWARE. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Authors: 2462306a36Sopenharmony_ci * Eric Anholt <eric@anholt.net> 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/dma-fence-array.h> 2962306a36Sopenharmony_ci#include <linux/kthread.h> 3062306a36Sopenharmony_ci#include <linux/dma-resv.h> 3162306a36Sopenharmony_ci#include <linux/shmem_fs.h> 3262306a36Sopenharmony_ci#include <linux/slab.h> 3362306a36Sopenharmony_ci#include <linux/stop_machine.h> 3462306a36Sopenharmony_ci#include <linux/swap.h> 3562306a36Sopenharmony_ci#include <linux/pci.h> 3662306a36Sopenharmony_ci#include <linux/dma-buf.h> 3762306a36Sopenharmony_ci#include <linux/mman.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <drm/drm_cache.h> 4062306a36Sopenharmony_ci#include <drm/drm_vma_manager.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include "display/intel_display.h" 4362306a36Sopenharmony_ci#include "display/intel_frontbuffer.h" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include "gem/i915_gem_clflush.h" 4662306a36Sopenharmony_ci#include "gem/i915_gem_context.h" 4762306a36Sopenharmony_ci#include "gem/i915_gem_ioctls.h" 4862306a36Sopenharmony_ci#include "gem/i915_gem_mman.h" 4962306a36Sopenharmony_ci#include "gem/i915_gem_pm.h" 5062306a36Sopenharmony_ci#include "gem/i915_gem_region.h" 5162306a36Sopenharmony_ci#include "gem/i915_gem_userptr.h" 5262306a36Sopenharmony_ci#include "gt/intel_engine_user.h" 5362306a36Sopenharmony_ci#include "gt/intel_gt.h" 5462306a36Sopenharmony_ci#include "gt/intel_gt_pm.h" 5562306a36Sopenharmony_ci#include "gt/intel_workarounds.h" 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#include "i915_drv.h" 5862306a36Sopenharmony_ci#include "i915_file_private.h" 5962306a36Sopenharmony_ci#include "i915_trace.h" 6062306a36Sopenharmony_ci#include "i915_vgpu.h" 6162306a36Sopenharmony_ci#include "intel_clock_gating.h" 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int 6462306a36Sopenharmony_ciinsert_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node, u32 size) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci int err; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci err = mutex_lock_interruptible(&ggtt->vm.mutex); 6962306a36Sopenharmony_ci if (err) 7062306a36Sopenharmony_ci return err; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci memset(node, 0, sizeof(*node)); 7362306a36Sopenharmony_ci err = drm_mm_insert_node_in_range(&ggtt->vm.mm, node, 7462306a36Sopenharmony_ci size, 0, I915_COLOR_UNEVICTABLE, 7562306a36Sopenharmony_ci 0, ggtt->mappable_end, 7662306a36Sopenharmony_ci DRM_MM_INSERT_LOW); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci mutex_unlock(&ggtt->vm.mutex); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return err; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void 8462306a36Sopenharmony_ciremove_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci mutex_lock(&ggtt->vm.mutex); 8762306a36Sopenharmony_ci drm_mm_remove_node(node); 8862306a36Sopenharmony_ci mutex_unlock(&ggtt->vm.mutex); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciint 9262306a36Sopenharmony_cii915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, 9362306a36Sopenharmony_ci struct drm_file *file) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct drm_i915_private *i915 = to_i915(dev); 9662306a36Sopenharmony_ci struct i915_ggtt *ggtt = to_gt(i915)->ggtt; 9762306a36Sopenharmony_ci struct drm_i915_gem_get_aperture *args = data; 9862306a36Sopenharmony_ci struct i915_vma *vma; 9962306a36Sopenharmony_ci u64 pinned; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (mutex_lock_interruptible(&ggtt->vm.mutex)) 10262306a36Sopenharmony_ci return -EINTR; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci pinned = ggtt->vm.reserved; 10562306a36Sopenharmony_ci list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link) 10662306a36Sopenharmony_ci if (i915_vma_is_pinned(vma)) 10762306a36Sopenharmony_ci pinned += vma->node.size; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci mutex_unlock(&ggtt->vm.mutex); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci args->aper_size = ggtt->vm.total; 11262306a36Sopenharmony_ci args->aper_available_size = args->aper_size - pinned; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciint i915_gem_object_unbind(struct drm_i915_gem_object *obj, 11862306a36Sopenharmony_ci unsigned long flags) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct intel_runtime_pm *rpm = &to_i915(obj->base.dev)->runtime_pm; 12162306a36Sopenharmony_ci bool vm_trylock = !!(flags & I915_GEM_OBJECT_UNBIND_VM_TRYLOCK); 12262306a36Sopenharmony_ci LIST_HEAD(still_in_list); 12362306a36Sopenharmony_ci intel_wakeref_t wakeref; 12462306a36Sopenharmony_ci struct i915_vma *vma; 12562306a36Sopenharmony_ci int ret; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci assert_object_held(obj); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (list_empty(&obj->vma.list)) 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * As some machines use ACPI to handle runtime-resume callbacks, and 13462306a36Sopenharmony_ci * ACPI is quite kmalloc happy, we cannot resume beneath the vm->mutex 13562306a36Sopenharmony_ci * as they are required by the shrinker. Ergo, we wake the device up 13662306a36Sopenharmony_ci * first just in case. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci wakeref = intel_runtime_pm_get(rpm); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_citry_again: 14162306a36Sopenharmony_ci ret = 0; 14262306a36Sopenharmony_ci spin_lock(&obj->vma.lock); 14362306a36Sopenharmony_ci while (!ret && (vma = list_first_entry_or_null(&obj->vma.list, 14462306a36Sopenharmony_ci struct i915_vma, 14562306a36Sopenharmony_ci obj_link))) { 14662306a36Sopenharmony_ci list_move_tail(&vma->obj_link, &still_in_list); 14762306a36Sopenharmony_ci if (!i915_vma_is_bound(vma, I915_VMA_BIND_MASK)) 14862306a36Sopenharmony_ci continue; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (flags & I915_GEM_OBJECT_UNBIND_TEST) { 15162306a36Sopenharmony_ci ret = -EBUSY; 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * Requiring the vm destructor to take the object lock 15762306a36Sopenharmony_ci * before destroying a vma would help us eliminate the 15862306a36Sopenharmony_ci * i915_vm_tryget() here, AND thus also the barrier stuff 15962306a36Sopenharmony_ci * at the end. That's an easy fix, but sleeping locks in 16062306a36Sopenharmony_ci * a kthread should generally be avoided. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci ret = -EAGAIN; 16362306a36Sopenharmony_ci if (!i915_vm_tryget(vma->vm)) 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci spin_unlock(&obj->vma.lock); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * Since i915_vma_parked() takes the object lock 17062306a36Sopenharmony_ci * before vma destruction, it won't race us here, 17162306a36Sopenharmony_ci * and destroy the vma from under us. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ret = -EBUSY; 17562306a36Sopenharmony_ci if (flags & I915_GEM_OBJECT_UNBIND_ASYNC) { 17662306a36Sopenharmony_ci assert_object_held(vma->obj); 17762306a36Sopenharmony_ci ret = i915_vma_unbind_async(vma, vm_trylock); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (ret == -EBUSY && (flags & I915_GEM_OBJECT_UNBIND_ACTIVE || 18162306a36Sopenharmony_ci !i915_vma_is_active(vma))) { 18262306a36Sopenharmony_ci if (vm_trylock) { 18362306a36Sopenharmony_ci if (mutex_trylock(&vma->vm->mutex)) { 18462306a36Sopenharmony_ci ret = __i915_vma_unbind(vma); 18562306a36Sopenharmony_ci mutex_unlock(&vma->vm->mutex); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } else { 18862306a36Sopenharmony_ci ret = i915_vma_unbind(vma); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci i915_vm_put(vma->vm); 19362306a36Sopenharmony_ci spin_lock(&obj->vma.lock); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci list_splice_init(&still_in_list, &obj->vma.list); 19662306a36Sopenharmony_ci spin_unlock(&obj->vma.lock); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (ret == -EAGAIN && flags & I915_GEM_OBJECT_UNBIND_BARRIER) { 19962306a36Sopenharmony_ci rcu_barrier(); /* flush the i915_vm_release() */ 20062306a36Sopenharmony_ci goto try_again; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci intel_runtime_pm_put(rpm, wakeref); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return ret; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int 20962306a36Sopenharmony_cishmem_pread(struct page *page, int offset, int len, char __user *user_data, 21062306a36Sopenharmony_ci bool needs_clflush) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci char *vaddr; 21362306a36Sopenharmony_ci int ret; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci vaddr = kmap(page); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (needs_clflush) 21862306a36Sopenharmony_ci drm_clflush_virt_range(vaddr + offset, len); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = __copy_to_user(user_data, vaddr + offset, len); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci kunmap(page); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return ret ? -EFAULT : 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int 22862306a36Sopenharmony_cii915_gem_shmem_pread(struct drm_i915_gem_object *obj, 22962306a36Sopenharmony_ci struct drm_i915_gem_pread *args) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci unsigned int needs_clflush; 23262306a36Sopenharmony_ci char __user *user_data; 23362306a36Sopenharmony_ci unsigned long offset; 23462306a36Sopenharmony_ci pgoff_t idx; 23562306a36Sopenharmony_ci u64 remain; 23662306a36Sopenharmony_ci int ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = i915_gem_object_lock_interruptible(obj, NULL); 23962306a36Sopenharmony_ci if (ret) 24062306a36Sopenharmony_ci return ret; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ret = i915_gem_object_pin_pages(obj); 24362306a36Sopenharmony_ci if (ret) 24462306a36Sopenharmony_ci goto err_unlock; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci ret = i915_gem_object_prepare_read(obj, &needs_clflush); 24762306a36Sopenharmony_ci if (ret) 24862306a36Sopenharmony_ci goto err_unpin; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci i915_gem_object_finish_access(obj); 25162306a36Sopenharmony_ci i915_gem_object_unlock(obj); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci remain = args->size; 25462306a36Sopenharmony_ci user_data = u64_to_user_ptr(args->data_ptr); 25562306a36Sopenharmony_ci offset = offset_in_page(args->offset); 25662306a36Sopenharmony_ci for (idx = args->offset >> PAGE_SHIFT; remain; idx++) { 25762306a36Sopenharmony_ci struct page *page = i915_gem_object_get_page(obj, idx); 25862306a36Sopenharmony_ci unsigned int length = min_t(u64, remain, PAGE_SIZE - offset); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ret = shmem_pread(page, offset, length, user_data, 26162306a36Sopenharmony_ci needs_clflush); 26262306a36Sopenharmony_ci if (ret) 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci remain -= length; 26662306a36Sopenharmony_ci user_data += length; 26762306a36Sopenharmony_ci offset = 0; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci i915_gem_object_unpin_pages(obj); 27162306a36Sopenharmony_ci return ret; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cierr_unpin: 27462306a36Sopenharmony_ci i915_gem_object_unpin_pages(obj); 27562306a36Sopenharmony_cierr_unlock: 27662306a36Sopenharmony_ci i915_gem_object_unlock(obj); 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic inline bool 28162306a36Sopenharmony_cigtt_user_read(struct io_mapping *mapping, 28262306a36Sopenharmony_ci loff_t base, int offset, 28362306a36Sopenharmony_ci char __user *user_data, int length) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci void __iomem *vaddr; 28662306a36Sopenharmony_ci unsigned long unwritten; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* We can use the cpu mem copy function because this is X86. */ 28962306a36Sopenharmony_ci vaddr = io_mapping_map_atomic_wc(mapping, base); 29062306a36Sopenharmony_ci unwritten = __copy_to_user_inatomic(user_data, 29162306a36Sopenharmony_ci (void __force *)vaddr + offset, 29262306a36Sopenharmony_ci length); 29362306a36Sopenharmony_ci io_mapping_unmap_atomic(vaddr); 29462306a36Sopenharmony_ci if (unwritten) { 29562306a36Sopenharmony_ci vaddr = io_mapping_map_wc(mapping, base, PAGE_SIZE); 29662306a36Sopenharmony_ci unwritten = copy_to_user(user_data, 29762306a36Sopenharmony_ci (void __force *)vaddr + offset, 29862306a36Sopenharmony_ci length); 29962306a36Sopenharmony_ci io_mapping_unmap(vaddr); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci return unwritten; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic struct i915_vma *i915_gem_gtt_prepare(struct drm_i915_gem_object *obj, 30562306a36Sopenharmony_ci struct drm_mm_node *node, 30662306a36Sopenharmony_ci bool write) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct drm_i915_private *i915 = to_i915(obj->base.dev); 30962306a36Sopenharmony_ci struct i915_ggtt *ggtt = to_gt(i915)->ggtt; 31062306a36Sopenharmony_ci struct i915_vma *vma; 31162306a36Sopenharmony_ci struct i915_gem_ww_ctx ww; 31262306a36Sopenharmony_ci int ret; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci i915_gem_ww_ctx_init(&ww, true); 31562306a36Sopenharmony_ciretry: 31662306a36Sopenharmony_ci vma = ERR_PTR(-ENODEV); 31762306a36Sopenharmony_ci ret = i915_gem_object_lock(obj, &ww); 31862306a36Sopenharmony_ci if (ret) 31962306a36Sopenharmony_ci goto err_ww; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ret = i915_gem_object_set_to_gtt_domain(obj, write); 32262306a36Sopenharmony_ci if (ret) 32362306a36Sopenharmony_ci goto err_ww; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!i915_gem_object_is_tiled(obj)) 32662306a36Sopenharmony_ci vma = i915_gem_object_ggtt_pin_ww(obj, &ww, NULL, 0, 0, 32762306a36Sopenharmony_ci PIN_MAPPABLE | 32862306a36Sopenharmony_ci PIN_NONBLOCK /* NOWARN */ | 32962306a36Sopenharmony_ci PIN_NOEVICT); 33062306a36Sopenharmony_ci if (vma == ERR_PTR(-EDEADLK)) { 33162306a36Sopenharmony_ci ret = -EDEADLK; 33262306a36Sopenharmony_ci goto err_ww; 33362306a36Sopenharmony_ci } else if (!IS_ERR(vma)) { 33462306a36Sopenharmony_ci node->start = i915_ggtt_offset(vma); 33562306a36Sopenharmony_ci node->flags = 0; 33662306a36Sopenharmony_ci } else { 33762306a36Sopenharmony_ci ret = insert_mappable_node(ggtt, node, PAGE_SIZE); 33862306a36Sopenharmony_ci if (ret) 33962306a36Sopenharmony_ci goto err_ww; 34062306a36Sopenharmony_ci GEM_BUG_ON(!drm_mm_node_allocated(node)); 34162306a36Sopenharmony_ci vma = NULL; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci ret = i915_gem_object_pin_pages(obj); 34562306a36Sopenharmony_ci if (ret) { 34662306a36Sopenharmony_ci if (drm_mm_node_allocated(node)) { 34762306a36Sopenharmony_ci ggtt->vm.clear_range(&ggtt->vm, node->start, node->size); 34862306a36Sopenharmony_ci remove_mappable_node(ggtt, node); 34962306a36Sopenharmony_ci } else { 35062306a36Sopenharmony_ci i915_vma_unpin(vma); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cierr_ww: 35562306a36Sopenharmony_ci if (ret == -EDEADLK) { 35662306a36Sopenharmony_ci ret = i915_gem_ww_ctx_backoff(&ww); 35762306a36Sopenharmony_ci if (!ret) 35862306a36Sopenharmony_ci goto retry; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci i915_gem_ww_ctx_fini(&ww); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return ret ? ERR_PTR(ret) : vma; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void i915_gem_gtt_cleanup(struct drm_i915_gem_object *obj, 36662306a36Sopenharmony_ci struct drm_mm_node *node, 36762306a36Sopenharmony_ci struct i915_vma *vma) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct drm_i915_private *i915 = to_i915(obj->base.dev); 37062306a36Sopenharmony_ci struct i915_ggtt *ggtt = to_gt(i915)->ggtt; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci i915_gem_object_unpin_pages(obj); 37362306a36Sopenharmony_ci if (drm_mm_node_allocated(node)) { 37462306a36Sopenharmony_ci ggtt->vm.clear_range(&ggtt->vm, node->start, node->size); 37562306a36Sopenharmony_ci remove_mappable_node(ggtt, node); 37662306a36Sopenharmony_ci } else { 37762306a36Sopenharmony_ci i915_vma_unpin(vma); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int 38262306a36Sopenharmony_cii915_gem_gtt_pread(struct drm_i915_gem_object *obj, 38362306a36Sopenharmony_ci const struct drm_i915_gem_pread *args) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct drm_i915_private *i915 = to_i915(obj->base.dev); 38662306a36Sopenharmony_ci struct i915_ggtt *ggtt = to_gt(i915)->ggtt; 38762306a36Sopenharmony_ci unsigned long remain, offset; 38862306a36Sopenharmony_ci intel_wakeref_t wakeref; 38962306a36Sopenharmony_ci struct drm_mm_node node; 39062306a36Sopenharmony_ci void __user *user_data; 39162306a36Sopenharmony_ci struct i915_vma *vma; 39262306a36Sopenharmony_ci int ret = 0; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (overflows_type(args->size, remain) || 39562306a36Sopenharmony_ci overflows_type(args->offset, offset)) 39662306a36Sopenharmony_ci return -EINVAL; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci wakeref = intel_runtime_pm_get(&i915->runtime_pm); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci vma = i915_gem_gtt_prepare(obj, &node, false); 40162306a36Sopenharmony_ci if (IS_ERR(vma)) { 40262306a36Sopenharmony_ci ret = PTR_ERR(vma); 40362306a36Sopenharmony_ci goto out_rpm; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci user_data = u64_to_user_ptr(args->data_ptr); 40762306a36Sopenharmony_ci remain = args->size; 40862306a36Sopenharmony_ci offset = args->offset; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci while (remain > 0) { 41162306a36Sopenharmony_ci /* Operation in this page 41262306a36Sopenharmony_ci * 41362306a36Sopenharmony_ci * page_base = page offset within aperture 41462306a36Sopenharmony_ci * page_offset = offset within page 41562306a36Sopenharmony_ci * page_length = bytes to copy for this page 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci u32 page_base = node.start; 41862306a36Sopenharmony_ci unsigned page_offset = offset_in_page(offset); 41962306a36Sopenharmony_ci unsigned page_length = PAGE_SIZE - page_offset; 42062306a36Sopenharmony_ci page_length = remain < page_length ? remain : page_length; 42162306a36Sopenharmony_ci if (drm_mm_node_allocated(&node)) { 42262306a36Sopenharmony_ci ggtt->vm.insert_page(&ggtt->vm, 42362306a36Sopenharmony_ci i915_gem_object_get_dma_address(obj, 42462306a36Sopenharmony_ci offset >> PAGE_SHIFT), 42562306a36Sopenharmony_ci node.start, 42662306a36Sopenharmony_ci i915_gem_get_pat_index(i915, 42762306a36Sopenharmony_ci I915_CACHE_NONE), 0); 42862306a36Sopenharmony_ci } else { 42962306a36Sopenharmony_ci page_base += offset & PAGE_MASK; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (gtt_user_read(&ggtt->iomap, page_base, page_offset, 43362306a36Sopenharmony_ci user_data, page_length)) { 43462306a36Sopenharmony_ci ret = -EFAULT; 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci remain -= page_length; 43962306a36Sopenharmony_ci user_data += page_length; 44062306a36Sopenharmony_ci offset += page_length; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci i915_gem_gtt_cleanup(obj, &node, vma); 44462306a36Sopenharmony_ciout_rpm: 44562306a36Sopenharmony_ci intel_runtime_pm_put(&i915->runtime_pm, wakeref); 44662306a36Sopenharmony_ci return ret; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/** 45062306a36Sopenharmony_ci * i915_gem_pread_ioctl - Reads data from the object referenced by handle. 45162306a36Sopenharmony_ci * @dev: drm device pointer 45262306a36Sopenharmony_ci * @data: ioctl data blob 45362306a36Sopenharmony_ci * @file: drm file pointer 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * On error, the contents of *data are undefined. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_ciint 45862306a36Sopenharmony_cii915_gem_pread_ioctl(struct drm_device *dev, void *data, 45962306a36Sopenharmony_ci struct drm_file *file) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct drm_i915_private *i915 = to_i915(dev); 46262306a36Sopenharmony_ci struct drm_i915_gem_pread *args = data; 46362306a36Sopenharmony_ci struct drm_i915_gem_object *obj; 46462306a36Sopenharmony_ci int ret; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* PREAD is disallowed for all platforms after TGL-LP. This also 46762306a36Sopenharmony_ci * covers all platforms with local memory. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci if (GRAPHICS_VER(i915) >= 12 && !IS_TIGERLAKE(i915)) 47062306a36Sopenharmony_ci return -EOPNOTSUPP; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (args->size == 0) 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (!access_ok(u64_to_user_ptr(args->data_ptr), 47662306a36Sopenharmony_ci args->size)) 47762306a36Sopenharmony_ci return -EFAULT; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci obj = i915_gem_object_lookup(file, args->handle); 48062306a36Sopenharmony_ci if (!obj) 48162306a36Sopenharmony_ci return -ENOENT; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* Bounds check source. */ 48462306a36Sopenharmony_ci if (range_overflows_t(u64, args->offset, args->size, obj->base.size)) { 48562306a36Sopenharmony_ci ret = -EINVAL; 48662306a36Sopenharmony_ci goto out; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci trace_i915_gem_object_pread(obj, args->offset, args->size); 49062306a36Sopenharmony_ci ret = -ENODEV; 49162306a36Sopenharmony_ci if (obj->ops->pread) 49262306a36Sopenharmony_ci ret = obj->ops->pread(obj, args); 49362306a36Sopenharmony_ci if (ret != -ENODEV) 49462306a36Sopenharmony_ci goto out; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ret = i915_gem_object_wait(obj, 49762306a36Sopenharmony_ci I915_WAIT_INTERRUPTIBLE, 49862306a36Sopenharmony_ci MAX_SCHEDULE_TIMEOUT); 49962306a36Sopenharmony_ci if (ret) 50062306a36Sopenharmony_ci goto out; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci ret = i915_gem_shmem_pread(obj, args); 50362306a36Sopenharmony_ci if (ret == -EFAULT || ret == -ENODEV) 50462306a36Sopenharmony_ci ret = i915_gem_gtt_pread(obj, args); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciout: 50762306a36Sopenharmony_ci i915_gem_object_put(obj); 50862306a36Sopenharmony_ci return ret; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/* This is the fast write path which cannot handle 51262306a36Sopenharmony_ci * page faults in the source data 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic inline bool 51662306a36Sopenharmony_ciggtt_write(struct io_mapping *mapping, 51762306a36Sopenharmony_ci loff_t base, int offset, 51862306a36Sopenharmony_ci char __user *user_data, int length) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci void __iomem *vaddr; 52162306a36Sopenharmony_ci unsigned long unwritten; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* We can use the cpu mem copy function because this is X86. */ 52462306a36Sopenharmony_ci vaddr = io_mapping_map_atomic_wc(mapping, base); 52562306a36Sopenharmony_ci unwritten = __copy_from_user_inatomic_nocache((void __force *)vaddr + offset, 52662306a36Sopenharmony_ci user_data, length); 52762306a36Sopenharmony_ci io_mapping_unmap_atomic(vaddr); 52862306a36Sopenharmony_ci if (unwritten) { 52962306a36Sopenharmony_ci vaddr = io_mapping_map_wc(mapping, base, PAGE_SIZE); 53062306a36Sopenharmony_ci unwritten = copy_from_user((void __force *)vaddr + offset, 53162306a36Sopenharmony_ci user_data, length); 53262306a36Sopenharmony_ci io_mapping_unmap(vaddr); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return unwritten; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci/** 53962306a36Sopenharmony_ci * i915_gem_gtt_pwrite_fast - This is the fast pwrite path, where we copy the data directly from the 54062306a36Sopenharmony_ci * user into the GTT, uncached. 54162306a36Sopenharmony_ci * @obj: i915 GEM object 54262306a36Sopenharmony_ci * @args: pwrite arguments structure 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_cistatic int 54562306a36Sopenharmony_cii915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj, 54662306a36Sopenharmony_ci const struct drm_i915_gem_pwrite *args) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct drm_i915_private *i915 = to_i915(obj->base.dev); 54962306a36Sopenharmony_ci struct i915_ggtt *ggtt = to_gt(i915)->ggtt; 55062306a36Sopenharmony_ci struct intel_runtime_pm *rpm = &i915->runtime_pm; 55162306a36Sopenharmony_ci unsigned long remain, offset; 55262306a36Sopenharmony_ci intel_wakeref_t wakeref; 55362306a36Sopenharmony_ci struct drm_mm_node node; 55462306a36Sopenharmony_ci struct i915_vma *vma; 55562306a36Sopenharmony_ci void __user *user_data; 55662306a36Sopenharmony_ci int ret = 0; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (overflows_type(args->size, remain) || 55962306a36Sopenharmony_ci overflows_type(args->offset, offset)) 56062306a36Sopenharmony_ci return -EINVAL; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (i915_gem_object_has_struct_page(obj)) { 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * Avoid waking the device up if we can fallback, as 56562306a36Sopenharmony_ci * waking/resuming is very slow (worst-case 10-100 ms 56662306a36Sopenharmony_ci * depending on PCI sleeps and our own resume time). 56762306a36Sopenharmony_ci * This easily dwarfs any performance advantage from 56862306a36Sopenharmony_ci * using the cache bypass of indirect GGTT access. 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_ci wakeref = intel_runtime_pm_get_if_in_use(rpm); 57162306a36Sopenharmony_ci if (!wakeref) 57262306a36Sopenharmony_ci return -EFAULT; 57362306a36Sopenharmony_ci } else { 57462306a36Sopenharmony_ci /* No backing pages, no fallback, we must force GGTT access */ 57562306a36Sopenharmony_ci wakeref = intel_runtime_pm_get(rpm); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci vma = i915_gem_gtt_prepare(obj, &node, true); 57962306a36Sopenharmony_ci if (IS_ERR(vma)) { 58062306a36Sopenharmony_ci ret = PTR_ERR(vma); 58162306a36Sopenharmony_ci goto out_rpm; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci user_data = u64_to_user_ptr(args->data_ptr); 58762306a36Sopenharmony_ci offset = args->offset; 58862306a36Sopenharmony_ci remain = args->size; 58962306a36Sopenharmony_ci while (remain) { 59062306a36Sopenharmony_ci /* Operation in this page 59162306a36Sopenharmony_ci * 59262306a36Sopenharmony_ci * page_base = page offset within aperture 59362306a36Sopenharmony_ci * page_offset = offset within page 59462306a36Sopenharmony_ci * page_length = bytes to copy for this page 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci u32 page_base = node.start; 59762306a36Sopenharmony_ci unsigned int page_offset = offset_in_page(offset); 59862306a36Sopenharmony_ci unsigned int page_length = PAGE_SIZE - page_offset; 59962306a36Sopenharmony_ci page_length = remain < page_length ? remain : page_length; 60062306a36Sopenharmony_ci if (drm_mm_node_allocated(&node)) { 60162306a36Sopenharmony_ci /* flush the write before we modify the GGTT */ 60262306a36Sopenharmony_ci intel_gt_flush_ggtt_writes(ggtt->vm.gt); 60362306a36Sopenharmony_ci ggtt->vm.insert_page(&ggtt->vm, 60462306a36Sopenharmony_ci i915_gem_object_get_dma_address(obj, 60562306a36Sopenharmony_ci offset >> PAGE_SHIFT), 60662306a36Sopenharmony_ci node.start, 60762306a36Sopenharmony_ci i915_gem_get_pat_index(i915, 60862306a36Sopenharmony_ci I915_CACHE_NONE), 0); 60962306a36Sopenharmony_ci wmb(); /* flush modifications to the GGTT (insert_page) */ 61062306a36Sopenharmony_ci } else { 61162306a36Sopenharmony_ci page_base += offset & PAGE_MASK; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci /* If we get a fault while copying data, then (presumably) our 61462306a36Sopenharmony_ci * source page isn't available. Return the error and we'll 61562306a36Sopenharmony_ci * retry in the slow path. 61662306a36Sopenharmony_ci * If the object is non-shmem backed, we retry again with the 61762306a36Sopenharmony_ci * path that handles page fault. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci if (ggtt_write(&ggtt->iomap, page_base, page_offset, 62062306a36Sopenharmony_ci user_data, page_length)) { 62162306a36Sopenharmony_ci ret = -EFAULT; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci remain -= page_length; 62662306a36Sopenharmony_ci user_data += page_length; 62762306a36Sopenharmony_ci offset += page_length; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci intel_gt_flush_ggtt_writes(ggtt->vm.gt); 63162306a36Sopenharmony_ci i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci i915_gem_gtt_cleanup(obj, &node, vma); 63462306a36Sopenharmony_ciout_rpm: 63562306a36Sopenharmony_ci intel_runtime_pm_put(rpm, wakeref); 63662306a36Sopenharmony_ci return ret; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci/* Per-page copy function for the shmem pwrite fastpath. 64062306a36Sopenharmony_ci * Flushes invalid cachelines before writing to the target if 64162306a36Sopenharmony_ci * needs_clflush_before is set and flushes out any written cachelines after 64262306a36Sopenharmony_ci * writing if needs_clflush is set. 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_cistatic int 64562306a36Sopenharmony_cishmem_pwrite(struct page *page, int offset, int len, char __user *user_data, 64662306a36Sopenharmony_ci bool needs_clflush_before, 64762306a36Sopenharmony_ci bool needs_clflush_after) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci char *vaddr; 65062306a36Sopenharmony_ci int ret; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci vaddr = kmap(page); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (needs_clflush_before) 65562306a36Sopenharmony_ci drm_clflush_virt_range(vaddr + offset, len); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ret = __copy_from_user(vaddr + offset, user_data, len); 65862306a36Sopenharmony_ci if (!ret && needs_clflush_after) 65962306a36Sopenharmony_ci drm_clflush_virt_range(vaddr + offset, len); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci kunmap(page); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return ret ? -EFAULT : 0; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic int 66762306a36Sopenharmony_cii915_gem_shmem_pwrite(struct drm_i915_gem_object *obj, 66862306a36Sopenharmony_ci const struct drm_i915_gem_pwrite *args) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci unsigned int partial_cacheline_write; 67162306a36Sopenharmony_ci unsigned int needs_clflush; 67262306a36Sopenharmony_ci void __user *user_data; 67362306a36Sopenharmony_ci unsigned long offset; 67462306a36Sopenharmony_ci pgoff_t idx; 67562306a36Sopenharmony_ci u64 remain; 67662306a36Sopenharmony_ci int ret; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci ret = i915_gem_object_lock_interruptible(obj, NULL); 67962306a36Sopenharmony_ci if (ret) 68062306a36Sopenharmony_ci return ret; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci ret = i915_gem_object_pin_pages(obj); 68362306a36Sopenharmony_ci if (ret) 68462306a36Sopenharmony_ci goto err_unlock; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci ret = i915_gem_object_prepare_write(obj, &needs_clflush); 68762306a36Sopenharmony_ci if (ret) 68862306a36Sopenharmony_ci goto err_unpin; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci i915_gem_object_finish_access(obj); 69162306a36Sopenharmony_ci i915_gem_object_unlock(obj); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* If we don't overwrite a cacheline completely we need to be 69462306a36Sopenharmony_ci * careful to have up-to-date data by first clflushing. Don't 69562306a36Sopenharmony_ci * overcomplicate things and flush the entire patch. 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_ci partial_cacheline_write = 0; 69862306a36Sopenharmony_ci if (needs_clflush & CLFLUSH_BEFORE) 69962306a36Sopenharmony_ci partial_cacheline_write = boot_cpu_data.x86_clflush_size - 1; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci user_data = u64_to_user_ptr(args->data_ptr); 70262306a36Sopenharmony_ci remain = args->size; 70362306a36Sopenharmony_ci offset = offset_in_page(args->offset); 70462306a36Sopenharmony_ci for (idx = args->offset >> PAGE_SHIFT; remain; idx++) { 70562306a36Sopenharmony_ci struct page *page = i915_gem_object_get_page(obj, idx); 70662306a36Sopenharmony_ci unsigned int length = min_t(u64, remain, PAGE_SIZE - offset); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci ret = shmem_pwrite(page, offset, length, user_data, 70962306a36Sopenharmony_ci (offset | length) & partial_cacheline_write, 71062306a36Sopenharmony_ci needs_clflush & CLFLUSH_AFTER); 71162306a36Sopenharmony_ci if (ret) 71262306a36Sopenharmony_ci break; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci remain -= length; 71562306a36Sopenharmony_ci user_data += length; 71662306a36Sopenharmony_ci offset = 0; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci i915_gem_object_unpin_pages(obj); 72262306a36Sopenharmony_ci return ret; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cierr_unpin: 72562306a36Sopenharmony_ci i915_gem_object_unpin_pages(obj); 72662306a36Sopenharmony_cierr_unlock: 72762306a36Sopenharmony_ci i915_gem_object_unlock(obj); 72862306a36Sopenharmony_ci return ret; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci/** 73262306a36Sopenharmony_ci * i915_gem_pwrite_ioctl - Writes data to the object referenced by handle. 73362306a36Sopenharmony_ci * @dev: drm device 73462306a36Sopenharmony_ci * @data: ioctl data blob 73562306a36Sopenharmony_ci * @file: drm file 73662306a36Sopenharmony_ci * 73762306a36Sopenharmony_ci * On error, the contents of the buffer that were to be modified are undefined. 73862306a36Sopenharmony_ci */ 73962306a36Sopenharmony_ciint 74062306a36Sopenharmony_cii915_gem_pwrite_ioctl(struct drm_device *dev, void *data, 74162306a36Sopenharmony_ci struct drm_file *file) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct drm_i915_private *i915 = to_i915(dev); 74462306a36Sopenharmony_ci struct drm_i915_gem_pwrite *args = data; 74562306a36Sopenharmony_ci struct drm_i915_gem_object *obj; 74662306a36Sopenharmony_ci int ret; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* PWRITE is disallowed for all platforms after TGL-LP. This also 74962306a36Sopenharmony_ci * covers all platforms with local memory. 75062306a36Sopenharmony_ci */ 75162306a36Sopenharmony_ci if (GRAPHICS_VER(i915) >= 12 && !IS_TIGERLAKE(i915)) 75262306a36Sopenharmony_ci return -EOPNOTSUPP; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (args->size == 0) 75562306a36Sopenharmony_ci return 0; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (!access_ok(u64_to_user_ptr(args->data_ptr), args->size)) 75862306a36Sopenharmony_ci return -EFAULT; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci obj = i915_gem_object_lookup(file, args->handle); 76162306a36Sopenharmony_ci if (!obj) 76262306a36Sopenharmony_ci return -ENOENT; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* Bounds check destination. */ 76562306a36Sopenharmony_ci if (range_overflows_t(u64, args->offset, args->size, obj->base.size)) { 76662306a36Sopenharmony_ci ret = -EINVAL; 76762306a36Sopenharmony_ci goto err; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* Writes not allowed into this read-only object */ 77162306a36Sopenharmony_ci if (i915_gem_object_is_readonly(obj)) { 77262306a36Sopenharmony_ci ret = -EINVAL; 77362306a36Sopenharmony_ci goto err; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci trace_i915_gem_object_pwrite(obj, args->offset, args->size); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci ret = -ENODEV; 77962306a36Sopenharmony_ci if (obj->ops->pwrite) 78062306a36Sopenharmony_ci ret = obj->ops->pwrite(obj, args); 78162306a36Sopenharmony_ci if (ret != -ENODEV) 78262306a36Sopenharmony_ci goto err; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci ret = i915_gem_object_wait(obj, 78562306a36Sopenharmony_ci I915_WAIT_INTERRUPTIBLE | 78662306a36Sopenharmony_ci I915_WAIT_ALL, 78762306a36Sopenharmony_ci MAX_SCHEDULE_TIMEOUT); 78862306a36Sopenharmony_ci if (ret) 78962306a36Sopenharmony_ci goto err; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci ret = -EFAULT; 79262306a36Sopenharmony_ci /* We can only do the GTT pwrite on untiled buffers, as otherwise 79362306a36Sopenharmony_ci * it would end up going through the fenced access, and we'll get 79462306a36Sopenharmony_ci * different detiling behavior between reading and writing. 79562306a36Sopenharmony_ci * pread/pwrite currently are reading and writing from the CPU 79662306a36Sopenharmony_ci * perspective, requiring manual detiling by the client. 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci if (!i915_gem_object_has_struct_page(obj) || 79962306a36Sopenharmony_ci i915_gem_cpu_write_needs_clflush(obj)) 80062306a36Sopenharmony_ci /* Note that the gtt paths might fail with non-page-backed user 80162306a36Sopenharmony_ci * pointers (e.g. gtt mappings when moving data between 80262306a36Sopenharmony_ci * textures). Fallback to the shmem path in that case. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci ret = i915_gem_gtt_pwrite_fast(obj, args); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (ret == -EFAULT || ret == -ENOSPC) { 80762306a36Sopenharmony_ci if (i915_gem_object_has_struct_page(obj)) 80862306a36Sopenharmony_ci ret = i915_gem_shmem_pwrite(obj, args); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cierr: 81262306a36Sopenharmony_ci i915_gem_object_put(obj); 81362306a36Sopenharmony_ci return ret; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci/** 81762306a36Sopenharmony_ci * i915_gem_sw_finish_ioctl - Called when user space has done writes to this buffer 81862306a36Sopenharmony_ci * @dev: drm device 81962306a36Sopenharmony_ci * @data: ioctl data blob 82062306a36Sopenharmony_ci * @file: drm file 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ciint 82362306a36Sopenharmony_cii915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, 82462306a36Sopenharmony_ci struct drm_file *file) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct drm_i915_gem_sw_finish *args = data; 82762306a36Sopenharmony_ci struct drm_i915_gem_object *obj; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci obj = i915_gem_object_lookup(file, args->handle); 83062306a36Sopenharmony_ci if (!obj) 83162306a36Sopenharmony_ci return -ENOENT; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* 83462306a36Sopenharmony_ci * Proxy objects are barred from CPU access, so there is no 83562306a36Sopenharmony_ci * need to ban sw_finish as it is a nop. 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Pinned buffers may be scanout, so flush the cache */ 83962306a36Sopenharmony_ci i915_gem_object_flush_if_display(obj); 84062306a36Sopenharmony_ci i915_gem_object_put(obj); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci return 0; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_civoid i915_gem_runtime_suspend(struct drm_i915_private *i915) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct drm_i915_gem_object *obj, *on; 84862306a36Sopenharmony_ci int i; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* 85162306a36Sopenharmony_ci * Only called during RPM suspend. All users of the userfault_list 85262306a36Sopenharmony_ci * must be holding an RPM wakeref to ensure that this can not 85362306a36Sopenharmony_ci * run concurrently with themselves (and use the struct_mutex for 85462306a36Sopenharmony_ci * protection between themselves). 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci list_for_each_entry_safe(obj, on, 85862306a36Sopenharmony_ci &to_gt(i915)->ggtt->userfault_list, userfault_link) 85962306a36Sopenharmony_ci __i915_gem_object_release_mmap_gtt(obj); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci list_for_each_entry_safe(obj, on, 86262306a36Sopenharmony_ci &i915->runtime_pm.lmem_userfault_list, userfault_link) 86362306a36Sopenharmony_ci i915_gem_object_runtime_pm_release_mmap_offset(obj); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* 86662306a36Sopenharmony_ci * The fence will be lost when the device powers down. If any were 86762306a36Sopenharmony_ci * in use by hardware (i.e. they are pinned), we should not be powering 86862306a36Sopenharmony_ci * down! All other fences will be reacquired by the user upon waking. 86962306a36Sopenharmony_ci */ 87062306a36Sopenharmony_ci for (i = 0; i < to_gt(i915)->ggtt->num_fences; i++) { 87162306a36Sopenharmony_ci struct i915_fence_reg *reg = &to_gt(i915)->ggtt->fence_regs[i]; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* 87462306a36Sopenharmony_ci * Ideally we want to assert that the fence register is not 87562306a36Sopenharmony_ci * live at this point (i.e. that no piece of code will be 87662306a36Sopenharmony_ci * trying to write through fence + GTT, as that both violates 87762306a36Sopenharmony_ci * our tracking of activity and associated locking/barriers, 87862306a36Sopenharmony_ci * but also is illegal given that the hw is powered down). 87962306a36Sopenharmony_ci * 88062306a36Sopenharmony_ci * Previously we used reg->pin_count as a "liveness" indicator. 88162306a36Sopenharmony_ci * That is not sufficient, and we need a more fine-grained 88262306a36Sopenharmony_ci * tool if we want to have a sanity check here. 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (!reg->vma) 88662306a36Sopenharmony_ci continue; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci GEM_BUG_ON(i915_vma_has_userfault(reg->vma)); 88962306a36Sopenharmony_ci reg->dirty = true; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic void discard_ggtt_vma(struct i915_vma *vma) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci struct drm_i915_gem_object *obj = vma->obj; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci spin_lock(&obj->vma.lock); 89862306a36Sopenharmony_ci if (!RB_EMPTY_NODE(&vma->obj_node)) { 89962306a36Sopenharmony_ci rb_erase(&vma->obj_node, &obj->vma.tree); 90062306a36Sopenharmony_ci RB_CLEAR_NODE(&vma->obj_node); 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci spin_unlock(&obj->vma.lock); 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistruct i915_vma * 90662306a36Sopenharmony_cii915_gem_object_ggtt_pin_ww(struct drm_i915_gem_object *obj, 90762306a36Sopenharmony_ci struct i915_gem_ww_ctx *ww, 90862306a36Sopenharmony_ci const struct i915_gtt_view *view, 90962306a36Sopenharmony_ci u64 size, u64 alignment, u64 flags) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct drm_i915_private *i915 = to_i915(obj->base.dev); 91262306a36Sopenharmony_ci struct i915_ggtt *ggtt = to_gt(i915)->ggtt; 91362306a36Sopenharmony_ci struct i915_vma *vma; 91462306a36Sopenharmony_ci int ret; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci GEM_WARN_ON(!ww); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (flags & PIN_MAPPABLE && 91962306a36Sopenharmony_ci (!view || view->type == I915_GTT_VIEW_NORMAL)) { 92062306a36Sopenharmony_ci /* 92162306a36Sopenharmony_ci * If the required space is larger than the available 92262306a36Sopenharmony_ci * aperture, we will not able to find a slot for the 92362306a36Sopenharmony_ci * object and unbinding the object now will be in 92462306a36Sopenharmony_ci * vain. Worse, doing so may cause us to ping-pong 92562306a36Sopenharmony_ci * the object in and out of the Global GTT and 92662306a36Sopenharmony_ci * waste a lot of cycles under the mutex. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_ci if (obj->base.size > ggtt->mappable_end) 92962306a36Sopenharmony_ci return ERR_PTR(-E2BIG); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* 93262306a36Sopenharmony_ci * If NONBLOCK is set the caller is optimistically 93362306a36Sopenharmony_ci * trying to cache the full object within the mappable 93462306a36Sopenharmony_ci * aperture, and *must* have a fallback in place for 93562306a36Sopenharmony_ci * situations where we cannot bind the object. We 93662306a36Sopenharmony_ci * can be a little more lax here and use the fallback 93762306a36Sopenharmony_ci * more often to avoid costly migrations of ourselves 93862306a36Sopenharmony_ci * and other objects within the aperture. 93962306a36Sopenharmony_ci * 94062306a36Sopenharmony_ci * Half-the-aperture is used as a simple heuristic. 94162306a36Sopenharmony_ci * More interesting would to do search for a free 94262306a36Sopenharmony_ci * block prior to making the commitment to unbind. 94362306a36Sopenharmony_ci * That caters for the self-harm case, and with a 94462306a36Sopenharmony_ci * little more heuristics (e.g. NOFAULT, NOEVICT) 94562306a36Sopenharmony_ci * we could try to minimise harm to others. 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_ci if (flags & PIN_NONBLOCK && 94862306a36Sopenharmony_ci obj->base.size > ggtt->mappable_end / 2) 94962306a36Sopenharmony_ci return ERR_PTR(-ENOSPC); 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cinew_vma: 95362306a36Sopenharmony_ci vma = i915_vma_instance(obj, &ggtt->vm, view); 95462306a36Sopenharmony_ci if (IS_ERR(vma)) 95562306a36Sopenharmony_ci return vma; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (i915_vma_misplaced(vma, size, alignment, flags)) { 95862306a36Sopenharmony_ci if (flags & PIN_NONBLOCK) { 95962306a36Sopenharmony_ci if (i915_vma_is_pinned(vma) || i915_vma_is_active(vma)) 96062306a36Sopenharmony_ci return ERR_PTR(-ENOSPC); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* 96362306a36Sopenharmony_ci * If this misplaced vma is too big (i.e, at-least 96462306a36Sopenharmony_ci * half the size of aperture) or hasn't been pinned 96562306a36Sopenharmony_ci * mappable before, we ignore the misplacement when 96662306a36Sopenharmony_ci * PIN_NONBLOCK is set in order to avoid the ping-pong 96762306a36Sopenharmony_ci * issue described above. In other words, we try to 96862306a36Sopenharmony_ci * avoid the costly operation of unbinding this vma 96962306a36Sopenharmony_ci * from the GGTT and rebinding it back because there 97062306a36Sopenharmony_ci * may not be enough space for this vma in the aperture. 97162306a36Sopenharmony_ci */ 97262306a36Sopenharmony_ci if (flags & PIN_MAPPABLE && 97362306a36Sopenharmony_ci (vma->fence_size > ggtt->mappable_end / 2 || 97462306a36Sopenharmony_ci !i915_vma_is_map_and_fenceable(vma))) 97562306a36Sopenharmony_ci return ERR_PTR(-ENOSPC); 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (i915_vma_is_pinned(vma) || i915_vma_is_active(vma)) { 97962306a36Sopenharmony_ci discard_ggtt_vma(vma); 98062306a36Sopenharmony_ci goto new_vma; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci ret = i915_vma_unbind(vma); 98462306a36Sopenharmony_ci if (ret) 98562306a36Sopenharmony_ci return ERR_PTR(ret); 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci ret = i915_vma_pin_ww(vma, ww, size, alignment, flags | PIN_GLOBAL); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (ret) 99162306a36Sopenharmony_ci return ERR_PTR(ret); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (vma->fence && !i915_gem_object_is_tiled(obj)) { 99462306a36Sopenharmony_ci mutex_lock(&ggtt->vm.mutex); 99562306a36Sopenharmony_ci i915_vma_revoke_fence(vma); 99662306a36Sopenharmony_ci mutex_unlock(&ggtt->vm.mutex); 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci ret = i915_vma_wait_for_bind(vma); 100062306a36Sopenharmony_ci if (ret) { 100162306a36Sopenharmony_ci i915_vma_unpin(vma); 100262306a36Sopenharmony_ci return ERR_PTR(ret); 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return vma; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistruct i915_vma * __must_check 100962306a36Sopenharmony_cii915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, 101062306a36Sopenharmony_ci const struct i915_gtt_view *view, 101162306a36Sopenharmony_ci u64 size, u64 alignment, u64 flags) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci struct i915_gem_ww_ctx ww; 101462306a36Sopenharmony_ci struct i915_vma *ret; 101562306a36Sopenharmony_ci int err; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci for_i915_gem_ww(&ww, err, true) { 101862306a36Sopenharmony_ci err = i915_gem_object_lock(obj, &ww); 101962306a36Sopenharmony_ci if (err) 102062306a36Sopenharmony_ci continue; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci ret = i915_gem_object_ggtt_pin_ww(obj, &ww, view, size, 102362306a36Sopenharmony_ci alignment, flags); 102462306a36Sopenharmony_ci if (IS_ERR(ret)) 102562306a36Sopenharmony_ci err = PTR_ERR(ret); 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci return err ? ERR_PTR(err) : ret; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ciint 103262306a36Sopenharmony_cii915_gem_madvise_ioctl(struct drm_device *dev, void *data, 103362306a36Sopenharmony_ci struct drm_file *file_priv) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct drm_i915_private *i915 = to_i915(dev); 103662306a36Sopenharmony_ci struct drm_i915_gem_madvise *args = data; 103762306a36Sopenharmony_ci struct drm_i915_gem_object *obj; 103862306a36Sopenharmony_ci int err; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci switch (args->madv) { 104162306a36Sopenharmony_ci case I915_MADV_DONTNEED: 104262306a36Sopenharmony_ci case I915_MADV_WILLNEED: 104362306a36Sopenharmony_ci break; 104462306a36Sopenharmony_ci default: 104562306a36Sopenharmony_ci return -EINVAL; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci obj = i915_gem_object_lookup(file_priv, args->handle); 104962306a36Sopenharmony_ci if (!obj) 105062306a36Sopenharmony_ci return -ENOENT; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci err = i915_gem_object_lock_interruptible(obj, NULL); 105362306a36Sopenharmony_ci if (err) 105462306a36Sopenharmony_ci goto out; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (i915_gem_object_has_pages(obj) && 105762306a36Sopenharmony_ci i915_gem_object_is_tiled(obj) && 105862306a36Sopenharmony_ci i915->gem_quirks & GEM_QUIRK_PIN_SWIZZLED_PAGES) { 105962306a36Sopenharmony_ci if (obj->mm.madv == I915_MADV_WILLNEED) { 106062306a36Sopenharmony_ci GEM_BUG_ON(!i915_gem_object_has_tiling_quirk(obj)); 106162306a36Sopenharmony_ci i915_gem_object_clear_tiling_quirk(obj); 106262306a36Sopenharmony_ci i915_gem_object_make_shrinkable(obj); 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci if (args->madv == I915_MADV_WILLNEED) { 106562306a36Sopenharmony_ci GEM_BUG_ON(i915_gem_object_has_tiling_quirk(obj)); 106662306a36Sopenharmony_ci i915_gem_object_make_unshrinkable(obj); 106762306a36Sopenharmony_ci i915_gem_object_set_tiling_quirk(obj); 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (obj->mm.madv != __I915_MADV_PURGED) { 107262306a36Sopenharmony_ci obj->mm.madv = args->madv; 107362306a36Sopenharmony_ci if (obj->ops->adjust_lru) 107462306a36Sopenharmony_ci obj->ops->adjust_lru(obj); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (i915_gem_object_has_pages(obj) || 107862306a36Sopenharmony_ci i915_gem_object_has_self_managed_shrink_list(obj)) { 107962306a36Sopenharmony_ci unsigned long flags; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci spin_lock_irqsave(&i915->mm.obj_lock, flags); 108262306a36Sopenharmony_ci if (!list_empty(&obj->mm.link)) { 108362306a36Sopenharmony_ci struct list_head *list; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (obj->mm.madv != I915_MADV_WILLNEED) 108662306a36Sopenharmony_ci list = &i915->mm.purge_list; 108762306a36Sopenharmony_ci else 108862306a36Sopenharmony_ci list = &i915->mm.shrink_list; 108962306a36Sopenharmony_ci list_move_tail(&obj->mm.link, list); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci spin_unlock_irqrestore(&i915->mm.obj_lock, flags); 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci /* if the object is no longer attached, discard its backing storage */ 109662306a36Sopenharmony_ci if (obj->mm.madv == I915_MADV_DONTNEED && 109762306a36Sopenharmony_ci !i915_gem_object_has_pages(obj)) 109862306a36Sopenharmony_ci i915_gem_object_truncate(obj); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci args->retained = obj->mm.madv != __I915_MADV_PURGED; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci i915_gem_object_unlock(obj); 110362306a36Sopenharmony_ciout: 110462306a36Sopenharmony_ci i915_gem_object_put(obj); 110562306a36Sopenharmony_ci return err; 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci/* 110962306a36Sopenharmony_ci * A single pass should suffice to release all the freed objects (along most 111062306a36Sopenharmony_ci * call paths), but be a little more paranoid in that freeing the objects does 111162306a36Sopenharmony_ci * take a little amount of time, during which the rcu callbacks could have added 111262306a36Sopenharmony_ci * new objects into the freed list, and armed the work again. 111362306a36Sopenharmony_ci */ 111462306a36Sopenharmony_civoid i915_gem_drain_freed_objects(struct drm_i915_private *i915) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci while (atomic_read(&i915->mm.free_count)) { 111762306a36Sopenharmony_ci flush_work(&i915->mm.free_work); 111862306a36Sopenharmony_ci drain_workqueue(i915->bdev.wq); 111962306a36Sopenharmony_ci rcu_barrier(); 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci/* 112462306a36Sopenharmony_ci * Similar to objects above (see i915_gem_drain_freed-objects), in general we 112562306a36Sopenharmony_ci * have workers that are armed by RCU and then rearm themselves in their 112662306a36Sopenharmony_ci * callbacks. To be paranoid, we need to drain the workqueue a second time after 112762306a36Sopenharmony_ci * waiting for the RCU grace period so that we catch work queued via RCU from 112862306a36Sopenharmony_ci * the first pass. As neither drain_workqueue() nor flush_workqueue() report a 112962306a36Sopenharmony_ci * result, we make an assumption that we only don't require more than 3 passes 113062306a36Sopenharmony_ci * to catch all _recursive_ RCU delayed work. 113162306a36Sopenharmony_ci */ 113262306a36Sopenharmony_civoid i915_gem_drain_workqueue(struct drm_i915_private *i915) 113362306a36Sopenharmony_ci{ 113462306a36Sopenharmony_ci int i; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 113762306a36Sopenharmony_ci flush_workqueue(i915->wq); 113862306a36Sopenharmony_ci rcu_barrier(); 113962306a36Sopenharmony_ci i915_gem_drain_freed_objects(i915); 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci drain_workqueue(i915->wq); 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ciint i915_gem_init(struct drm_i915_private *dev_priv) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci struct intel_gt *gt; 114862306a36Sopenharmony_ci unsigned int i; 114962306a36Sopenharmony_ci int ret; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci /* 115262306a36Sopenharmony_ci * In the proccess of replacing cache_level with pat_index a tricky 115362306a36Sopenharmony_ci * dependency is created on the definition of the enum i915_cache_level. 115462306a36Sopenharmony_ci * in case this enum is changed, PTE encode would be broken. 115562306a36Sopenharmony_ci * Add a WARNING here. And remove when we completely quit using this 115662306a36Sopenharmony_ci * enum 115762306a36Sopenharmony_ci */ 115862306a36Sopenharmony_ci BUILD_BUG_ON(I915_CACHE_NONE != 0 || 115962306a36Sopenharmony_ci I915_CACHE_LLC != 1 || 116062306a36Sopenharmony_ci I915_CACHE_L3_LLC != 2 || 116162306a36Sopenharmony_ci I915_CACHE_WT != 3 || 116262306a36Sopenharmony_ci I915_MAX_CACHE_LEVEL != 4); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci /* We need to fallback to 4K pages if host doesn't support huge gtt. */ 116562306a36Sopenharmony_ci if (intel_vgpu_active(dev_priv) && !intel_vgpu_has_huge_gtt(dev_priv)) 116662306a36Sopenharmony_ci RUNTIME_INFO(dev_priv)->page_sizes = I915_GTT_PAGE_SIZE_4K; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci ret = i915_gem_init_userptr(dev_priv); 116962306a36Sopenharmony_ci if (ret) 117062306a36Sopenharmony_ci return ret; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci for_each_gt(gt, dev_priv, i) { 117362306a36Sopenharmony_ci intel_uc_fetch_firmwares(>->uc); 117462306a36Sopenharmony_ci intel_wopcm_init(>->wopcm); 117562306a36Sopenharmony_ci if (GRAPHICS_VER(dev_priv) >= 8) 117662306a36Sopenharmony_ci setup_private_pat(gt); 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci ret = i915_init_ggtt(dev_priv); 118062306a36Sopenharmony_ci if (ret) { 118162306a36Sopenharmony_ci GEM_BUG_ON(ret == -EIO); 118262306a36Sopenharmony_ci goto err_unlock; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* 118662306a36Sopenharmony_ci * Despite its name intel_clock_gating_init applies both display 118762306a36Sopenharmony_ci * clock gating workarounds; GT mmio workarounds and the occasional 118862306a36Sopenharmony_ci * GT power context workaround. Worse, sometimes it includes a context 118962306a36Sopenharmony_ci * register workaround which we need to apply before we record the 119062306a36Sopenharmony_ci * default HW state for all contexts. 119162306a36Sopenharmony_ci * 119262306a36Sopenharmony_ci * FIXME: break up the workarounds and apply them at the right time! 119362306a36Sopenharmony_ci */ 119462306a36Sopenharmony_ci intel_clock_gating_init(dev_priv); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci for_each_gt(gt, dev_priv, i) { 119762306a36Sopenharmony_ci ret = intel_gt_init(gt); 119862306a36Sopenharmony_ci if (ret) 119962306a36Sopenharmony_ci goto err_unlock; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* 120362306a36Sopenharmony_ci * Register engines early to ensure the engine list is in its final 120462306a36Sopenharmony_ci * rb-tree form, lowering the amount of code that has to deal with 120562306a36Sopenharmony_ci * the intermediate llist state. 120662306a36Sopenharmony_ci */ 120762306a36Sopenharmony_ci intel_engines_driver_register(dev_priv); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci return 0; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* 121262306a36Sopenharmony_ci * Unwinding is complicated by that we want to handle -EIO to mean 121362306a36Sopenharmony_ci * disable GPU submission but keep KMS alive. We want to mark the 121462306a36Sopenharmony_ci * HW as irrevisibly wedged, but keep enough state around that the 121562306a36Sopenharmony_ci * driver doesn't explode during runtime. 121662306a36Sopenharmony_ci */ 121762306a36Sopenharmony_cierr_unlock: 121862306a36Sopenharmony_ci i915_gem_drain_workqueue(dev_priv); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (ret != -EIO) { 122162306a36Sopenharmony_ci for_each_gt(gt, dev_priv, i) { 122262306a36Sopenharmony_ci intel_gt_driver_remove(gt); 122362306a36Sopenharmony_ci intel_gt_driver_release(gt); 122462306a36Sopenharmony_ci intel_uc_cleanup_firmwares(>->uc); 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (ret == -EIO) { 122962306a36Sopenharmony_ci /* 123062306a36Sopenharmony_ci * Allow engines or uC initialisation to fail by marking the GPU 123162306a36Sopenharmony_ci * as wedged. But we only want to do this when the GPU is angry, 123262306a36Sopenharmony_ci * for all other failure, such as an allocation failure, bail. 123362306a36Sopenharmony_ci */ 123462306a36Sopenharmony_ci for_each_gt(gt, dev_priv, i) { 123562306a36Sopenharmony_ci if (!intel_gt_is_wedged(gt)) { 123662306a36Sopenharmony_ci i915_probe_error(dev_priv, 123762306a36Sopenharmony_ci "Failed to initialize GPU, declaring it wedged!\n"); 123862306a36Sopenharmony_ci intel_gt_set_wedged(gt); 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci /* Minimal basic recovery for KMS */ 124362306a36Sopenharmony_ci ret = i915_ggtt_enable_hw(dev_priv); 124462306a36Sopenharmony_ci i915_ggtt_resume(to_gt(dev_priv)->ggtt); 124562306a36Sopenharmony_ci intel_clock_gating_init(dev_priv); 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci i915_gem_drain_freed_objects(dev_priv); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci return ret; 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_civoid i915_gem_driver_register(struct drm_i915_private *i915) 125462306a36Sopenharmony_ci{ 125562306a36Sopenharmony_ci i915_gem_driver_register__shrinker(i915); 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_civoid i915_gem_driver_unregister(struct drm_i915_private *i915) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci i915_gem_driver_unregister__shrinker(i915); 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_civoid i915_gem_driver_remove(struct drm_i915_private *dev_priv) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci struct intel_gt *gt; 126662306a36Sopenharmony_ci unsigned int i; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci i915_gem_suspend_late(dev_priv); 126962306a36Sopenharmony_ci for_each_gt(gt, dev_priv, i) 127062306a36Sopenharmony_ci intel_gt_driver_remove(gt); 127162306a36Sopenharmony_ci dev_priv->uabi_engines = RB_ROOT; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci /* Flush any outstanding unpin_work. */ 127462306a36Sopenharmony_ci i915_gem_drain_workqueue(dev_priv); 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_civoid i915_gem_driver_release(struct drm_i915_private *dev_priv) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci struct intel_gt *gt; 128062306a36Sopenharmony_ci unsigned int i; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci for_each_gt(gt, dev_priv, i) { 128362306a36Sopenharmony_ci intel_gt_driver_release(gt); 128462306a36Sopenharmony_ci intel_uc_cleanup_firmwares(>->uc); 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci /* Flush any outstanding work, including i915_gem_context.release_work. */ 128862306a36Sopenharmony_ci i915_gem_drain_workqueue(dev_priv); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, !list_empty(&dev_priv->gem.contexts.list)); 129162306a36Sopenharmony_ci} 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_cistatic void i915_gem_init__mm(struct drm_i915_private *i915) 129462306a36Sopenharmony_ci{ 129562306a36Sopenharmony_ci spin_lock_init(&i915->mm.obj_lock); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci init_llist_head(&i915->mm.free_list); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci INIT_LIST_HEAD(&i915->mm.purge_list); 130062306a36Sopenharmony_ci INIT_LIST_HEAD(&i915->mm.shrink_list); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci i915_gem_init__objects(i915); 130362306a36Sopenharmony_ci} 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_civoid i915_gem_init_early(struct drm_i915_private *dev_priv) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci i915_gem_init__mm(dev_priv); 130862306a36Sopenharmony_ci i915_gem_init__contexts(dev_priv); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci spin_lock_init(&dev_priv->display.fb_tracking.lock); 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_civoid i915_gem_cleanup_early(struct drm_i915_private *dev_priv) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci i915_gem_drain_workqueue(dev_priv); 131662306a36Sopenharmony_ci GEM_BUG_ON(!llist_empty(&dev_priv->mm.free_list)); 131762306a36Sopenharmony_ci GEM_BUG_ON(atomic_read(&dev_priv->mm.free_count)); 131862306a36Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, dev_priv->mm.shrink_count); 131962306a36Sopenharmony_ci} 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ciint i915_gem_open(struct drm_i915_private *i915, struct drm_file *file) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci struct drm_i915_file_private *file_priv; 132462306a36Sopenharmony_ci struct i915_drm_client *client; 132562306a36Sopenharmony_ci int ret = -ENOMEM; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci drm_dbg(&i915->drm, "\n"); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); 133062306a36Sopenharmony_ci if (!file_priv) 133162306a36Sopenharmony_ci goto err_alloc; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci client = i915_drm_client_alloc(); 133462306a36Sopenharmony_ci if (!client) 133562306a36Sopenharmony_ci goto err_client; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci file->driver_priv = file_priv; 133862306a36Sopenharmony_ci file_priv->i915 = i915; 133962306a36Sopenharmony_ci file_priv->file = file; 134062306a36Sopenharmony_ci file_priv->client = client; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci file_priv->bsd_engine = -1; 134362306a36Sopenharmony_ci file_priv->hang_timestamp = jiffies; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci ret = i915_gem_context_open(i915, file); 134662306a36Sopenharmony_ci if (ret) 134762306a36Sopenharmony_ci goto err_context; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci return 0; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cierr_context: 135262306a36Sopenharmony_ci i915_drm_client_put(client); 135362306a36Sopenharmony_cierr_client: 135462306a36Sopenharmony_ci kfree(file_priv); 135562306a36Sopenharmony_cierr_alloc: 135662306a36Sopenharmony_ci return ret; 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) 136062306a36Sopenharmony_ci#include "selftests/mock_gem_device.c" 136162306a36Sopenharmony_ci#include "selftests/i915_gem.c" 136262306a36Sopenharmony_ci#endif 1363