Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
29d8808
Documentation
LICENSES
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
connector
counter
cpufreq
cpuidle
crypto
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fpga
fsi
gnss
gpio
gpu
drm
amd
arc
arm
armada
aspeed
ast
atmel-hlcdc
bochs
bridge
etnaviv
exynos
fsl-dcu
gma500
hisilicon
i2c
i810
i915
display
gem
gt
gvt
selftests
.gitignore
Kconfig
Kconfig.debug
Kconfig.profile
Kconfig.unstable
Makefile
dma_resv_utils.c
dma_resv_utils.h
i915_active.c
i915_active.h
i915_active_types.h
i915_buddy.c
i915_buddy.h
i915_cmd_parser.c
i915_config.c
i915_debugfs.c
i915_debugfs.h
i915_debugfs_params.c
i915_debugfs_params.h
i915_drv.c
i915_drv.h
i915_fixed.h
i915_gem.c
i915_gem.h
i915_gem_evict.c
i915_gem_gtt.c
i915_gem_gtt.h
i915_getparam.c
i915_globals.c
i915_globals.h
i915_gpu_error.c
i915_gpu_error.h
i915_ioc32.c
i915_ioc32.h
i915_irq.c
i915_irq.h
i915_memcpy.c
i915_memcpy.h
i915_mitigations.c
i915_mitigations.h
i915_mm.c
i915_params.c
i915_params.h
i915_pci.c
i915_perf.c
i915_perf.h
i915_perf_types.h
i915_pmu.c
i915_pmu.h
i915_priolist_types.h
i915_pvinfo.h
i915_query.c
i915_query.h
i915_reg.h
i915_request.c
i915_request.h
i915_scatterlist.c
i915_scatterlist.h
i915_scheduler.c
i915_scheduler.h
i915_scheduler_types.h
i915_selftest.h
i915_suspend.c
i915_suspend.h
i915_sw_fence.c
i915_sw_fence.h
i915_sw_fence_work.c
i915_sw_fence_work.h
i915_switcheroo.c
i915_switcheroo.h
i915_syncmap.c
i915_syncmap.h
i915_sysfs.c
i915_sysfs.h
i915_trace.h
i915_trace_points.c
i915_user_extensions.c
i915_user_extensions.h
i915_utils.c
i915_utils.h
i915_vgpu.c
i915_vgpu.h
i915_vma.c
i915_vma.h
i915_vma_types.h
intel_device_info.c
intel_device_info.h
intel_dram.c
intel_dram.h
intel_gvt.c
intel_gvt.h
intel_memory_region.c
intel_memory_region.h
intel_pch.c
intel_pch.h
intel_pm.c
intel_pm.h
intel_runtime_pm.c
intel_runtime_pm.h
intel_sideband.c
intel_sideband.h
intel_uncore.c
intel_uncore.h
intel_wakeref.c
intel_wakeref.h
intel_wopcm.c
intel_wopcm.h
vlv_suspend.c
vlv_suspend.h
imx
ingenic
kmb
lib
lima
mcde
mediatek
meson
mga
mgag200
msm
mxsfb
nouveau
omapdrm
panel
panfrost
pl111
qxl
r128
radeon
rcar-du
rockchip
savage
scheduler
selftests
shmobile
sis
sti
stm
sun4i
tdfx
tegra
tidss
tilcdc
tiny
ttm
tve200
udl
v3d
vboxvideo
vc4
vgem
via
virtio
vkms
vmwgfx
xen
xlnx
zte
Kconfig
Makefile
drm_agpsupport.c
drm_atomic.c
drm_atomic_helper.c
drm_atomic_state_helper.c
drm_atomic_uapi.c
drm_auth.c
drm_blend.c
drm_bridge.c
drm_bridge_connector.c
drm_bufs.c
drm_cache.c
drm_client.c
drm_client_modeset.c
drm_color_mgmt.c
drm_connector.c
drm_context.c
drm_crtc.c
drm_crtc_helper.c
drm_crtc_helper_internal.h
drm_crtc_internal.h
drm_damage_helper.c
drm_debugfs.c
drm_debugfs_crc.c
drm_dma.c
drm_dp_aux_dev.c
drm_dp_cec.c
drm_dp_dual_mode_helper.c
drm_dp_helper.c
drm_dp_mst_topology.c
drm_dp_mst_topology_internal.h
drm_drv.c
drm_dsc.c
drm_dumb_buffers.c
drm_edid.c
drm_edid_load.c
drm_encoder.c
drm_encoder_slave.c
drm_fb_cma_helper.c
drm_fb_helper.c
drm_file.c
drm_flip_work.c
drm_format_helper.c
drm_fourcc.c
drm_framebuffer.c
drm_gem.c
drm_gem_cma_helper.c
drm_gem_framebuffer_helper.c
drm_gem_shmem_helper.c
drm_gem_ttm_helper.c
drm_gem_vram_helper.c
drm_hashtab.c
drm_hdcp.c
drm_internal.h
drm_ioc32.c
drm_ioctl.c
drm_irq.c
drm_kms_helper_common.c
drm_lease.c
drm_legacy.h
drm_legacy_misc.c
drm_lock.c
drm_managed.c
drm_memory.c
drm_mipi_dbi.c
drm_mipi_dsi.c
drm_mm.c
drm_mode_config.c
drm_mode_object.c
drm_modes.c
drm_modeset_helper.c
drm_modeset_lock.c
drm_of.c
drm_panel.c
drm_panel_orientation_quirks.c
drm_pci.c
drm_plane.c
drm_plane_helper.c
drm_prime.c
drm_print.c
drm_probe_helper.c
drm_property.c
drm_rect.c
drm_scatter.c
drm_scdc_helper.c
drm_self_refresh_helper.c
drm_simple_kms_helper.c
drm_syncobj.c
drm_sysfs.c
drm_trace.h
drm_trace_points.c
drm_vblank.c
drm_vblank_work.c
drm_vm.c
drm_vma_manager.c
drm_writeback.c
host1x
ipu-v3
trace
vga
Makefile
greybus
hid
hsi
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
ide
idle
iio
infiniband
input
interconnect
iommu
ipack
irqchip
isdn
leds
lightnvm
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
most
mtd
mux
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
oprofile
parisc
parport
pci
pcmcia
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sfi
sh
siox
slimbus
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
uio
usb
vdpa
vfio
vhost
video
virt
virtio
visorbus
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
gpu
/
drm
/
i915
/
i915_gem.c
Copy path
Blame
Blame
Latest commit
Chris Wilson
and
Rodrigo Vivi
drm/i915/gem: Move freeze/freeze_late next to suspend/suspend_late
Feb 8, 2021
29d8808
·
Feb 8, 2021
History
History
1228 lines (1023 loc) · 30.8 KB
Breadcrumbs
linux
/
drivers
/
gpu
/
drm
/
i915
/
i915_gem.c
Top
File metadata and controls
Code
Blame
1228 lines (1023 loc) · 30.8 KB
Raw
/* * Copyright © 2008-2015 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authors: * Eric Anholt <eric@anholt.net> * */ #include <drm/drm_vma_manager.h> #include <linux/dma-fence-array.h> #include <linux/kthread.h> #include <linux/dma-resv.h> #include <linux/shmem_fs.h> #include <linux/slab.h> #include <linux/stop_machine.h> #include <linux/swap.h> #include <linux/pci.h> #include <linux/dma-buf.h> #include <linux/mman.h> #include "display/intel_display.h" #include "display/intel_frontbuffer.h" #include "gem/i915_gem_clflush.h" #include "gem/i915_gem_context.h" #include "gem/i915_gem_ioctls.h" #include "gem/i915_gem_mman.h" #include "gem/i915_gem_region.h" #include "gt/intel_engine_user.h" #include "gt/intel_gt.h" #include "gt/intel_gt_pm.h" #include "gt/intel_workarounds.h" #include "i915_drv.h" #include "i915_trace.h" #include "i915_vgpu.h" #include "intel_pm.h" static int insert_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node, u32 size) { int err; err = mutex_lock_interruptible(&ggtt->vm.mutex); if (err) return err; memset(node, 0, sizeof(*node)); err = drm_mm_insert_node_in_range(&ggtt->vm.mm, node, size, 0, I915_COLOR_UNEVICTABLE, 0, ggtt->mappable_end, DRM_MM_INSERT_LOW); mutex_unlock(&ggtt->vm.mutex); return err; } static void remove_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node) { mutex_lock(&ggtt->vm.mutex); drm_mm_remove_node(node); mutex_unlock(&ggtt->vm.mutex); } int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { struct i915_ggtt *ggtt = &to_i915(dev)->ggtt; struct drm_i915_gem_get_aperture *args = data; struct i915_vma *vma; u64 pinned; if (mutex_lock_interruptible(&ggtt->vm.mutex)) return -EINTR; pinned = ggtt->vm.reserved; list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link) if (i915_vma_is_pinned(vma)) pinned += vma->node.size; mutex_unlock(&ggtt->vm.mutex); args->aper_size = ggtt->vm.total; args->aper_available_size = args->aper_size - pinned; return 0; } int i915_gem_object_unbind(struct drm_i915_gem_object *obj, unsigned long flags) { struct intel_runtime_pm *rpm = &to_i915(obj->base.dev)->runtime_pm; LIST_HEAD(still_in_list); intel_wakeref_t wakeref; struct i915_vma *vma; int ret; if (list_empty(&obj->vma.list)) return 0; /* * As some machines use ACPI to handle runtime-resume callbacks, and * ACPI is quite kmalloc happy, we cannot resume beneath the vm->mutex * as they are required by the shrinker. Ergo, we wake the device up * first just in case. */ wakeref = intel_runtime_pm_get(rpm); try_again: ret = 0; spin_lock(&obj->vma.lock); while (!ret && (vma = list_first_entry_or_null(&obj->vma.list, struct i915_vma, obj_link))) { struct i915_address_space *vm = vma->vm; list_move_tail(&vma->obj_link, &still_in_list); if (!i915_vma_is_bound(vma, I915_VMA_BIND_MASK)) continue; if (flags & I915_GEM_OBJECT_UNBIND_TEST) { ret = -EBUSY; break; } ret = -EAGAIN; if (!i915_vm_tryopen(vm)) break; /* Prevent vma being freed by i915_vma_parked as we unbind */ vma = __i915_vma_get(vma); spin_unlock(&obj->vma.lock); if (vma) { ret = -EBUSY; if (flags & I915_GEM_OBJECT_UNBIND_ACTIVE || !i915_vma_is_active(vma)) ret = i915_vma_unbind(vma); __i915_vma_put(vma); } i915_vm_close(vm); spin_lock(&obj->vma.lock); } list_splice_init(&still_in_list, &obj->vma.list); spin_unlock(&obj->vma.lock); if (ret == -EAGAIN && flags & I915_GEM_OBJECT_UNBIND_BARRIER) { rcu_barrier(); /* flush the i915_vm_release() */ goto try_again; } intel_runtime_pm_put(rpm, wakeref); return ret; } static int shmem_pread(struct page *page, int offset, int len, char __user *user_data, bool needs_clflush) { char *vaddr; int ret; vaddr = kmap(page); if (needs_clflush) drm_clflush_virt_range(vaddr + offset, len); ret = __copy_to_user(user_data, vaddr + offset, len); kunmap(page); return ret ? -EFAULT : 0; } static int i915_gem_shmem_pread(struct drm_i915_gem_object *obj, struct drm_i915_gem_pread *args) { unsigned int needs_clflush; unsigned int idx, offset; struct dma_fence *fence; char __user *user_data; u64 remain; int ret; ret = i915_gem_object_lock_interruptible(obj, NULL); if (ret) return ret; ret = i915_gem_object_prepare_read(obj, &needs_clflush); if (ret) { i915_gem_object_unlock(obj); return ret; } fence = i915_gem_object_lock_fence(obj); i915_gem_object_finish_access(obj); i915_gem_object_unlock(obj); if (!fence) return -ENOMEM; remain = args->size; user_data = u64_to_user_ptr(args->data_ptr); offset = offset_in_page(args->offset); for (idx = args->offset >> PAGE_SHIFT; remain; idx++) { struct page *page = i915_gem_object_get_page(obj, idx); unsigned int length = min_t(u64, remain, PAGE_SIZE - offset); ret = shmem_pread(page, offset, length, user_data, needs_clflush); if (ret) break; remain -= length; user_data += length; offset = 0; } i915_gem_object_unlock_fence(obj, fence); return ret; } static inline bool gtt_user_read(struct io_mapping *mapping, loff_t base, int offset, char __user *user_data, int length) { void __iomem *vaddr; unsigned long unwritten; /* We can use the cpu mem copy function because this is X86. */ vaddr = io_mapping_map_atomic_wc(mapping, base); unwritten = __copy_to_user_inatomic(user_data, (void __force *)vaddr + offset, length); io_mapping_unmap_atomic(vaddr); if (unwritten) { vaddr = io_mapping_map_wc(mapping, base, PAGE_SIZE); unwritten = copy_to_user(user_data, (void __force *)vaddr + offset, length); io_mapping_unmap(vaddr); } return unwritten; } static int i915_gem_gtt_pread(struct drm_i915_gem_object *obj, const struct drm_i915_gem_pread *args) { struct drm_i915_private *i915 = to_i915(obj->base.dev); struct i915_ggtt *ggtt = &i915->ggtt; intel_wakeref_t wakeref; struct drm_mm_node node; struct dma_fence *fence; void __user *user_data; struct i915_vma *vma; u64 remain, offset; int ret; wakeref = intel_runtime_pm_get(&i915->runtime_pm); vma = ERR_PTR(-ENODEV); if (!i915_gem_object_is_tiled(obj)) vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE | PIN_NONBLOCK /* NOWARN */ | PIN_NOEVICT); if (!IS_ERR(vma)) { node.start = i915_ggtt_offset(vma); node.flags = 0; } else { ret = insert_mappable_node(ggtt, &node, PAGE_SIZE); if (ret) goto out_rpm; GEM_BUG_ON(!drm_mm_node_allocated(&node)); } ret = i915_gem_object_lock_interruptible(obj, NULL); if (ret) goto out_unpin; ret = i915_gem_object_set_to_gtt_domain(obj, false); if (ret) { i915_gem_object_unlock(obj); goto out_unpin; } fence = i915_gem_object_lock_fence(obj); i915_gem_object_unlock(obj); if (!fence) { ret = -ENOMEM; goto out_unpin; } user_data = u64_to_user_ptr(args->data_ptr); remain = args->size; offset = args->offset; while (remain > 0) { /* Operation in this page * * page_base = page offset within aperture * page_offset = offset within page * page_length = bytes to copy for this page */ u32 page_base = node.start; unsigned page_offset = offset_in_page(offset); unsigned page_length = PAGE_SIZE - page_offset; page_length = remain < page_length ? remain : page_length; if (drm_mm_node_allocated(&node)) { ggtt->vm.insert_page(&ggtt->vm, i915_gem_object_get_dma_address(obj, offset >> PAGE_SHIFT), node.start, I915_CACHE_NONE, 0); } else { page_base += offset & PAGE_MASK; } if (gtt_user_read(&ggtt->iomap, page_base, page_offset, user_data, page_length)) { ret = -EFAULT; break; } remain -= page_length; user_data += page_length; offset += page_length; } i915_gem_object_unlock_fence(obj, fence); out_unpin: if (drm_mm_node_allocated(&node)) { ggtt->vm.clear_range(&ggtt->vm, node.start, node.size); remove_mappable_node(ggtt, &node); } else { i915_vma_unpin(vma); } out_rpm: intel_runtime_pm_put(&i915->runtime_pm, wakeref); return ret; } /** * Reads data from the object referenced by handle. * @dev: drm device pointer * @data: ioctl data blob * @file: drm file pointer * * On error, the contents of *data are undefined. */ int i915_gem_pread_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_i915_gem_pread *args = data; struct drm_i915_gem_object *obj; int ret; if (args->size == 0) return 0; if (!access_ok(u64_to_user_ptr(args->data_ptr), args->size)) return -EFAULT; obj = i915_gem_object_lookup(file, args->handle); if (!obj) return -ENOENT; /* Bounds check source. */ if (range_overflows_t(u64, args->offset, args->size, obj->base.size)) { ret = -EINVAL; goto out; } trace_i915_gem_object_pread(obj, args->offset, args->size); ret = -ENODEV; if (obj->ops->pread) ret = obj->ops->pread(obj, args); if (ret != -ENODEV) goto out; ret = i915_gem_object_wait(obj, I915_WAIT_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); if (ret) goto out; ret = i915_gem_object_pin_pages(obj); if (ret) goto out; ret = i915_gem_shmem_pread(obj, args); if (ret == -EFAULT || ret == -ENODEV) ret = i915_gem_gtt_pread(obj, args); i915_gem_object_unpin_pages(obj); out: i915_gem_object_put(obj); return ret; } /* This is the fast write path which cannot handle * page faults in the source data */ static inline bool ggtt_write(struct io_mapping *mapping, loff_t base, int offset, char __user *user_data, int length) { void __iomem *vaddr; unsigned long unwritten; /* We can use the cpu mem copy function because this is X86. */ vaddr = io_mapping_map_atomic_wc(mapping, base); unwritten = __copy_from_user_inatomic_nocache((void __force *)vaddr + offset, user_data, length); io_mapping_unmap_atomic(vaddr); if (unwritten) { vaddr = io_mapping_map_wc(mapping, base, PAGE_SIZE); unwritten = copy_from_user((void __force *)vaddr + offset, user_data, length); io_mapping_unmap(vaddr); } return unwritten; } /** * This is the fast pwrite path, where we copy the data directly from the * user into the GTT, uncached. * @obj: i915 GEM object * @args: pwrite arguments structure */ static int i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj, const struct drm_i915_gem_pwrite *args) { struct drm_i915_private *i915 = to_i915(obj->base.dev); struct i915_ggtt *ggtt = &i915->ggtt; struct intel_runtime_pm *rpm = &i915->runtime_pm; intel_wakeref_t wakeref; struct drm_mm_node node; struct dma_fence *fence; struct i915_vma *vma; u64 remain, offset; void __user *user_data; int ret; if (i915_gem_object_has_struct_page(obj)) { /* * Avoid waking the device up if we can fallback, as * waking/resuming is very slow (worst-case 10-100 ms * depending on PCI sleeps and our own resume time). * This easily dwarfs any performance advantage from * using the cache bypass of indirect GGTT access. */ wakeref = intel_runtime_pm_get_if_in_use(rpm); if (!wakeref) return -EFAULT; } else { /* No backing pages, no fallback, we must force GGTT access */ wakeref = intel_runtime_pm_get(rpm); } vma = ERR_PTR(-ENODEV); if (!i915_gem_object_is_tiled(obj)) vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE | PIN_NONBLOCK /* NOWARN */ | PIN_NOEVICT); if (!IS_ERR(vma)) { node.start = i915_ggtt_offset(vma); node.flags = 0; } else { ret = insert_mappable_node(ggtt, &node, PAGE_SIZE); if (ret) goto out_rpm; GEM_BUG_ON(!drm_mm_node_allocated(&node)); } ret = i915_gem_object_lock_interruptible(obj, NULL); if (ret) goto out_unpin; ret = i915_gem_object_set_to_gtt_domain(obj, true); if (ret) { i915_gem_object_unlock(obj); goto out_unpin; } fence = i915_gem_object_lock_fence(obj); i915_gem_object_unlock(obj); if (!fence) { ret = -ENOMEM; goto out_unpin; } i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU); user_data = u64_to_user_ptr(args->data_ptr); offset = args->offset; remain = args->size; while (remain) { /* Operation in this page * * page_base = page offset within aperture * page_offset = offset within page * page_length = bytes to copy for this page */ u32 page_base = node.start; unsigned int page_offset = offset_in_page(offset); unsigned int page_length = PAGE_SIZE - page_offset; page_length = remain < page_length ? remain : page_length; if (drm_mm_node_allocated(&node)) { /* flush the write before we modify the GGTT */ intel_gt_flush_ggtt_writes(ggtt->vm.gt); ggtt->vm.insert_page(&ggtt->vm, i915_gem_object_get_dma_address(obj, offset >> PAGE_SHIFT), node.start, I915_CACHE_NONE, 0); wmb(); /* flush modifications to the GGTT (insert_page) */ } else { page_base += offset & PAGE_MASK; } /* If we get a fault while copying data, then (presumably) our * source page isn't available. Return the error and we'll * retry in the slow path. * If the object is non-shmem backed, we retry again with the * path that handles page fault. */ if (ggtt_write(&ggtt->iomap, page_base, page_offset, user_data, page_length)) { ret = -EFAULT; break; } remain -= page_length; user_data += page_length; offset += page_length; } intel_gt_flush_ggtt_writes(ggtt->vm.gt); i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU); i915_gem_object_unlock_fence(obj, fence); out_unpin: if (drm_mm_node_allocated(&node)) { ggtt->vm.clear_range(&ggtt->vm, node.start, node.size); remove_mappable_node(ggtt, &node); } else { i915_vma_unpin(vma); } out_rpm: intel_runtime_pm_put(rpm, wakeref); return ret; } /* Per-page copy function for the shmem pwrite fastpath. * Flushes invalid cachelines before writing to the target if * needs_clflush_before is set and flushes out any written cachelines after * writing if needs_clflush is set. */ static int shmem_pwrite(struct page *page, int offset, int len, char __user *user_data, bool needs_clflush_before, bool needs_clflush_after) { char *vaddr; int ret; vaddr = kmap(page); if (needs_clflush_before) drm_clflush_virt_range(vaddr + offset, len); ret = __copy_from_user(vaddr + offset, user_data, len); if (!ret && needs_clflush_after) drm_clflush_virt_range(vaddr + offset, len); kunmap(page); return ret ? -EFAULT : 0; } static int i915_gem_shmem_pwrite(struct drm_i915_gem_object *obj, const struct drm_i915_gem_pwrite *args) { unsigned int partial_cacheline_write; unsigned int needs_clflush; unsigned int offset, idx; struct dma_fence *fence; void __user *user_data; u64 remain; int ret; ret = i915_gem_object_lock_interruptible(obj, NULL); if (ret) return ret; ret = i915_gem_object_prepare_write(obj, &needs_clflush); if (ret) { i915_gem_object_unlock(obj); return ret; } fence = i915_gem_object_lock_fence(obj); i915_gem_object_finish_access(obj); i915_gem_object_unlock(obj); if (!fence) return -ENOMEM; /* If we don't overwrite a cacheline completely we need to be * careful to have up-to-date data by first clflushing. Don't * overcomplicate things and flush the entire patch. */ partial_cacheline_write = 0; if (needs_clflush & CLFLUSH_BEFORE) partial_cacheline_write = boot_cpu_data.x86_clflush_size - 1; user_data = u64_to_user_ptr(args->data_ptr); remain = args->size; offset = offset_in_page(args->offset); for (idx = args->offset >> PAGE_SHIFT; remain; idx++) { struct page *page = i915_gem_object_get_page(obj, idx); unsigned int length = min_t(u64, remain, PAGE_SIZE - offset); ret = shmem_pwrite(page, offset, length, user_data, (offset | length) & partial_cacheline_write, needs_clflush & CLFLUSH_AFTER); if (ret) break; remain -= length; user_data += length; offset = 0; } i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU); i915_gem_object_unlock_fence(obj, fence); return ret; } /** * Writes data to the object referenced by handle. * @dev: drm device * @data: ioctl data blob * @file: drm file * * On error, the contents of the buffer that were to be modified are undefined. */ int i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_i915_gem_pwrite *args = data; struct drm_i915_gem_object *obj; int ret; if (args->size == 0) return 0; if (!access_ok(u64_to_user_ptr(args->data_ptr), args->size)) return -EFAULT; obj = i915_gem_object_lookup(file, args->handle); if (!obj) return -ENOENT; /* Bounds check destination. */ if (range_overflows_t(u64, args->offset, args->size, obj->base.size)) { ret = -EINVAL; goto err; } /* Writes not allowed into this read-only object */ if (i915_gem_object_is_readonly(obj)) { ret = -EINVAL; goto err; } trace_i915_gem_object_pwrite(obj, args->offset, args->size); ret = -ENODEV; if (obj->ops->pwrite) ret = obj->ops->pwrite(obj, args); if (ret != -ENODEV) goto err; ret = i915_gem_object_wait(obj, I915_WAIT_INTERRUPTIBLE | I915_WAIT_ALL, MAX_SCHEDULE_TIMEOUT); if (ret) goto err; ret = i915_gem_object_pin_pages(obj); if (ret) goto err; ret = -EFAULT; /* We can only do the GTT pwrite on untiled buffers, as otherwise * it would end up going through the fenced access, and we'll get * different detiling behavior between reading and writing. * pread/pwrite currently are reading and writing from the CPU * perspective, requiring manual detiling by the client. */ if (!i915_gem_object_has_struct_page(obj) || cpu_write_needs_clflush(obj)) /* Note that the gtt paths might fail with non-page-backed user * pointers (e.g. gtt mappings when moving data between * textures). Fallback to the shmem path in that case. */ ret = i915_gem_gtt_pwrite_fast(obj, args); if (ret == -EFAULT || ret == -ENOSPC) { if (i915_gem_object_has_struct_page(obj)) ret = i915_gem_shmem_pwrite(obj, args); } i915_gem_object_unpin_pages(obj); err: i915_gem_object_put(obj); return ret; } /** * Called when user space has done writes to this buffer * @dev: drm device * @data: ioctl data blob * @file: drm file */ int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_i915_gem_sw_finish *args = data; struct drm_i915_gem_object *obj; obj = i915_gem_object_lookup(file, args->handle); if (!obj) return -ENOENT; /* * Proxy objects are barred from CPU access, so there is no * need to ban sw_finish as it is a nop. */ /* Pinned buffers may be scanout, so flush the cache */ i915_gem_object_flush_if_display(obj); i915_gem_object_put(obj); return 0; } void i915_gem_runtime_suspend(struct drm_i915_private *i915) { struct drm_i915_gem_object *obj, *on; int i; /* * Only called during RPM suspend. All users of the userfault_list * must be holding an RPM wakeref to ensure that this can not * run concurrently with themselves (and use the struct_mutex for * protection between themselves). */ list_for_each_entry_safe(obj, on, &i915->ggtt.userfault_list, userfault_link) __i915_gem_object_release_mmap_gtt(obj); /* * The fence will be lost when the device powers down. If any were * in use by hardware (i.e. they are pinned), we should not be powering * down! All other fences will be reacquired by the user upon waking. */ for (i = 0; i < i915->ggtt.num_fences; i++) { struct i915_fence_reg *reg = &i915->ggtt.fence_regs[i]; /* * Ideally we want to assert that the fence register is not * live at this point (i.e. that no piece of code will be * trying to write through fence + GTT, as that both violates * our tracking of activity and associated locking/barriers, * but also is illegal given that the hw is powered down). * * Previously we used reg->pin_count as a "liveness" indicator. * That is not sufficient, and we need a more fine-grained * tool if we want to have a sanity check here. */ if (!reg->vma) continue; GEM_BUG_ON(i915_vma_has_userfault(reg->vma)); reg->dirty = true; } } static void discard_ggtt_vma(struct i915_vma *vma) { struct drm_i915_gem_object *obj = vma->obj; spin_lock(&obj->vma.lock); if (!RB_EMPTY_NODE(&vma->obj_node)) { rb_erase(&vma->obj_node, &obj->vma.tree); RB_CLEAR_NODE(&vma->obj_node); } spin_unlock(&obj->vma.lock); } struct i915_vma * i915_gem_object_ggtt_pin_ww(struct drm_i915_gem_object *obj, struct i915_gem_ww_ctx *ww, const struct i915_ggtt_view *view, u64 size, u64 alignment, u64 flags) { struct drm_i915_private *i915 = to_i915(obj->base.dev); struct i915_ggtt *ggtt = &i915->ggtt; struct i915_vma *vma; int ret; if (flags & PIN_MAPPABLE && (!view || view->type == I915_GGTT_VIEW_NORMAL)) { /* * If the required space is larger than the available * aperture, we will not able to find a slot for the * object and unbinding the object now will be in * vain. Worse, doing so may cause us to ping-pong * the object in and out of the Global GTT and * waste a lot of cycles under the mutex. */ if (obj->base.size > ggtt->mappable_end) return ERR_PTR(-E2BIG); /* * If NONBLOCK is set the caller is optimistically * trying to cache the full object within the mappable * aperture, and *must* have a fallback in place for * situations where we cannot bind the object. We * can be a little more lax here and use the fallback * more often to avoid costly migrations of ourselves * and other objects within the aperture. * * Half-the-aperture is used as a simple heuristic. * More interesting would to do search for a free * block prior to making the commitment to unbind. * That caters for the self-harm case, and with a * little more heuristics (e.g. NOFAULT, NOEVICT) * we could try to minimise harm to others. */ if (flags & PIN_NONBLOCK && obj->base.size > ggtt->mappable_end / 2) return ERR_PTR(-ENOSPC); } new_vma: vma = i915_vma_instance(obj, &ggtt->vm, view); if (IS_ERR(vma)) return vma; if (i915_vma_misplaced(vma, size, alignment, flags)) { if (flags & PIN_NONBLOCK) { if (i915_vma_is_pinned(vma) || i915_vma_is_active(vma)) return ERR_PTR(-ENOSPC); if (flags & PIN_MAPPABLE && vma->fence_size > ggtt->mappable_end / 2) return ERR_PTR(-ENOSPC); } if (i915_vma_is_pinned(vma) || i915_vma_is_active(vma)) { discard_ggtt_vma(vma); goto new_vma; } ret = i915_vma_unbind(vma); if (ret) return ERR_PTR(ret); } ret = i915_vma_pin_ww(vma, ww, size, alignment, flags | PIN_GLOBAL); if (ret) return ERR_PTR(ret); if (vma->fence && !i915_gem_object_is_tiled(obj)) { mutex_lock(&ggtt->vm.mutex); i915_vma_revoke_fence(vma); mutex_unlock(&ggtt->vm.mutex); } ret = i915_vma_wait_for_bind(vma); if (ret) { i915_vma_unpin(vma); return ERR_PTR(ret); } return vma; } int i915_gem_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_i915_private *i915 = to_i915(dev); struct drm_i915_gem_madvise *args = data; struct drm_i915_gem_object *obj; int err; switch (args->madv) { case I915_MADV_DONTNEED: case I915_MADV_WILLNEED: break; default: return -EINVAL; } obj = i915_gem_object_lookup(file_priv, args->handle); if (!obj) return -ENOENT; err = mutex_lock_interruptible(&obj->mm.lock); if (err) goto out; if (i915_gem_object_has_pages(obj) && i915_gem_object_is_tiled(obj) && i915->quirks & QUIRK_PIN_SWIZZLED_PAGES) { if (obj->mm.madv == I915_MADV_WILLNEED) { GEM_BUG_ON(!i915_gem_object_has_tiling_quirk(obj)); i915_gem_object_clear_tiling_quirk(obj); i915_gem_object_make_shrinkable(obj); } if (args->madv == I915_MADV_WILLNEED) { GEM_BUG_ON(i915_gem_object_has_tiling_quirk(obj)); i915_gem_object_make_unshrinkable(obj); i915_gem_object_set_tiling_quirk(obj); } } if (obj->mm.madv != __I915_MADV_PURGED) obj->mm.madv = args->madv; if (i915_gem_object_has_pages(obj)) { struct list_head *list; if (i915_gem_object_is_shrinkable(obj)) { unsigned long flags; spin_lock_irqsave(&i915->mm.obj_lock, flags); if (obj->mm.madv != I915_MADV_WILLNEED) list = &i915->mm.purge_list; else list = &i915->mm.shrink_list; list_move_tail(&obj->mm.link, list); spin_unlock_irqrestore(&i915->mm.obj_lock, flags); } } /* if the object is no longer attached, discard its backing storage */ if (obj->mm.madv == I915_MADV_DONTNEED && !i915_gem_object_has_pages(obj)) i915_gem_object_truncate(obj); args->retained = obj->mm.madv != __I915_MADV_PURGED; mutex_unlock(&obj->mm.lock); out: i915_gem_object_put(obj); return err; } int i915_gem_init(struct drm_i915_private *dev_priv) { int ret; /* We need to fallback to 4K pages if host doesn't support huge gtt. */ if (intel_vgpu_active(dev_priv) && !intel_vgpu_has_huge_gtt(dev_priv)) mkwrite_device_info(dev_priv)->page_sizes = I915_GTT_PAGE_SIZE_4K; ret = i915_gem_init_userptr(dev_priv); if (ret) return ret; intel_uc_fetch_firmwares(&dev_priv->gt.uc); intel_wopcm_init(&dev_priv->wopcm); ret = i915_init_ggtt(dev_priv); if (ret) { GEM_BUG_ON(ret == -EIO); goto err_unlock; } /* * Despite its name intel_init_clock_gating applies both display * clock gating workarounds; GT mmio workarounds and the occasional * GT power context workaround. Worse, sometimes it includes a context * register workaround which we need to apply before we record the * default HW state for all contexts. * * FIXME: break up the workarounds and apply them at the right time! */ intel_init_clock_gating(dev_priv); ret = intel_gt_init(&dev_priv->gt); if (ret) goto err_unlock; return 0; /* * Unwinding is complicated by that we want to handle -EIO to mean * disable GPU submission but keep KMS alive. We want to mark the * HW as irrevisibly wedged, but keep enough state around that the * driver doesn't explode during runtime. */ err_unlock: i915_gem_drain_workqueue(dev_priv); if (ret != -EIO) { intel_uc_cleanup_firmwares(&dev_priv->gt.uc); i915_gem_cleanup_userptr(dev_priv); } if (ret == -EIO) { /* * Allow engines or uC initialisation to fail by marking the GPU * as wedged. But we only want to do this when the GPU is angry, * for all other failure, such as an allocation failure, bail. */ if (!intel_gt_is_wedged(&dev_priv->gt)) { i915_probe_error(dev_priv, "Failed to initialize GPU, declaring it wedged!\n"); intel_gt_set_wedged(&dev_priv->gt); } /* Minimal basic recovery for KMS */ ret = i915_ggtt_enable_hw(dev_priv); i915_ggtt_resume(&dev_priv->ggtt); intel_init_clock_gating(dev_priv); } i915_gem_drain_freed_objects(dev_priv); return ret; } void i915_gem_driver_register(struct drm_i915_private *i915) { i915_gem_driver_register__shrinker(i915); intel_engines_driver_register(i915); } void i915_gem_driver_unregister(struct drm_i915_private *i915) { i915_gem_driver_unregister__shrinker(i915); } void i915_gem_driver_remove(struct drm_i915_private *dev_priv) { intel_wakeref_auto_fini(&dev_priv->ggtt.userfault_wakeref); i915_gem_suspend_late(dev_priv); intel_gt_driver_remove(&dev_priv->gt); dev_priv->uabi_engines = RB_ROOT; /* Flush any outstanding unpin_work. */ i915_gem_drain_workqueue(dev_priv); i915_gem_drain_freed_objects(dev_priv); } void i915_gem_driver_release(struct drm_i915_private *dev_priv) { intel_gt_driver_release(&dev_priv->gt); intel_wa_list_free(&dev_priv->gt_wa_list); intel_uc_cleanup_firmwares(&dev_priv->gt.uc); i915_gem_cleanup_userptr(dev_priv); i915_gem_drain_freed_objects(dev_priv); drm_WARN_ON(&dev_priv->drm, !list_empty(&dev_priv->gem.contexts.list)); } static void i915_gem_init__mm(struct drm_i915_private *i915) { spin_lock_init(&i915->mm.obj_lock); init_llist_head(&i915->mm.free_list); INIT_LIST_HEAD(&i915->mm.purge_list); INIT_LIST_HEAD(&i915->mm.shrink_list); i915_gem_init__objects(i915); } void i915_gem_init_early(struct drm_i915_private *dev_priv) { i915_gem_init__mm(dev_priv); i915_gem_init__contexts(dev_priv); spin_lock_init(&dev_priv->fb_tracking.lock); } void i915_gem_cleanup_early(struct drm_i915_private *dev_priv) { i915_gem_drain_freed_objects(dev_priv); GEM_BUG_ON(!llist_empty(&dev_priv->mm.free_list)); GEM_BUG_ON(atomic_read(&dev_priv->mm.free_count)); drm_WARN_ON(&dev_priv->drm, dev_priv->mm.shrink_count); } int i915_gem_open(struct drm_i915_private *i915, struct drm_file *file) { struct drm_i915_file_private *file_priv; int ret; DRM_DEBUG("\n"); file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); if (!file_priv) return -ENOMEM; file->driver_priv = file_priv; file_priv->dev_priv = i915; file_priv->file = file; file_priv->bsd_engine = -1; file_priv->hang_timestamp = jiffies; ret = i915_gem_context_open(i915, file); if (ret) kfree(file_priv); return ret; } void i915_gem_ww_ctx_init(struct i915_gem_ww_ctx *ww, bool intr) { ww_acquire_init(&ww->ctx, &reservation_ww_class); INIT_LIST_HEAD(&ww->obj_list); ww->intr = intr; ww->contended = NULL; } static void i915_gem_ww_ctx_unlock_all(struct i915_gem_ww_ctx *ww) { struct drm_i915_gem_object *obj; while ((obj = list_first_entry_or_null(&ww->obj_list, struct drm_i915_gem_object, obj_link))) { list_del(&obj->obj_link); i915_gem_object_unlock(obj); } } void i915_gem_ww_unlock_single(struct drm_i915_gem_object *obj) { list_del(&obj->obj_link); i915_gem_object_unlock(obj); } void i915_gem_ww_ctx_fini(struct i915_gem_ww_ctx *ww) { i915_gem_ww_ctx_unlock_all(ww); WARN_ON(ww->contended); ww_acquire_fini(&ww->ctx); } int __must_check i915_gem_ww_ctx_backoff(struct i915_gem_ww_ctx *ww) { int ret = 0; if (WARN_ON(!ww->contended)) return -EINVAL; i915_gem_ww_ctx_unlock_all(ww); if (ww->intr) ret = dma_resv_lock_slow_interruptible(ww->contended->base.resv, &ww->ctx); else dma_resv_lock_slow(ww->contended->base.resv, &ww->ctx); if (!ret) list_add_tail(&ww->contended->obj_link, &ww->obj_list); ww->contended = NULL; return ret; } #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) #include "selftests/mock_gem_device.c" #include "selftests/i915_gem.c" #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
You can’t perform that action at this time.