drm fixes for 7.3-rc5

client:
 - fix restore of partially initialized client
 
 i915:
 - Fix incorrect RCU teardown order leading to endless loop
 - Fix DP MST TU and FEC handling for disconnected streams
 - Fix selective fetch disable, again
 - Fix export namespace for kunit helpers
 - Workaround eDP flicker on a specific laptop model
 
 xe:
 - CRI throttle reasons report
 - TLB invalidation at wedge
 - SVM eviction and VM close
 - Display corruption on LNL on Xen PV
 - W/a fix and addition
 
 amdgpu:
 - Display ref count fix
 - Userq fixes
 - VCN 4, 5 reset fixes
 - Fixes for various error paths
 - Stack frame size fixes for various combinations of compilers and configs
 
 amdkfd:
 - Possible UAF fix
 
 nouveau:
 - runtime suspend/resume fixes for newer firmware
 - rcu free the scheduler
 - fix VRAM pinning
 - fix double free
 - fix reference leaks
 - fix runtime PM leak
 - fix cursor list usage problems
 - fix HDMI config rejection without SCDC
 
 virtio:
 - fix a bunch of object/memory leaks in failure paths
 - add pixel blend mode property to cursor plane
 - revert prime buffers import
 - sync shmem backing on guest transfers
 
 imagination:
 - propogate map failures properly
 - fix page count in map interface
 - clamp freelist reconstruction requests
 
 ivpu:
 - use separate flag for job timeout
 
 bridge:
 - samsung-dsim: fix TE GPIO lifetime for host attach
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmq29FYACgkQDHTzWXnE
 hr4pnw/+JhcAdts2saCNt/j7EtD6DqXJvyfo4BVyjYCSVP3YEZuZCrpBJh02UUUo
 sU8MgjmzwXHRxflTk8DLQMMKhN7cvFmzdREro5x7gJkCi2m/x3eanafQJS0iyvqN
 rl+K+rVeEmrOELN7auNFSl3kBtvR9yvbPZtBKNNe1IhCSq/iWKm5I4HLwP85diJZ
 OHOPuXXh+QFhtXsbwH3p1NsbWb/Kiovbevqitd/X/9CFZRMiiwuy5QFX8xOAW4CR
 0id0njQPWVbLupuqZRGSDMAsqiVq/ClKIb8X0uMzU92y6l8oMsamPXxbs7Z6pfsT
 d8k9+zMP0LXXkwdS/sTI/9axJ5LZmd4mqEL+iAkoKkO2EGnDerzyklOCWtk38qvc
 qpjhQDW77AGLoQbISKZzI2Ce9B3RotdDghyd0Bt++GJegKQAAvevEQKNzWVvlMd3
 aQcpI/EKf1PXJnX44LtDqNmoEmmEy4fKOICu52GzpPYOGaSQL25tuA7/KGNX35D8
 hY7KiJRYaPcU0hAwAPLvQDHUvFBhDJD4BHGO01YOZ0fZzBARwE57Hwp3hxkHez64
 CbcVZ5bzTREmZ3KHKI79OMhKUfCbq+E6YoJ1jxSDs902LWTrULx/iMCA+7NTZL1U
 f/9nIe2fkA0/D+++CraKd+HYkfdxvZZwuRuZtp5jWqRLxB4bdes=
 =oRa2
 -----END PGP SIGNATURE-----

Merge tag 'drm-fixes-2026-09-26' of https://gitlab.freedesktop.org/drm/kernel

Pull drm fixes from Dave Airlie:
 "While most of this is AI inspired fixes for error handling paths,
  leaks and use after frees, there are some normal things.

  nouveau has probably the biggest changes with some fixes to stabilise
  runtime suspend/resume on 570 firmware which regressed after we moved
  from 535, there are some fixes to stackframe issues seen with amdgpu,
  and otherwise the usual bunch of i915/xe/amdgpu fixes, and some
  virtio-gpu fixes.

  Hopefully it will start to quiten down a bit from here.

  client:
   - fix restore of partially initialized client

  i915:
   - Fix incorrect RCU teardown order leading to endless loop
   - Fix DP MST TU and FEC handling for disconnected streams
   - Fix selective fetch disable, again
   - Fix export namespace for kunit helpers
   - Workaround eDP flicker on a specific laptop model

  xe:
   - CRI throttle reasons report
   - TLB invalidation at wedge
   - SVM eviction and VM close
   - Display corruption on LNL on Xen PV
   - W/a fix and addition

  amdgpu:
   - Display ref count fix
   - Userq fixes
   - VCN 4, 5 reset fixes
   - Fixes for various error paths
   - Stack frame size fixes for various combinations of compilers and
     configs

  amdkfd:
   - Possible UAF fix

  nouveau:
   - runtime suspend/resume fixes for newer firmware
   - rcu free the scheduler
   - fix VRAM pinning
   - fix double free
   - fix reference leaks
   - fix runtime PM leak
   - fix cursor list usage problems
   - fix HDMI config rejection without SCDC

  virtio:
   - fix a bunch of object/memory leaks in failure paths
   - add pixel blend mode property to cursor plane
   - revert prime buffers import
   - sync shmem backing on guest transfers

  imagination:
   - propogate map failures properly
   - fix page count in map interface
   - clamp freelist reconstruction requests

  ivpu:
   - use separate flag for job timeout

  bridge:
   - samsung-dsim: fix TE GPIO lifetime for host attach"

* tag 'drm-fixes-2026-09-26' of https://gitlab.freedesktop.org/drm/kernel: (60 commits)
  drm/amd/display: Bump frame warning limit for all builds of dml
  drm/imagination: clamp freelist reconstruction requests
  drm/imagination: Fix page count for page table for map() interface
  drm/imagination: Propagate map failures correctly from pvr_mmu_map_sgl()
  drm/amd/display: Bump frame warning limit for clang builds of dml
  drm/amd/display: Relax DML frame limit with UBSAN
  drm/amdgpu: Fix runtime PM leak in amdgpu_debugfs_test_ib_show()
  drm/amdgpu: Fix last_update fence leak in amdgpu_vm_init()
  drm/amdgpu: Fix acpi device leak in amdgpu_acpi_enumerate_xcc()
  drm/amdgpu: Fix vmid_wait fence leak in amdgpu_ring_init()
  drm/amdkfd: fix use-after-free and multi-container gap in kfd_dev_mapping
  drm/amdgpu/vcn4.0.3: fix video_timeout unit mismatch in jpeg reset wait
  drm/amdgpu/vcn5.0.1: fix video_timeout unit mismatch in jpeg reset wait
  drm/amdgpu/userq: fix double jiffies conversion in hang detect timeout
  drm/amdgpu: move userq fence wait out of signalling section
  drm/amd/display: Fix dc stream excess put in dm_update_crtc_state()
  drm/xe: Add wa_14025941587 to xe2, xe3 and xe3p platforms
  drm/xe: harden adjust_idledly() against divide-by-zero and overflow
  drm/xe: Limit sg segment size to PAGE_SIZE on Xen PV
  drm/i915: fix incorrect RCU teardown order
  ...
This commit is contained in:
Linus Torvalds 2026-09-25 16:00:18 -07:00
commit 6812ce4e43
79 changed files with 810 additions and 135 deletions

View File

@ -515,6 +515,7 @@ void ivpu_prepare_for_reset(struct ivpu_device *vdev)
{
ivpu_hw_irq_disable(vdev);
disable_irq(vdev->irq);
atomic_set(&vdev->job_timeout_detected, 0);
flush_work(&vdev->irq_dct_work);
flush_work(&vdev->context_abort_work);
flush_work(&vdev->job_destroy_work);
@ -710,7 +711,7 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
vdev->context_xa_limit.max = IVPU_USER_CONTEXT_MAX_SSID;
atomic64_set(&vdev->unique_id_counter, 0);
atomic_set(&vdev->job_timeout_counter, 0);
atomic_set(&vdev->faults_detected, 0);
atomic_set(&vdev->job_timeout_detected, 0);
xa_init_flags(&vdev->context_xa, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ);
xa_init_flags(&vdev->submitted_jobs_xa, XA_FLAGS_ALLOC1);
xa_init_flags(&vdev->db_xa, XA_FLAGS_ALLOC1);

View File

@ -171,7 +171,7 @@ struct ivpu_device {
struct xarray submitted_jobs_xa;
struct ivpu_ipc_consumer job_done_consumer;
atomic_t job_timeout_counter;
atomic_t faults_detected;
atomic_t job_timeout_detected;
atomic64_t unique_id_counter;

View File

@ -621,7 +621,6 @@ bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_
* status and ensure both are handled in the same way
*/
job->file_priv->has_mmu_faults = true;
atomic_set(&vdev->faults_detected, 1);
queue_work(system_percpu_wq, &vdev->context_abort_work);
return true;
}
@ -1175,10 +1174,10 @@ static int reset_engine_and_mark_faulty_contexts(struct ivpu_device *vdev)
return ret;
/*
* If faults are detected, ignore guilty contexts from engine reset as NPU may not be stuck
* and could return currently running good context and faulty contexts are already marked
* If job timeout is detected, read guilty context from engine reset, for other reasons
* faulty context is already known
*/
if (atomic_cmpxchg(&vdev->faults_detected, 1, 0) == 1)
if (atomic_cmpxchg(&vdev->job_timeout_detected, 1, 0) == 0)
return 0;
num_impacted_contexts = resp.payload.engine_reset_done.num_impacted_contexts;

View File

@ -964,7 +964,6 @@ void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
file_priv = xa_load(&vdev->context_xa, ssid);
if (file_priv) {
if (!READ_ONCE(file_priv->has_mmu_faults)) {
atomic_set(&vdev->faults_detected, 1);
ivpu_mmu_dump_event(vdev, event);
WRITE_ONCE(file_priv->has_mmu_faults, true);
}

View File

@ -229,6 +229,7 @@ static void ivpu_job_timeout_work(struct work_struct *work)
ivpu_jsm_state_dump(vdev);
ivpu_dev_coredump(vdev);
atomic_set(&vdev->job_timeout_detected, 1);
queue_work(system_percpu_wq, &vdev->context_abort_work);
}

View File

@ -1167,8 +1167,10 @@ int amdgpu_acpi_enumerate_xcc(void)
}
xcc_info = kzalloc_obj(struct amdgpu_acpi_xcc_info);
if (!xcc_info)
if (!xcc_info) {
acpi_dev_put(acpi_dev);
return -ENOMEM;
}
INIT_LIST_HEAD(&xcc_info->list);
xcc_info->handle = acpi_device_handle(acpi_dev);

View File

@ -1780,8 +1780,10 @@ static int amdgpu_debugfs_test_ib_show(struct seq_file *m, void *unused)
/* Avoid accidently unparking the sched thread during GPU reset */
r = down_write_killable(&adev->reset_domain->sem);
if (r)
if (r) {
pm_runtime_put_autosuspend(dev->dev);
return r;
}
/* hold on the scheduler */
for (i = 0; i < AMDGPU_MAX_RINGS; i++) {

View File

@ -68,6 +68,9 @@ amdgpu_eviction_fence_suspend_worker(struct work_struct *work)
mutex_lock(&uq_mgr->userq_mutex);
/* Fence waits are not allowed in a fence signalling critical section. */
amdgpu_userq_wait_for_signal(uq_mgr);
/*
* This is intentionally after taking the userq_mutex since we do
* allocate memory while holding this lock, but only after ensuring that

View File

@ -254,7 +254,6 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
ring->adev = adev;
ring->num_hw_submission = sched_hw_submission;
ring->sched_score = sched_score;
ring->vmid_wait = dma_fence_get_stub();
ring->idx = adev->num_rings++;
adev->rings[ring->idx] = ring;
@ -374,6 +373,7 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
ring->max_dw = max_dw;
ring->hw_prio = hw_prio;
ring->vmid_wait = dma_fence_get_stub();
if (!ring->no_scheduler && ring->funcs->type < AMDGPU_HW_IP_NUM) {
hw_ip = ring->funcs->type;

View File

@ -184,27 +184,27 @@ static void amdgpu_userq_hang_detect_work(struct work_struct *work)
void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue)
{
struct amdgpu_device *adev;
unsigned long timeout_ms;
unsigned long timeout_jiffies;
adev = queue->userq_mgr->adev;
/* Determine timeout based on queue type */
switch (queue->queue_type) {
case AMDGPU_RING_TYPE_GFX:
timeout_ms = adev->gfx_timeout;
timeout_jiffies = adev->gfx_timeout;
break;
case AMDGPU_RING_TYPE_COMPUTE:
timeout_ms = adev->compute_timeout;
timeout_jiffies = adev->compute_timeout;
break;
case AMDGPU_RING_TYPE_SDMA:
timeout_ms = adev->sdma_timeout;
timeout_jiffies = adev->sdma_timeout;
break;
default:
timeout_ms = adev->gfx_timeout;
timeout_jiffies = adev->gfx_timeout;
break;
}
queue_delayed_work(adev->reset_domain->wq, &queue->hang_detect_work,
msecs_to_jiffies(timeout_ms));
timeout_jiffies);
}
void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell)
@ -1272,7 +1272,7 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
return ret;
}
static void
void
amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr)
{
struct amdgpu_usermode_queue *queue;
@ -1291,8 +1291,6 @@ amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr)
void
amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr)
{
/* Wait for any pending userqueue fence work to finish */
amdgpu_userq_wait_for_signal(uq_mgr);
amdgpu_userq_evict_all(uq_mgr);
}

View File

@ -162,6 +162,7 @@ void amdgpu_userq_mgr_cancel_reset_work(struct amdgpu_device *adev);
void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr);
void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr);
void amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr);
void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,

View File

@ -2678,6 +2678,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,
amdgpu_bo_unref(&root_bo);
error_free_delayed:
dma_fence_put(vm->last_update);
dma_fence_put(vm->last_tlb_flush);
dma_fence_put(vm->last_unlocked);
ttm_lru_bulk_move_fini(&adev->mman.bdev, &vm->lru_bulk_move);

View File

@ -1689,7 +1689,8 @@ static int vcn_v4_0_3_reset_jpeg_pre_helper(struct amdgpu_device *adev, int inst
/* if Jobs are still pending after timeout,
* We'll handle them in the bottom helper
*/
amdgpu_fence_wait_polling(ring, wait_seq, adev->video_timeout);
amdgpu_fence_wait_polling(ring, wait_seq,
jiffies_to_usecs(adev->video_timeout));
}
return 0;

View File

@ -1335,7 +1335,8 @@ static int vcn_v5_0_1_reset_jpeg_pre_helper(struct amdgpu_device *adev, int inst
/* if Jobs are still pending after timeout,
* We'll handle them in the bottom helper
*/
amdgpu_fence_wait_polling(ring, wait_seq, adev->video_timeout);
amdgpu_fence_wait_polling(ring, wait_seq,
jiffies_to_usecs(adev->video_timeout));
}
return 0;

View File

@ -35,6 +35,7 @@
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/pseudo_fs.h>
#include <linux/ptrace.h>
#include <linux/dma-buf.h>
#include <linux/processor.h>
@ -70,18 +71,54 @@ static const struct class kfd_class = {
};
/*
* Cache the address space of the chardev on first open so that the reset
* path can drop all userspace mappings of doorbell and MMIO ranges via
* unmap_mapping_range().
* Private pseudo-filesystem for KFD, Provides a stable, module-owned
* inode whose address_space is the unmap target for all /dev/kfd
* openers during GPU reset.
*/
static struct address_space *kfd_dev_mapping;
static struct vfsmount *kfd_fs_mnt;
static int kfd_fs_cnt;
static int kfd_fs_init_fs_context(struct fs_context *fc)
{
return init_pseudo(fc, 0x4b464400 /* "KFD" */) ? 0 : -ENOMEM;
}
static struct file_system_type kfd_fs_type = {
.name = "kfd",
.init_fs_context = kfd_fs_init_fs_context,
.kill_sb = kill_anon_super,
};
static struct inode *kfd_fs_inode_new(void)
{
struct inode *inode;
int r;
r = simple_pin_fs(&kfd_fs_type, &kfd_fs_mnt, &kfd_fs_cnt);
if (r < 0)
return ERR_PTR(r);
inode = alloc_anon_inode(kfd_fs_mnt->mnt_sb);
if (IS_ERR(inode))
simple_release_fs(&kfd_fs_mnt, &kfd_fs_cnt);
return inode;
}
static void kfd_fs_inode_free(struct inode *inode)
{
if (inode) {
iput(inode);
simple_release_fs(&kfd_fs_mnt, &kfd_fs_cnt);
}
}
static struct inode *kfd_anon_inode;
void kfd_dev_unmap_mapping_range(loff_t const holebegin, loff_t const holelen)
{
struct address_space *mapping = READ_ONCE(kfd_dev_mapping);
if (mapping)
unmap_mapping_range(mapping, holebegin, holelen, 1);
if (kfd_anon_inode)
unmap_mapping_range(kfd_anon_inode->i_mapping, holebegin, holelen, 1);
}
static inline struct kfd_process_device *kfd_lock_pdd_by_id(struct kfd_process *p, __u32 gpu_id)
@ -107,6 +144,13 @@ int kfd_chardev_init(void)
{
int err = 0;
kfd_anon_inode = kfd_fs_inode_new();
if (IS_ERR(kfd_anon_inode)) {
err = PTR_ERR(kfd_anon_inode);
kfd_anon_inode = NULL;
return err;
}
kfd_char_dev_major = register_chrdev(0, kfd_dev_name, &kfd_fops);
err = kfd_char_dev_major;
if (err < 0)
@ -130,6 +174,8 @@ int kfd_chardev_init(void)
err_class_create:
unregister_chrdev(kfd_char_dev_major, kfd_dev_name);
err_register_chrdev:
kfd_fs_inode_free(kfd_anon_inode);
kfd_anon_inode = NULL;
return err;
}
@ -138,6 +184,8 @@ void kfd_chardev_exit(void)
device_destroy(&kfd_class, MKDEV(kfd_char_dev_major, 0));
class_unregister(&kfd_class);
unregister_chrdev(kfd_char_dev_major, kfd_dev_name);
kfd_fs_inode_free(kfd_anon_inode);
kfd_anon_inode = NULL;
kfd_device = NULL;
}
@ -150,12 +198,7 @@ static int kfd_open(struct inode *inode, struct file *filep)
if (iminor(inode) != 0)
return -ENODEV;
/*
* /dev/kfd is a single chardev so all opens share one inode. Cache
* its address_space on the first open for use by the reset path.
*/
if (!READ_ONCE(kfd_dev_mapping))
cmpxchg(&kfd_dev_mapping, NULL, inode->i_mapping);
filep->f_mapping = kfd_anon_inode->i_mapping;
is_32bit_user_mode = in_compat_syscall();

View File

@ -5583,8 +5583,10 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
skip_modeset:
/* Release extra reference */
if (new_stream)
if (new_stream) {
dc_stream_release(new_stream);
new_stream = NULL;
}
new_stream = NULL;
/*

View File

@ -29,14 +29,14 @@ dml_ccflags := $(CC_FLAGS_FPU)
dml_rcflags := $(CC_FLAGS_NO_FPU)
ifneq ($(CONFIG_FRAME_WARN),0)
ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)),y)
ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)$(CONFIG_UBSAN)),y)
ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_COMPILE_TEST),yy)
frame_warn_limit := 4096
else
frame_warn_limit := 3072
endif
else
frame_warn_limit := 2048
frame_warn_limit := 3072
endif
ifeq ($(call test-lt, $(CONFIG_FRAME_WARN), $(frame_warn_limit)),y)

View File

@ -28,14 +28,14 @@ dml2_ccflags := $(CC_FLAGS_FPU)
dml2_rcflags := $(CC_FLAGS_NO_FPU)
ifneq ($(CONFIG_FRAME_WARN),0)
ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)),y)
ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)$(CONFIG_UBSAN)),y)
ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_COMPILE_TEST),yy)
frame_warn_limit := 4096
else
frame_warn_limit := 3072
endif
else
frame_warn_limit := 2056
frame_warn_limit := 3072
endif
ifeq ($(call test-lt, $(CONFIG_FRAME_WARN), $(frame_warn_limit)),y)

View File

@ -1862,7 +1862,7 @@ static int samsung_dsim_register_te_irq(struct samsung_dsim *dsi, struct device
int te_gpio_irq;
int ret;
dsi->te_gpio = devm_gpiod_get_optional(dev, "te", GPIOD_IN);
dsi->te_gpio = gpiod_get_optional(dev, "te", GPIOD_IN);
if (!dsi->te_gpio)
return 0;
else if (IS_ERR(dsi->te_gpio))

View File

@ -42,6 +42,14 @@ static int drm_fbdev_client_restore(struct drm_client_dev *client, bool force)
{
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
/*
* The client is registered before the initial fbdev probe.
* If probing failed, the client remains registered but there
* is no valid fbdev framebuffer to restore.
*/
if (!fb_helper->info || !fb_helper->fb)
return 0;
drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
return 0;

View File

@ -536,7 +536,8 @@ static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb,
struct intel_display *display = to_intel_display(plane);
enum pipe pipe = plane->pipe;
if (!crtc_state->enable_psr2_sel_fetch)
if (!crtc_state->enable_psr2_sel_fetch &&
!crtc_state->clear_psr2_sel_fetch)
return;
intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0);
@ -569,8 +570,10 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb,
struct intel_display *display = to_intel_display(plane);
enum pipe pipe = plane->pipe;
if (!crtc_state->enable_psr2_sel_fetch)
if (!crtc_state->enable_psr2_sel_fetch) {
i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
return;
}
if (drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0) {
if (crtc_state->enable_psr2_su_region_et) {

View File

@ -1187,6 +1187,8 @@ struct intel_crtc_state {
bool has_sel_update;
bool enable_psr2_sel_fetch;
bool enable_psr2_su_region_et;
/* Drop the stale selective fetch enable bits as selective fetch is turned off */
bool clear_psr2_sel_fetch;
bool req_psr2_sdp_prior_scanline;
bool has_panel_replay;
bool link_off_after_as_sdp_when_pr_active;

View File

@ -3,6 +3,8 @@
* Copyright © 2026 Intel Corporation
*/
#include <kunit/visibility.h>
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/log2.h>
@ -1302,14 +1304,14 @@ void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps)
const struct intel_dp_link_caps_test_ops i915_display_dp_link_caps_test_ops = {
INTEL_DP_LINK_CAPS_TEST_OPS_INIT
};
EXPORT_SYMBOL(i915_display_dp_link_caps_test_ops);
EXPORT_SYMBOL_IF_KUNIT(i915_display_dp_link_caps_test_ops);
#else
const struct intel_dp_link_caps_test_ops intel_display_dp_link_caps_test_ops = {
INTEL_DP_LINK_CAPS_TEST_OPS_INIT
};
EXPORT_SYMBOL(intel_display_dp_link_caps_test_ops);
EXPORT_SYMBOL_IF_KUNIT(intel_display_dp_link_caps_test_ops);
#endif /* I915 */

View File

@ -2825,14 +2825,14 @@ void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training
const struct intel_dp_link_training_test_ops i915_display_dp_link_training_test_ops = {
INTEL_DP_LINK_TRAINING_TEST_OPS_INIT
};
EXPORT_SYMBOL(i915_display_dp_link_training_test_ops);
EXPORT_SYMBOL_IF_KUNIT(i915_display_dp_link_training_test_ops);
#else
const struct intel_dp_link_training_test_ops intel_display_dp_link_training_test_ops = {
INTEL_DP_LINK_TRAINING_TEST_OPS_INIT
};
EXPORT_SYMBOL(intel_display_dp_link_training_test_ops);
EXPORT_SYMBOL_IF_KUNIT(intel_display_dp_link_training_test_ops);
#endif /* I915 */

View File

@ -852,7 +852,8 @@ static u8 get_pipes_downstream_of_mst_port(struct intel_atomic_state *state,
if (&connector->mst.dp->mst.mgr != mst_mgr)
continue;
if (connector->mst.port != parent_port &&
if (parent_port &&
connector->mst.port != parent_port &&
!drm_dp_mst_port_downstream_of_parent(mst_mgr,
connector->mst.port,
parent_port))
@ -2167,6 +2168,27 @@ bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
return false;
}
bool intel_dp_mst_stream_disconnected(struct intel_atomic_state *state,
const struct intel_crtc *crtc)
{
struct intel_connector *connector;
connector = get_connector_in_state_for_crtc(state, crtc);
if (!connector)
return false;
if (!connector->mst.dp)
return false;
if (!connector->mst.dp->mst.mgr.mst_state)
return true;
if (drm_connector_is_unregistered(&connector->base))
return true;
return false;
}
/**
* intel_dp_mst_prepare_probe - Prepare an MST link for topology probing
* @intel_dp: DP port object

View File

@ -28,6 +28,8 @@ int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
struct intel_link_bw_limits *limits);
bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
struct intel_crtc *crtc);
bool intel_dp_mst_stream_disconnected(struct intel_atomic_state *state,
const struct intel_crtc *crtc);
void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp);
bool intel_dp_mst_verify_dpcd_state(struct intel_dp *intel_dp);

View File

@ -64,7 +64,8 @@ void intel_link_bw_init_limits(struct intel_atomic_state *state,
intel_atomic_get_new_crtc_state(state, crtc);
int forced_bpp_x16 = get_forced_link_bpp_x16(state, crtc);
if (state->base.duplicated && crtc_state) {
if ((state->base.duplicated && crtc_state) ||
intel_dp_mst_stream_disconnected(state, crtc)) {
limits->max_bpp_x16[pipe] = crtc_state->max_link_bpp_x16;
if (intel_dsc_enabled_on_link(crtc_state))
limits->link_dsc_pipes |= BIT(pipe);

View File

@ -2883,6 +2883,8 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
struct intel_display *display = to_intel_display(state);
const struct intel_crtc_state *old_crtc_state =
intel_atomic_get_old_crtc_state(state, crtc);
struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
struct intel_plane_state *new_plane_state, *old_plane_state;
struct intel_plane *plane;
@ -2895,6 +2897,19 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,
bool full_update = false, su_area_changed;
int i, ret;
/*
* Selective fetch is not always usable, for instance it is dropped
* while pipe CRC is active. The planes keep their selective fetch
* enable bit set in hardware over that, and a plane disabled while
* selective fetch is off never gets the bit cleared. Once selective
* fetch comes back the hardware would resume fetching for a plane that
* is no longer enabled and keep its DDB range reserved, so have the
* plane update drop the bit for every plane of the pipe as selective
* fetch is turned off.
*/
crtc_state->clear_psr2_sel_fetch = old_crtc_state->enable_psr2_sel_fetch &&
!crtc_state->enable_psr2_sel_fetch;
if (!crtc_state->enable_psr2_sel_fetch)
return 0;

View File

@ -257,6 +257,9 @@ static struct intel_quirk intel_quirks[] = {
/* Dell XPS 13 7390 2-in-1 */
{ 0x8a52, 0x1028, 0x08b0, quirk_edp_limit_rate_hbr2 },
/* HP Pavilion Plus Laptop 14-ew1xxx */
{ 0x7d55, 0x103c, 0x8c31, quirk_edp_limit_rate_hbr2 },
/* Xiaomi Book Pro 14 2026 */
{ 0xb081, 0x1d72, 0x2424, quirk_disable_psr2 },
};

View File

@ -885,7 +885,8 @@ static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb,
struct intel_display *display = to_intel_display(plane);
enum pipe pipe = plane->pipe;
if (!crtc_state->enable_psr2_sel_fetch)
if (!crtc_state->enable_psr2_sel_fetch &&
!crtc_state->clear_psr2_sel_fetch)
return;
intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0);
@ -1634,10 +1635,8 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb,
struct intel_display *display = to_intel_display(plane);
enum pipe pipe = plane->pipe;
if (!crtc_state->enable_psr2_sel_fetch)
return;
if (drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0)
if (crtc_state->enable_psr2_sel_fetch &&
drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0)
intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id),
SEL_FETCH_PLANE_CTL_ENABLE);
else

View File

@ -89,6 +89,7 @@ struct drm_i915_gem_object *i915_gem_object_alloc(void)
void i915_gem_object_free(struct drm_i915_gem_object *obj)
{
dma_resv_fini(&obj->base._resv);
return kmem_cache_free(slab_objects, obj);
}
@ -144,7 +145,6 @@ void __i915_gem_object_fini(struct drm_i915_gem_object *obj)
{
mutex_destroy(&obj->mm.get_page.lock);
mutex_destroy(&obj->mm.get_dma_page.lock);
dma_resv_fini(&obj->base._resv);
}
/**

View File

@ -8,6 +8,7 @@
#include "pvr_vm.h"
#include <drm/drm_gem.h>
#include <drm/drm_print.h>
#include <linux/slab.h>
#include <linux/xarray.h>
#include <uapi/drm/pvr_drm.h>
@ -612,13 +613,21 @@ pvr_free_list_process_reconstruct_req(struct pvr_device *pvr_dev,
};
struct rogue_fwif_freelists_reconstruction_data *resp =
&resp_cmd.cmd_data.free_lists_reconstruction_data;
u32 count = min_t(u32, req->freelist_count,
ARRAY_SIZE(req->freelist_ids));
for (u32 i = 0; i < req->freelist_count; i++)
if (count != req->freelist_count) {
drm_warn_once(from_pvr_device(pvr_dev),
"Requested reconstruction of %u freelists, limiting to %u\n",
req->freelist_count, count);
}
for (u32 i = 0; i < count; i++)
pvr_free_list_reconstruct(pvr_dev, req->freelist_ids[i]);
resp->freelist_count = req->freelist_count;
resp->freelist_count = count;
memcpy(resp->freelist_ids, req->freelist_ids,
req->freelist_count * sizeof(resp->freelist_ids[0]));
count * sizeof(resp->freelist_ids[0]));
WARN_ON(pvr_kccb_send_cmd(pvr_dev, &resp_cmd, NULL));
}

View File

@ -12,6 +12,7 @@
#include "pvr_rogue_mmu_defs.h"
#include <drm/drm_drv.h>
#include <drm/drm_print.h>
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/dma-mapping.h>
@ -2335,6 +2336,7 @@ void pvr_mmu_op_context_destroy(struct pvr_mmu_op_context *op_ctx)
* pvr_mmu_op_context_create() - Create an MMU op context.
* @ctx: MMU context associated with owning VM context.
* @sgt: Scatter gather table containing pages pinned for use by this context.
* @device_addr: Virtual device address at the start of the requested mapping.
* @sgt_offset: Start offset of the requested device-virtual memory mapping.
* @size: Size in bytes of the requested device-virtual memory mapping. For an
* unmapping, this should be zero so that no page tables are allocated.
@ -2346,8 +2348,9 @@ void pvr_mmu_op_context_destroy(struct pvr_mmu_op_context *op_ctx)
*/
struct pvr_mmu_op_context *
pvr_mmu_op_context_create(struct pvr_mmu_context *ctx, struct sg_table *sgt,
u64 sgt_offset, u64 size)
u64 device_addr, u64 sgt_offset, u64 size)
{
u64 start_addr = device_addr + sgt_offset;
int err;
struct pvr_mmu_op_context *op_ctx = kzalloc_obj(*op_ctx);
@ -2363,16 +2366,16 @@ pvr_mmu_op_context_create(struct pvr_mmu_context *ctx, struct sg_table *sgt,
if (size) {
/*
* The number of page table objects we need to prealloc is
* indicated by the mapping size, start offset and the sizes
* indicated by the mapping size, start address and the sizes
* of the areas mapped per PT or PD. The range calculation is
* identical to that for the index into a table for a device
* address, so we reuse those functions here.
*/
const u32 l1_start_idx = pvr_page_table_l2_idx(sgt_offset);
const u32 l1_end_idx = pvr_page_table_l2_idx(sgt_offset + size);
const u32 l1_start_idx = pvr_page_table_l2_idx(start_addr);
const u32 l1_end_idx = pvr_page_table_l2_idx(start_addr + size);
const u32 l1_count = l1_end_idx - l1_start_idx + 1;
const u32 l0_start_idx = pvr_page_table_l1_idx(sgt_offset);
const u32 l0_end_idx = pvr_page_table_l1_idx(sgt_offset + size);
const u32 l0_start_idx = pvr_page_table_l1_idx(start_addr);
const u32 l0_end_idx = pvr_page_table_l1_idx(start_addr + size);
const u32 l0_count = l0_end_idx - l0_start_idx + 1;
/*
@ -2553,7 +2556,9 @@ pvr_mmu_map_sgl(struct pvr_mmu_op_context *op_ctx, struct scatterlist *sgl,
err_destroy_pages:
memcpy(&op_ctx->curr_page, &ptr_copy, sizeof(op_ctx->curr_page));
err = pvr_mmu_op_context_unmap_curr_page(op_ctx, page);
if (pvr_mmu_op_context_unmap_curr_page(op_ctx, page))
drm_err(from_pvr_device(op_ctx->mmu_ctx->pvr_dev),
"%s : Failure in unmapping pages\n", __func__);
return err;
}

View File

@ -99,7 +99,7 @@ dma_addr_t pvr_mmu_get_root_table_dma_addr(struct pvr_mmu_context *ctx);
void pvr_mmu_op_context_destroy(struct pvr_mmu_op_context *op_ctx);
struct pvr_mmu_op_context *
pvr_mmu_op_context_create(struct pvr_mmu_context *ctx,
struct sg_table *sgt, u64 sgt_offset, u64 size);
struct sg_table *sgt, u64 device_addr, u64 sgt_offset, u64 size);
int pvr_mmu_map(struct pvr_mmu_op_context *op_ctx, u64 size, u64 flags,
u64 device_addr);

View File

@ -276,7 +276,7 @@ pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_op,
goto err_bind_op_fini;
bind_op->mmu_op_ctx =
pvr_mmu_op_context_create(vm_ctx->mmu_ctx, sgt, offset, size);
pvr_mmu_op_context_create(vm_ctx->mmu_ctx, sgt, device_addr, offset, size);
err = PTR_ERR_OR_ZERO(bind_op->mmu_op_ctx);
if (err) {
bind_op->mmu_op_ctx = NULL;
@ -318,7 +318,7 @@ pvr_vm_bind_op_unmap_init(struct pvr_vm_bind_op *bind_op,
}
bind_op->mmu_op_ctx =
pvr_mmu_op_context_create(vm_ctx->mmu_ctx, NULL, 0, 0);
pvr_mmu_op_context_create(vm_ctx->mmu_ctx, NULL, device_addr, 0, 0);
err = PTR_ERR_OR_ZERO(bind_op->mmu_op_ctx);
if (err) {
bind_op->mmu_op_ctx = NULL;

View File

@ -4,6 +4,7 @@
#define NV_DEVICE_V0_INFO 0x00
#define NV_DEVICE_V0_TIME 0x01
#define NV_DEVICE_V0_GCX_READY 0x02
struct nv_device_info_v0 {
__u8 version;
@ -55,6 +56,15 @@ struct nv_device_time_v0 {
__u64 time;
};
#define NV_DEVICE_GC6_READY BIT(0)
#define NV_DEVICE_GCOFF_READY BIT(1)
struct nv_device_gcx_ready_v0 {
__u8 version;
__u8 pad01[6];
__u8 ready;
};
#define NV_DEVICE_INFO_UNIT (0xffffffffULL << 32)
#define NV_DEVICE_INFO(n) ((n) | (0x00000000ULL << 32))
#define NV_DEVICE_HOST(n) ((n) | (0x00000001ULL << 32))

View File

@ -22,4 +22,5 @@ int nvif_device_ctor(struct nvif_client *, const char *name, struct nvif_device
void nvif_device_dtor(struct nvif_device *);
int nvif_device_map(struct nvif_device *);
u64 nvif_device_time(struct nvif_device *);
int nvif_device_gcx_ready(struct nvif_device *);
#endif

View File

@ -156,6 +156,10 @@ struct nvkm_gsp {
struct sg_table fbsr;
} sr;
struct {
bool use_raw_mode_comptagline_alloc;
} memsys;
struct {
struct nvkm_gsp_mem mem;
@ -495,6 +499,8 @@ nvkm_gsp_event_dtor(struct nvkm_gsp_event *event)
int nvkm_gsp_intr_stall(struct nvkm_gsp *, enum nvkm_subdev_type, int);
int nvkm_gsp_intr_nonstall(struct nvkm_gsp *, enum nvkm_subdev_type, int);
int nvkm_gsp_gcx_ready(struct nvkm_gsp *gsp);
int gv100_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);
int tu102_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);
int tu116_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);

View File

@ -578,8 +578,9 @@ int nouveau_bo_pin_locked(struct nouveau_bo *nvbo, uint32_t domain, bool contig)
"0x%08x vs 0x%08x\n", bo,
bo->resource->mem_type, domain);
ret = -EBUSY;
} else {
ttm_bo_pin(&nvbo->bo);
}
ttm_bo_pin(&nvbo->bo);
goto out;
}

View File

@ -600,8 +600,11 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
new_edid = drm_get_edid(connector, nv_encoder->i2c);
} else {
ret = nvif_outp_edid_get(&nv_encoder->outp, (u8 **)&new_edid);
if (ret < 0)
if (ret < 0) {
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return connector_status_disconnected;
}
}
nouveau_connector_set_edid(nv_connector, new_edid);

View File

@ -339,8 +339,8 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm, struct page **ppage,
chunk->pagemap.ops = &nouveau_dmem_pagemap_ops;
chunk->pagemap.owner = drm->dev;
ret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM, DMEM_CHUNK_SIZE,
&chunk->bo);
ret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM,
DMEM_CHUNK_SIZE * NR_CHUNKS, &chunk->bo);
if (ret)
goto out_release;

View File

@ -585,6 +585,7 @@ nouveau_drm_device_fini(struct nouveau_drm *drm)
if (nouveau_pmops_runtime()) {
pm_runtime_get_sync(dev->dev);
pm_runtime_forbid(dev->dev);
pm_runtime_dont_use_autosuspend(dev->dev);
}
nouveau_led_fini(dev);
@ -1148,6 +1149,7 @@ nouveau_pmops_runtime_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct nouveau_drm *drm = pci_get_drvdata(pdev);
struct nvif_device *nvif = &drm->client.device;
int ret;
if (!nouveau_pmops_runtime()) {
@ -1155,6 +1157,18 @@ nouveau_pmops_runtime_suspend(struct device *dev)
return -EBUSY;
}
// Check if the GPU itself is ready for runtime suspend, otherwise mark as busy and check
// again in a bit.
ret = nvif_device_gcx_ready(nvif);
if (ret < 0) {
NV_ERROR(drm, "Failed to query GCX readiness (returned %d)\n", ret);
return -EBUSY;
} else if (!(ret & NV_DEVICE_GCOFF_READY)) {
NV_DEBUG(drm, "GPU isn't ready for suspend yet, delaying...\n");
pm_runtime_mark_last_busy(dev);
return -EBUSY;
}
nouveau_switcheroo_optimus_dsm();
ret = nouveau_do_suspend(drm, true);
pci_save_state(pdev);
@ -1250,10 +1264,8 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
mutex_unlock(&drm->clients_lock);
done:
if (ret && cli) {
nouveau_cli_fini(cli);
if (ret && cli)
kfree(cli);
}
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);

View File

@ -522,6 +522,7 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
if (unlikely(ret)) {
if (ret != -ERESTARTSYS)
NV_PRINTK(err, cli, "fail reserve\n");
drm_gem_object_put(gem);
break;
}
}
@ -531,6 +532,7 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
struct nouveau_vma *vma = nouveau_vma_find(nvbo, vmm);
if (!vma) {
NV_PRINTK(err, cli, "vma not found!\n");
drm_gem_object_put(gem);
ret = -EINVAL;
break;
}

View File

@ -517,7 +517,7 @@ nouveau_sched_destroy(struct nouveau_sched **psched)
struct nouveau_sched *sched = *psched;
nouveau_sched_fini(sched);
kfree(sched);
kfree_rcu(sched, rcu);
*psched = NULL;
}

View File

@ -98,6 +98,7 @@ void nouveau_job_free(struct nouveau_job *job);
struct nouveau_sched {
struct drm_gpu_scheduler base;
struct rcu_head rcu;
struct drm_sched_entity entity;
struct workqueue_struct *wq;
struct mutex mutex;

View File

@ -846,6 +846,9 @@ op_map(struct nouveau_uvma *uvma)
{
struct nouveau_bo *nvbo = nouveau_gem_object(uvma->va.gem.obj);
if (drm_gpuva_invalidated(&uvma->va))
return;
nouveau_uvma_map(uvma, nouveau_mem(nvbo->bo.resource));
}
@ -1232,6 +1235,7 @@ bind_lock_validate(struct nouveau_job *job, struct drm_exec *exec,
drm_gpuva_for_each_op(va_op, op->ops) {
struct drm_gem_object *obj = op_gem_obj(va_op);
struct nouveau_bo *nvbo;
if (unlikely(!obj))
continue;
@ -1246,8 +1250,13 @@ bind_lock_validate(struct nouveau_job *job, struct drm_exec *exec,
if (va_op->op == DRM_GPUVA_OP_UNMAP)
continue;
ret = nouveau_bo_validate(nouveau_gem_object(obj),
true, false);
nvbo = nouveau_gem_object(obj);
if (!(nvbo->valid_domains &
(NOUVEAU_GEM_DOMAIN_VRAM | NOUVEAU_GEM_DOMAIN_GART)))
return -EINVAL;
nouveau_bo_placement_set(nvbo, nvbo->valid_domains, 0);
ret = nouveau_bo_validate(nvbo, true, false);
if (ret)
return ret;
}

View File

@ -38,6 +38,19 @@ nvif_device_time(struct nvif_device *device)
return device->user.func->time(&device->user);
}
int
nvif_device_gcx_ready(struct nvif_device *device)
{
struct nv_device_gcx_ready_v0 args = {};
int ret;
ret = nvif_object_mthd(&device->object, NV_DEVICE_V0_GCX_READY, &args, sizeof(args));
if (ret)
return ret;
return args.ready;
}
int
nvif_device_map(struct nvif_device *device)
{

View File

@ -192,6 +192,7 @@ void
nvif_vmm_dtor(struct nvif_vmm *vmm)
{
kfree(vmm->page);
vmm->page = NULL;
nvif_object_dtor(&vmm->object);
}

View File

@ -74,6 +74,7 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size)
const struct nvkm_domain *domain;
struct nvkm_pstate *pstate;
struct nvkm_cstate *cstate;
bool found = false;
int i = 0, j = -1;
u32 lo, hi;
int ret = -ENOSYS;
@ -104,10 +105,15 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size)
if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) {
list_for_each_entry(pstate, &clk->states, head) {
if (i++ == args->v0.state)
if (i++ == args->v0.state) {
found = true;
break;
}
}
if (!found)
return -EINVAL;
lo = pstate->base.domain[domain->name];
hi = lo;
list_for_each_entry(cstate, &pstate->list, head) {

View File

@ -27,6 +27,7 @@
#include <core/client.h>
#include <subdev/fb.h>
#include <subdev/gsp.h>
#include <subdev/instmem.h>
#include <subdev/timer.h>
@ -189,6 +190,38 @@ nvkm_udevice_time(struct nvkm_udevice *udev, void *data, u32 size)
return ret;
}
static int
nvkm_udevice_gcx_ready(struct nvkm_udevice *udev, void *data, u32 size)
{
struct nvkm_object *object = &udev->object;
struct nvkm_device *device = udev->device;
struct nvkm_gsp *gsp = device->gsp;
union {
struct nv_device_gcx_ready_v0 v0;
} *args = data;
int ret = -ENOSYS;
if (!gsp) {
args->v0.ready = NV_DEVICE_GC6_READY | NV_DEVICE_GCOFF_READY;
return 0;
}
nvif_ioctl(object, "device gcx ready size %d\n", size);
ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false);
if (!ret) {
nvif_ioctl(object, "device gcx ready vers %d\n", args->v0.version);
ret = nvkm_gsp_gcx_ready(gsp);
if (ret < 0)
return ret;
args->v0.ready = ret;
ret = 0;
}
return ret;
}
static int
nvkm_udevice_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
{
@ -199,6 +232,8 @@ nvkm_udevice_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
return nvkm_udevice_info(udev, data, size);
case NV_DEVICE_V0_TIME:
return nvkm_udevice_time(udev, data, size);
case NV_DEVICE_V0_GCX_READY:
return nvkm_udevice_gcx_ready(udev, data, size);
default:
break;
}

View File

@ -253,8 +253,7 @@ nvkm_uoutp_mthd_hdmi(struct nvkm_outp *outp, void *argv, u32 argc)
if (!ior->func->hdmi ||
args->v0.max_ac_packet > 0x1f ||
args->v0.rekey > 0x7f ||
(args->v0.scdc && !ior->func->hdmi->scdc))
args->v0.rekey > 0x7f)
return -EINVAL;
if (!args->v0.enable) {

View File

@ -199,16 +199,18 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
}
if (volt) {
ret = nvkm_volt_set_id(volt, cstate->voltage,
pstate->base.voltage, clk->temp, -1);
if (ret && ret != -ENODEV)
nvkm_error(subdev, "failed to lower voltage: %d\n", ret);
int err = nvkm_volt_set_id(volt, cstate->voltage,
pstate->base.voltage, clk->temp, -1);
if (err && err != -ENODEV)
nvkm_error(subdev, "failed to lower voltage: %d\n", err);
}
if (therm) {
ret = nvkm_therm_cstate(therm, pstate->fanspeed, -1);
if (ret && ret != -ENODEV)
nvkm_error(subdev, "failed to lower fan speed: %d\n", ret);
int err = nvkm_therm_cstate(therm, pstate->fanspeed, -1);
if (err && err != -ENODEV)
nvkm_error(subdev, "failed to lower fan speed: %d\n", err);
}
return ret;
@ -270,13 +272,19 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
struct nvkm_fb *fb = subdev->device->fb;
struct nvkm_pci *pci = subdev->device->pci;
struct nvkm_pstate *pstate;
bool found = false;
int ret, idx = 0;
list_for_each_entry(pstate, &clk->states, head) {
if (idx++ == pstatei)
if (idx++ == pstatei) {
found = true;
break;
}
}
if (!found)
return -EINVAL;
nvkm_debug(subdev, "setting performance state %d\n", pstatei);
clk->pstate = pstatei;
@ -473,6 +481,7 @@ static int
nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
{
struct nvkm_pstate *pstate;
bool found = false;
int i = 0;
if (!clk->allow_reclock)
@ -480,12 +489,14 @@ nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
if (req != -1 && req != -2) {
list_for_each_entry(pstate, &clk->states, head) {
if (pstate->pstate == req)
if (pstate->pstate == req) {
found = true;
break;
}
i++;
}
if (pstate->pstate != req)
if (!found)
return -EINVAL;
req = i;
}

View File

@ -51,6 +51,8 @@ nv1a_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
mib = ((mem >> 4) & 127) + 1;
}
pci_dev_put(bridge);
return nvkm_ram_new_(&nv04_ram_func, fb, NVKM_RAM_TYPE_STOLEN,
mib * 1024 * 1024, pram);
}

View File

@ -20,6 +20,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <nvif/cl0080.h>
int
nvkm_gsp_intr_nonstall(struct nvkm_gsp *gsp, enum nvkm_subdev_type type, int inst)
@ -47,6 +48,15 @@ nvkm_gsp_intr_stall(struct nvkm_gsp *gsp, enum nvkm_subdev_type type, int inst)
return -ENOENT;
}
int
nvkm_gsp_gcx_ready(struct nvkm_gsp *gsp)
{
if (!gsp->rm->api->gsp->gcx_ready)
return NV_DEVICE_GC6_READY | NV_DEVICE_GCOFF_READY;
return gsp->rm->api->gsp->gcx_ready(gsp);
}
static int
nvkm_gsp_fini(struct nvkm_subdev *subdev, enum nvkm_suspend_state suspend)
{

View File

@ -93,6 +93,7 @@ void r535_gsp_dtor(struct nvkm_gsp *);
int r535_gsp_oneinit(struct nvkm_gsp *);
int r535_gsp_init(struct nvkm_gsp *);
int r535_gsp_fini(struct nvkm_gsp *, enum nvkm_suspend_state suspend);
int r535_gsp_gcx_ready(struct nvkm_gsp *gsp);
int nvkm_gsp_new_(const struct nvkm_gsp_fwif *, struct nvkm_device *, enum nvkm_subdev_type, int,
struct nvkm_gsp **);

View File

@ -1782,6 +1782,23 @@ r535_gsp_fini(struct nvkm_gsp *gsp, enum nvkm_suspend_state suspend)
return 0;
}
int
r535_gsp_get_static_memsys_info(struct nvkm_gsp *gsp)
{
NV2080_CTRL_INTERNAL_MEMSYS_GET_STATIC_CONFIG_PARAMS *ctrl;
ctrl = nvkm_gsp_rm_ctrl_rd(&gsp->internal.device.subdevice,
NV2080_CTRL_CMD_INTERNAL_MEMSYS_GET_STATIC_CONFIG,
sizeof(*ctrl));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
gsp->memsys.use_raw_mode_comptagline_alloc = ctrl->bUseRawModeComptaglineAllocation;
nvkm_gsp_rm_ctrl_done(&gsp->internal.device.subdevice, ctrl);
return 0;
}
int
r535_gsp_init(struct nvkm_gsp *gsp)
{

View File

@ -782,6 +782,51 @@ typedef struct NV2080_CTRL_INTERNAL_INTR_GET_KERNEL_TABLE_PARAMS {
#define GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE ((48 << 10) * 2048) // Support 2048 channels
typedef struct NV2080_CTRL_INTERNAL_MEMSYS_GET_STATIC_CONFIG_PARAMS {
/*! Determines if RM should use 1 to 1 Comptagline allocation policy */
NvBool bOneToOneComptagLineAllocation;
/*! Determines if RM should use 1 to 4 Comptagline allocation policy */
NvBool bUseOneToFourComptagLineAllocation;
/*! Determines if RM should use raw Comptagline allocation policy */
NvBool bUseRawModeComptaglineAllocation;
/*! Has COMPBIT_BACKING_SIZE been overridden to zero (i.e. disabled)? */
NvBool bDisableCompbitBacking;
/*! Determine if we need to disable post L2 compression */
NvBool bDisablePostL2Compression;
/*! Is ECC DRAM feature supported? */
NvBool bEnabledEccFBPA;
NvBool bL2PreFill;
/*! L2 cache size */
NV_DECLARE_ALIGNED(NvU64 l2CacheSize, 8);
/*! Indicate whether fpba is present or not */
NvBool bFbpaPresent;
/*! Size covered by one comptag */
NvU32 comprPageSize;
/*! log32(comprPageSize) */
NvU32 comprPageShift;
/*! RAM type */
NvU32 ramType;
/*! LTC count */
NvU32 ltcCount;
/*! LTS per LTC count */
NvU32 ltsPerLtcCount;
} NV2080_CTRL_INTERNAL_MEMSYS_GET_STATIC_CONFIG_PARAMS;
#define NV2080_CTRL_CMD_INTERNAL_MEMSYS_GET_STATIC_CONFIG (0x20800a1c) /* finn: Evaluated from "(FINN_NV20_SUBDEVICE_0_INTERNAL_INTERFACE_ID << 8) | NV2080_CTRL_INTERNAL_MEMSYS_GET_STATIC_CONFIG_PARAMS_MESSAGE_ID" */
typedef union rpc_message_rpc_union_field_v03_00
{
NvU32 spare;

View File

@ -26,6 +26,52 @@ r570_fbsr_suspend_channels(struct nvkm_gsp *gsp, bool suspend)
return nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
}
static int
r570_fb_get_compbit_store_size(struct nvkm_gsp *gsp, u64 *size)
{
NV0080_CTRL_FB_GET_COMPBIT_STORE_INFO_PARAMS *ctrl;
ctrl = nvkm_gsp_rm_ctrl_rd(&gsp->internal.device.object,
NV0080_CTRL_CMD_FB_GET_COMPBIT_STORE_INFO,
sizeof(*ctrl));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
*size = ctrl->Size;
nvkm_gsp_rm_ctrl_done(&gsp->internal.device.object, ctrl);
return 0;
}
static int
r570_memsys_enable_raw_comp_mode(struct nvkm_gsp *gsp, bool enable)
{
NV2080_CTRL_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE_PARAMS *ctrl;
int ret;
ctrl = nvkm_gsp_rm_ctrl_get(&gsp->internal.device.subdevice,
NV2080_CTRL_CMD_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE,
sizeof(*ctrl));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
ctrl->bRawMode = enable;
ret = nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
if (!ret)
nvkm_debug(&gsp->subdev, "memsys: Raw compression mode %s\n",
str_enabled_disabled(enable));
return ret;
}
static bool
r570_need_raw_comp_war(struct nvkm_gsp *gsp, struct nvkm_device *device)
{
return (device->card_type == GA100 || device->card_type == AD100) &&
gsp->memsys.use_raw_mode_comptagline_alloc;
}
static void
r570_fbsr_resume(struct nvkm_gsp *gsp)
{
@ -33,6 +79,7 @@ r570_fbsr_resume(struct nvkm_gsp *gsp)
struct nvkm_instmem *imem = device->imem;
struct nvkm_instobj *iobj;
struct nvkm_vmm *vmm;
int ret;
/* Restore BAR2 page tables via BAR0 window, and re-enable BAR2. */
list_for_each_entry(iobj, &imem->boot, head) {
@ -54,6 +101,13 @@ r570_fbsr_resume(struct nvkm_gsp *gsp)
vmm = nvkm_bar_bar1_vmm(device);
vmm->func->flush(vmm, 0);
/* Re-enable raw mode if it was previously disabled */
if (r570_need_raw_comp_war(gsp, device)) {
ret = r570_memsys_enable_raw_comp_mode(gsp, true);
if (ret)
nvkm_error(&gsp->subdev, "Failed to re-enable raw comp mode\n");
}
/* Resume channel scheduling. */
r570_fbsr_suspend_channels(device->gsp, false);
@ -81,7 +135,7 @@ r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size)
ctrl->hClient = gsp->internal.client.object.handle;
ctrl->hSysMem = memlist.handle;
ctrl->sysmemAddrOfSuspendResumeData = gsp->sr.meta.addr;
ctrl->bEnteringGcoffState = 0;
ctrl->bEnteringGcoffState = 1;
ret = nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
if (ret)
@ -98,12 +152,29 @@ r570_fbsr_suspend(struct nvkm_gsp *gsp)
struct nvkm_device *device = subdev->device;
struct nvkm_instmem *imem = device->imem;
struct nvkm_instobj *iobj;
u64 size;
u64 size, compbit_store_size;
int ret;
/* Stop channel scheduling. */
r570_fbsr_suspend_channels(gsp, true);
/* Temporarily disable raw mode to prevent FBSR restore operations from corrupting
* compressed surfaces. Required for ampere and ada.
*
* Nvidia bug #3172217
*/
if (r570_need_raw_comp_war(gsp, device)) {
ret = r570_memsys_enable_raw_comp_mode(gsp, false);
if (ret)
return ret;
}
ret = r570_fb_get_compbit_store_size(gsp, &compbit_store_size);
if (ret < 0)
return ret;
nvkm_debug(&gsp->subdev, "fbsr: Compbit backing store size: 0x%llx bytes\n",
compbit_store_size);
/* Save BAR2 allocations to system memory. */
list_for_each_entry(iobj, &imem->list, head) {
if (iobj->preserve) {
@ -126,6 +197,8 @@ r570_fbsr_suspend(struct nvkm_gsp *gsp)
size = gsp->fb.heap.size;
size += gsp->fb.rsvd_size;
size += gsp->fb.bios.vga_workspace.size;
size += compbit_store_size;
nvkm_debug(subdev, "fbsr: size: 0x%llx bytes\n", size);
ret = nvkm_gsp_sg(device, size, &gsp->sr.fbsr);

View File

@ -10,6 +10,7 @@
#include "nvrm/gsp.h"
#include "nvrm/rpcfn.h"
#include "nvrm/msgfn.h"
#include "nvif/cl0080.h"
#include <core/pci.h>
#include <subdev/pci/priv.h>
@ -137,6 +138,14 @@ r570_gsp_get_static_info(struct nvkm_gsp *gsp)
}
}
ret = r535_gsp_get_static_memsys_info(gsp);
if (ret) {
nvkm_error(&gsp->subdev, "Retrieving static memsys info failed\n");
return ret;
}
nvkm_debug(&gsp->subdev, "memsys: Use raw mode for comptag allocations? %s\n",
str_yes_no(gsp->memsys.use_raw_mode_comptagline_alloc));
return 0;
}
@ -215,6 +224,32 @@ r570_gsp_set_rmargs(struct nvkm_gsp *gsp, bool resume)
args->bDmemStack = 1;
}
int
r570_gsp_gcx_ready(struct nvkm_gsp *gsp)
{
NV2080_CTRL_INTERNAL_GCX_ENTRY_PREREQUISITE_PARAMS *ctrl;
int ret = 0;
ctrl = nvkm_gsp_rm_ctrl_rd(&gsp->internal.device.subdevice,
NV2080_CTRL_CMD_INTERNAL_GCX_ENTRY_PREREQUISITE,
sizeof(*ctrl));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
if (ctrl->bIsGC6Satisfied)
ret |= NV_DEVICE_GC6_READY;
if (ctrl->bIsGCOFFSatisfied)
ret |= NV_DEVICE_GCOFF_READY;
nvkm_debug(&gsp->subdev,
"GCX ready status: GC6=%s GCOFF=%s\n",
str_yes_no(ctrl->bIsGC6Satisfied), str_yes_no(ctrl->bIsGCOFFSatisfied));
nvkm_gsp_rm_ctrl_done(&gsp->internal.device.subdevice, ctrl);
return ret;
}
const struct nvkm_rm_api_gsp
r570_gsp = {
.set_rmargs = r570_gsp_set_rmargs,
@ -223,4 +258,5 @@ r570_gsp = {
.xlat_mc_engine_idx = r570_gsp_xlat_mc_engine_idx,
.drop_post_nocat_record = r570_gsp_drop_post_nocat_record,
.sr_data_size = r570_gsp_sr_data_size,
.gcx_ready = r570_gsp_gcx_ready,
};

View File

@ -16,4 +16,33 @@ typedef struct NV2080_CTRL_INTERNAL_FBSR_INIT_PARAMS {
NV_DECLARE_ALIGNED(NvU64 sysmemAddrOfSuspendResumeData, 8);
} NV2080_CTRL_INTERNAL_FBSR_INIT_PARAMS;
#define NV0080_CTRL_CMD_FB_GET_COMPBIT_STORE_INFO (0x801306) /* finn: Evaluated from "(FINN_NV01_DEVICE_0_FB_INTERFACE_ID << 8) | NV0080_CTRL_FB_GET_COMPBIT_STORE_INFO_PARAMS_MESSAGE_ID" */
typedef struct NV0080_CTRL_FB_GET_COMPBIT_STORE_INFO_PARAMS {
NV_DECLARE_ALIGNED(NvU64 Size, 8);
NV_DECLARE_ALIGNED(NvU64 Address, 8);
NvU32 AddressSpace;
NvU32 MaxCompbitLine;
NvU32 comptagsPerCacheLine;
NvU32 cacheLineSize;
NvU32 cacheLineSizePerSlice;
NvU32 cacheLineFetchAlignment;
NV_DECLARE_ALIGNED(NvU64 backingStoreBase, 8);
NvU32 gobsPerComptagPerSlice;
NvU32 backingStoreCbcBase;
NvU32 comptaglineAllocationPolicy;
NV_DECLARE_ALIGNED(NvU64 privRegionStartOffset, 8);
NvU32 cbcCoveragePerSlice;
} NV0080_CTRL_FB_GET_COMPBIT_STORE_INFO_PARAMS;
#define NV0080_CTRL_CMD_FB_GET_COMPBIT_STORE_INFO_ADDRESS_SPACE_UNKNOWN 0 // ADDR_UNKNOWN
#define NV0080_CTRL_CMD_FB_GET_COMPBIT_STORE_INFO_ADDRESS_SPACE_SYSMEM 1 // ADDR_SYSMEM
#define NV0080_CTRL_CMD_FB_GET_COMPBIT_STORE_INFO_ADDRESS_SPACE_FBMEM 2 // ADDR_FBMEM
#define NV2080_CTRL_CMD_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE (0x20800a6f) /* finn: Evaluated from "(FINN_NV20_SUBDEVICE_0_INTERNAL_INTERFACE_ID << 8) | NV2080_CTRL_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE_PARAMS_MESSAGE_ID" */
typedef struct NV2080_CTRL_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE_PARAMS {
NvBool bRawMode;
} NV2080_CTRL_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE_PARAMS;
#endif

View File

@ -639,4 +639,11 @@ typedef struct GSP_FMC_BOOT_PARAMS
} GSP_FMC_BOOT_PARAMS;
#define GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100 (14 << 20) // Hopper+
#define NV2080_CTRL_CMD_INTERNAL_GCX_ENTRY_PREREQUISITE (0x2080a7d7)
typedef struct NV2080_CTRL_INTERNAL_GCX_ENTRY_PREREQUISITE_PARAMS {
NvBool bIsGC6Satisfied;
NvBool bIsGCOFFSatisfied;
} NV2080_CTRL_INTERNAL_GCX_ENTRY_PREREQUISITE_PARAMS;
#endif

View File

@ -40,6 +40,7 @@ struct nvkm_rm_api {
void (*drop_send_user_shared_data)(struct nvkm_gsp *);
void (*drop_post_nocat_record)(struct nvkm_gsp *);
u32 (*sr_data_size)(struct nvkm_gsp *);
int (*gcx_ready)(struct nvkm_gsp *gsp);
} *gsp;
const struct nvkm_rm_api_rpc {
@ -174,6 +175,8 @@ int r535_gr_chan_new(struct nvkm_gr *, struct nvkm_chan *, const struct nvkm_ocl
int r535_gr_promote_ctx(struct r535_gr *, bool golden, struct nvkm_vmm *,
struct nvkm_memory **pctxbuf_mem, struct nvkm_vma **pctxbuf_vma,
struct nvkm_gsp_object *chan);
int r570_gsp_gcx_ready(struct nvkm_gsp *gsp);
int r535_gsp_get_static_memsys_info(struct nvkm_gsp *gsp);
extern const struct nvkm_rm_api_engine r535_nvdec;
extern const struct nvkm_rm_api_engine r535_nvenc;
extern const struct nvkm_rm_api_engine r535_nvjpg;

View File

@ -114,6 +114,8 @@ struct virtio_gpu_object {
bool dumb;
bool created;
bool attached;
/* a guest-bound transfer is queued and its mapping not yet synced */
bool from_host_pending;
bool host3d_blob, guest_blob;
uint32_t blob_mem, blob_flags;
@ -196,6 +198,9 @@ struct virtio_gpu_vbuffer {
struct list_head list;
uint32_t seqno;
/* guest-bound transfer whose shmem backing needs a CPU sync */
bool sync_for_cpu;
};
struct virtio_gpu_output {

View File

@ -45,7 +45,7 @@ static int virtio_gpu_gem_create(struct drm_file *file,
ret = drm_gem_handle_create(file, &obj->base.base, &handle);
if (ret) {
drm_gem_object_release(&obj->base.base);
drm_gem_object_put(&obj->base.base);
return ret;
}

View File

@ -185,7 +185,7 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data,
ret = drm_gem_handle_create(file, obj, &handle);
if (ret) {
drm_gem_object_release(obj);
drm_gem_object_put(obj);
return ret;
}
@ -261,6 +261,27 @@ static int virtio_gpu_transfer_from_host_ioctl(struct drm_device *dev,
if (ret != 0)
goto err_put_free;
if (virtio_gpu_is_shmem(bo) && virtio_gpu_use_dma_api(vgdev->vdev)) {
/*
* The sync on completion restores the whole mapping, so an
* earlier transfer has to be done before this one snapshots it.
* Otherwise the snapshot predates anything the CPU wrote once
* that transfer's fence signalled, and the later sync would
* discard it. Nothing can add a fence behind our back here,
* since doing so takes the reservation we already hold.
* This writes the pages, so it waits as a writer does. READ
* usage covers existing readers.
*/
long wait = dma_resv_wait_timeout(objs->objs[0]->resv,
DMA_RESV_USAGE_READ, true,
MAX_SCHEDULE_TIMEOUT);
if (wait < 0) {
ret = wait;
goto err_unlock;
}
}
fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context, 0);
if (!fence) {
ret = -ENOMEM;
@ -320,6 +341,28 @@ static int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data,
if (ret != 0)
goto err_put_free;
/*
* A transfer the other way may have queued without yet syncing
* its mapping. Pushing the guest pages into it now would
* discard what the device wrote there, so wait for that sync:
* it runs before the fence it belongs to is signalled. The
* flag is only set under this reservation, so it cannot appear
* behind our back, and the acquire pairs with the release in
* that sync, so finding it clear means the pages it wrote are
* visible here too.
*/
if (smp_load_acquire(&bo->from_host_pending)) {
long wait = dma_resv_wait_timeout(objs->objs[0]->resv,
DMA_RESV_USAGE_WRITE,
true,
MAX_SCHEDULE_TIMEOUT);
if (wait < 0) {
ret = wait;
goto err_unlock;
}
}
ret = -ENOMEM;
fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context,
0);
@ -557,14 +600,14 @@ static int virtio_gpu_resource_create_blob_ioctl(struct drm_device *dev,
if (params.blob_flags & VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE) {
ret = virtio_gpu_resource_assign_uuid(vgdev, bo);
if (ret) {
drm_gem_object_release(obj);
drm_gem_object_put(obj);
return ret;
}
}
ret = drm_gem_handle_create(file, obj, &handle);
if (ret) {
drm_gem_object_release(obj);
drm_gem_object_put(obj);
return ret;
}

View File

@ -589,6 +589,7 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev,
struct drm_plane *plane;
const uint32_t *formats;
int nformats;
int ret;
if (type == DRM_PLANE_TYPE_CURSOR) {
formats = virtio_gpu_cursor_formats;
@ -614,5 +615,17 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev,
drm_plane_create_blend_mode_property(plane,
BIT(DRM_MODE_BLEND_PREMULTI));
if (type == DRM_PLANE_TYPE_CURSOR) {
/*
* The cursor plane exposes a format with an alpha channel,
* which requires a blend mode property. The host blends
* premultiplied alpha, matching the property's default.
*/
ret = drm_plane_create_blend_mode_property(plane,
BIT(DRM_MODE_BLEND_PREMULTI));
if (ret)
return ERR_PTR(ret);
}
return plane;
}

View File

@ -349,7 +349,7 @@ struct drm_gem_object *virtgpu_gem_prime_import(struct drm_device *dev,
}
}
if (!vgdev->has_resource_blob)
if (!vgdev->has_resource_blob || vgdev->has_virgl_3d)
return drm_gem_prime_import(dev, buf);
bo = kzalloc_obj(*bo);

View File

@ -389,10 +389,13 @@ static int virtio_gpu_init_submit(struct virtio_gpu_submit *submit,
if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) ||
exbuf->num_out_syncobjs ||
exbuf->num_bo_handles ||
drm_fence_event)
drm_fence_event) {
out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx);
else
if (!out_fence)
return -ENOMEM;
} else {
out_fence = NULL;
}
if (drm_fence_event) {
err = virtio_gpu_fence_event_create(dev, file, out_fence, ring_idx);
@ -538,6 +541,10 @@ int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
virtio_gpu_process_post_deps(&submit);
virtio_gpu_complete_submit(&submit);
cleanup:
if (ret && submit.out_fence && submit.out_fence->e) {
drm_event_cancel_free(dev, &submit.out_fence->e->base);
submit.out_fence->e = NULL;
}
virtio_gpu_cleanup_submit(&submit);
return ret;

View File

@ -256,6 +256,33 @@ void virtio_gpu_dequeue_ctrl_func(struct work_struct *work)
} while (!virtqueue_enable_cb(vgdev->ctrlq.vq));
spin_unlock(&vgdev->ctrlq.qlock);
/*
* Sync guest-bound transfers before signalling anything, so that a
* waiter cannot read the backing pages while what the device wrote is
* still in a bounce buffer. This cannot be folded into the loop below:
* virtio_gpu_fence_event_process() also signals every earlier fence in
* the same context, so any entry there may signal this entry's fence.
*/
list_for_each_entry(entry, &reclaim_list, list) {
if (entry->sync_for_cpu) {
struct virtio_gpu_object *bo =
gem_to_virtio_gpu_obj(entry->objs->objs[0]);
dma_sync_sgtable_for_cpu(vgdev->vdev->dev.parent,
bo->base.sgt, DMA_FROM_DEVICE);
/*
* Release, so a transfer the other way that skips its
* wait on the strength of this cannot go on to read
* the backing pages before the sync above is visible.
* Nothing orders the two otherwise: where the mapping
* bounces on a coherent device the sync is a plain
* copy, and dma_direct_sync_sg_for_cpu() emits its
* barrier only for the non-coherent case.
*/
smp_store_release(&bo->from_host_pending, false);
}
}
list_for_each_entry(entry, &reclaim_list, list) {
resp = (struct virtio_gpu_ctrl_hdr *)entry->resp_buf;
@ -1278,12 +1305,31 @@ void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
struct virtio_gpu_transfer_host_3d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
vbuf->objs = objs;
if (virtio_gpu_is_shmem(bo) && use_dma_api) {
/*
* The device writes only the requested box, so prime the
* mapping with the current contents: otherwise the sync on
* completion would hand back whatever a bounce buffer held for
* the regions the device does not touch.
*/
dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
bo->base.sgt, DMA_TO_DEVICE);
vbuf->sync_for_cpu = true;
/*
* Set under the reservation the caller holds, so a transfer
* the other way cannot miss it and push the guest pages into
* the mapping while the device still owns it.
*/
WRITE_ONCE(bo->from_host_pending, true);
}
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D);
cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
cmd_p->resource_id = cpu_to_le32(bo->hw_res_handle);

View File

@ -215,16 +215,12 @@ int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev,
/* Create fake offset */
ret = drm_gem_create_mmap_offset(obj);
if (ret) {
kfree(vram);
return ret;
}
if (ret)
goto err_release_obj;
ret = virtio_gpu_resource_id_get(vgdev, &vram->base.hw_res_handle);
if (ret) {
kfree(vram);
return ret;
}
if (ret)
goto err_release_obj;
virtio_gpu_cmd_resource_create_blob(vgdev, &vram->base, params, NULL,
0);
@ -240,6 +236,11 @@ int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev,
*bo_ptr = &vram->base;
return 0;
err_release_obj:
drm_gem_object_release(obj);
kfree(vram);
return ret;
}
void virtio_gpu_vram_map_deferred(struct virtio_gpu_object_vram *vram)

View File

@ -651,6 +651,7 @@
#define MEM_THERMAL_MASK REG_BIT(2)
#define VR_THERMAL_MASK REG_BIT(3)
#define ICCMAX_MASK REG_BIT(4)
#define PWRBRK_MASK REG_BIT(5)
#define SOC_AVG_THERMAL_MASK REG_BIT(6)
#define FASTVMODE_MASK REG_BIT(7)
#define PSYS_PL1_MASK REG_BIT(12)

View File

@ -1037,6 +1037,13 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
} else {
drm_dbg(&xe->drm, "Evict system allocator BO failed=%pe\n",
ERR_PTR(ret));
/*
* The semantic we want upon SVM eviction failure
* because of racing access is keep walking for
* eviction, which is -ENOSPC.
*/
if (ret == -EBUSY)
ret = -ENOSPC;
}
goto out;

View File

@ -9,6 +9,8 @@
#include <drm/drm_prime.h>
#include <drm/ttm/ttm_tt.h>
#include <xen/xen.h>
#include "xe_bo_types.h"
#include "xe_ggtt.h"
#include "xe_macros.h"
@ -575,6 +577,23 @@ static inline unsigned int xe_sg_segment_size(struct device *dev)
struct scatterlist __maybe_unused sg;
size_t max = BIT_ULL(sizeof(sg.length) * 8) - 1;
/*
* For Xen PV guests pages aren't contiguous in DMA (machine) address
* space. The DMA API takes care of that both in dma_alloc_* (by
* calling into the hypervisor to make the pages contiguous) and in
* dma_map_* (by bounce buffering). But xe (like i915, see commit
* 78a07fe777c4) ignores the coherency aspects of the DMA API and thus
* can't cope with bounce buffering actually happening, so add a hack
* here to force small allocations and mappings when running in PV
* mode on Xen.
*
* Note this will still break if bounce buffering is required for other
* reasons, like confidential computing hypervisors or PCIe root ports
* with addressing limitations.
*/
if (xen_pv_domain())
return PAGE_SIZE;
max = min_t(size_t, max, dma_max_mapping_size(dev));
/*

View File

@ -39,7 +39,7 @@
* - ``reason_mem_thermal``: Memory thermal
* - ``reason_vr_thermal``: VR thermal
* - ``reason_iccmax``: ICCMAX
* - ``reason_ratl``: RATL thermal algorithm
* - ``reason_pwrbrk``: Power brake
* - ``reason_soc_avg_thermal``: SoC average temp
* - ``reason_fastvmode``: VR is hitting FastVMode
* - ``reason_psys_pl1``: PSYS PL1
@ -200,6 +200,7 @@ static THROTTLE_ATTR_RO(reason_psys_pl1, PSYS_PL1_MASK);
static THROTTLE_ATTR_RO(reason_psys_pl2, PSYS_PL2_MASK);
static THROTTLE_ATTR_RO(reason_p0_freq, P0_FREQ_MASK);
static THROTTLE_ATTR_RO(reason_psys_crit, PSYS_CRIT_MASK);
static THROTTLE_ATTR_RO(reason_pwrbrk, PWRBRK_MASK);
static struct attribute *cri_throttle_attrs[] = {
/* Common */
@ -209,12 +210,12 @@ static struct attribute *cri_throttle_attrs[] = {
&attr_reason_pl2.attr.attr,
&attr_reason_pl4.attr.attr,
&attr_reason_prochot.attr.attr,
&attr_reason_ratl.attr.attr,
/* CRI */
&attr_reason_vr_thermal.attr.attr,
&attr_reason_soc_thermal.attr.attr,
&attr_reason_mem_thermal.attr.attr,
&attr_reason_iccmax.attr.attr,
&attr_reason_pwrbrk.attr.attr,
&attr_reason_soc_avg_thermal.attr.attr,
&attr_reason_fastvmode.attr.attr,
&attr_reason_psys_pl1.attr.attr,

View File

@ -864,7 +864,7 @@ static unsigned int guc_mmio_regset_write(struct xe_guc_ads *ads,
}
}
if (XE_GT_WA(hwe->gt, 16023105232))
if (XE_GT_WA(hwe->gt, 16023105232) || XE_GT_WA(hwe->gt, 14025941587))
guc_mmio_regset_write_one(ads, regset_map,
RING_IDLEDLY(hwe->mmio_base),
count++);

View File

@ -585,28 +585,102 @@ static void hw_engine_init_early(struct xe_gt *gt, struct xe_hw_engine *hwe,
xe_reg_whitelist_process_engine(hwe);
}
static u32 idledly_floor_ticks(u32 idledly_ns, u32 idledly_units_ps)
{
return DIV_ROUND_DOWN_ULL((u64)idledly_ns * 1000, idledly_units_ps);
}
static void adjust_idledly(struct xe_hw_engine *hwe)
{
struct xe_gt *gt = hwe->gt;
u32 idledly, maxcnt;
u32 idledly, idledly_hw, idledly_reg_val, maxcnt;
u32 idledly_units_ps = 8 * gt->info.timestamp_base;
u32 maxcnt_units_ns = 640;
bool inhibit_switch = 0;
bool inhibit_switch = false;
bool wa_applied = false;
bool clamped_below_maxcnt = false;
if (!IS_SRIOV_VF(gt_to_xe(hwe->gt)) && XE_GT_WA(gt, 16023105232)) {
idledly = xe_mmio_read32(&gt->mmio, RING_IDLEDLY(hwe->mmio_base));
if ((!IS_SRIOV_VF(gt_to_xe(gt)) && XE_GT_WA(gt, 16023105232)) ||
XE_GT_WA(gt, 14025941587)) {
u32 mincnt_idledly_ns = 5000;
/* xe_gt_clock_init() warns and zeroes timestamp_base on unknown crystal clock. */
if (!idledly_units_ps)
return;
idledly_reg_val = xe_mmio_read32(&gt->mmio, RING_IDLEDLY(hwe->mmio_base));
maxcnt = xe_mmio_read32(&gt->mmio, RING_PWRCTX_MAXCNT(hwe->mmio_base));
inhibit_switch = idledly & INHIBIT_SWITCH_UNTIL_PREEMPTED;
idledly = REG_FIELD_GET(IDLE_DELAY, idledly);
idledly = DIV_ROUND_CLOSEST(idledly * idledly_units_ps, 1000);
inhibit_switch = idledly_reg_val & INHIBIT_SWITCH_UNTIL_PREEMPTED;
idledly = REG_FIELD_GET(IDLE_DELAY, idledly_reg_val);
idledly = DIV_ROUND_CLOSEST_ULL((u64)idledly * idledly_units_ps, 1000);
idledly_hw = idledly;
maxcnt = REG_FIELD_GET(IDLE_WAIT_TIME, maxcnt);
maxcnt *= maxcnt_units_ns;
if (xe_gt_WARN_ON(gt, idledly >= maxcnt || inhibit_switch)) {
idledly = DIV_ROUND_CLOSEST(((maxcnt - 1) * 1000),
idledly_units_ps);
xe_mmio_write32(&gt->mmio, RING_IDLEDLY(hwe->mmio_base), idledly);
/*
* Wa_14025941587 is applied before Wa_16023105232, which takes
* priority if the two ever conflict (not expected in practice).
*/
if (XE_GT_WA(gt, 14025941587) &&
idledly < mincnt_idledly_ns) {
idledly = mincnt_idledly_ns;
wa_applied = true;
}
if (XE_GT_WA(gt, 16023105232)) {
/* Clear the inhibit switch without disturbing a valid delay. */
if (inhibit_switch) {
idledly_reg_val &= ~INHIBIT_SWITCH_UNTIL_PREEMPTED;
wa_applied = true;
}
/* Warn only on the value read from hardware. */
xe_gt_WARN_ON(gt, idledly_hw >= maxcnt);
if (idledly >= maxcnt) {
/* maxcnt may be 0 if IDLE_WAIT_TIME is unprogrammed. */
idledly = maxcnt ? maxcnt - 1 : 0;
clamped_below_maxcnt = true;
wa_applied = true;
}
}
if (wa_applied) {
u32 idledly_ticks;
/*
* Wa_16023105232 requires idledly < maxcnt, so floor
* that clamp; otherwise round up to guarantee the
* Wa_14025941587 minimum survives tick quantization.
*/
if (clamped_below_maxcnt)
idledly_ticks = idledly_floor_ticks(idledly, idledly_units_ps);
else
idledly_ticks = DIV_ROUND_UP_ULL((u64)idledly * 1000,
idledly_units_ps);
/*
* Tick quantization can still push the rounded-up value
* to/above maxcnt; re-floor here so Wa_16023105232 keeps
* priority even in that case.
*/
if (!clamped_below_maxcnt && XE_GT_WA(gt, 16023105232) &&
(u64)idledly_ticks * idledly_units_ps >= (u64)maxcnt * 1000) {
xe_gt_dbg(gt, "idledly %s: %u ticks would exceed maxcnt=%u, so flooring\n",
hwe->name, idledly_ticks, maxcnt);
idledly = maxcnt ? maxcnt - 1 : 0;
idledly_ticks = idledly_floor_ticks(idledly, idledly_units_ps);
}
idledly_reg_val &= ~IDLE_DELAY;
idledly_reg_val |= REG_FIELD_PREP(IDLE_DELAY, idledly_ticks);
xe_gt_dbg(gt, "idledly %s: set %u max=%u inh=%u ts=%u\n",
hwe->name, idledly, maxcnt,
!!inhibit_switch, gt->info.timestamp_base);
xe_mmio_write32(&gt->mmio,
RING_IDLEDLY(hwe->mmio_base),
idledly_reg_val);
}
}
}

View File

@ -280,7 +280,8 @@ static void xe_tlb_inval_fence_prep(struct xe_tlb_inval_fence *fence)
xe_tlb_inval_fence_signal_unlocked((__fence)); \
mutex_unlock(&(__tlb_inval)->seqno_lock); \
\
__ret == -ECANCELED ? 0 : __ret; \
/* Undelivered: fence already signalled, report done */ \
(__ret == -ECANCELED || __ret == -ENOTRECOVERABLE) ? 0 : __ret; \
})
/**

View File

@ -1947,21 +1947,13 @@ void xe_vm_close_and_put(struct xe_vm *vm)
vma->gpuva.flags |= XE_VMA_DESTROYED;
}
/*
* All vm operations will add shared fences to resv.
* The only exception is eviction for a shared object,
* but even so, the unbind when evicted would still
* install a fence to resv. Hence it's safe to
* destroy the pagetables immediately.
*/
xe_vm_free_scratch(vm);
xe_vm_pt_destroy(vm);
xe_vm_unlock(vm);
/*
* VM is now dead, cannot re-add nodes to vm->vmas if it's NULL
* Since we hold a refcount to the bo, we can remove and free
* the members safely without locking.
* Unlink and destroy all contested external-BO VMAs before destroying
* the page tables. Otherwise, concurrent eviction holding only bo->resv
* can walk the BO's VMAs and attempt to invalidate/zap page tables that
* have already been freed.
*/
list_for_each_entry_safe(vma, next_vma, &contested,
combined_links.destroy) {
@ -1969,6 +1961,11 @@ void xe_vm_close_and_put(struct xe_vm *vm)
xe_vma_destroy_unlocked(vma);
}
xe_vm_lock(vm, false);
xe_vm_free_scratch(vm);
xe_vm_pt_destroy(vm);
xe_vm_unlock(vm);
xe_svm_fini(vm);
up_write(&vm->lock);

View File

@ -72,3 +72,5 @@
16029897822 MEDIA_VERSION(3500)
GRAPHICS_VERSION(3510)
14027054324 GRAPHICS_VERSION(3511)
14025941587 GRAPHICS_VERSION_RANGE(2001, 3511), FUNC(xe_rtp_match_not_sriov_vf)
MEDIA_VERSION_RANGE(1301, 3503), FUNC(xe_rtp_match_not_sriov_vf)