summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/xe
diff options
context:
space:
mode:
authorThomas Zimmermann <tzimmermann@suse.de>2026-02-05 10:33:06 +0100
committerThomas Zimmermann <tzimmermann@suse.de>2026-02-05 10:33:06 +0100
commit2bebc88d5e37ddcb5ea5039a39f39527662b27f0 (patch)
treecd7213334fe604969486b693e44750fe19ecd925 /drivers/gpu/drm/xe
parent96f30ee0fb9db1663eb8fd55c12e4c67da8c4a90 (diff)
parent3cc9398a9ea69b77de01b370463b706c354e52fb (diff)
downloadlinux-next-2bebc88d5e37ddcb5ea5039a39f39527662b27f0.tar.gz
linux-next-2bebc88d5e37ddcb5ea5039a39f39527662b27f0.zip
Merge drm/drm-next into drm-misc-next
Backmerging to get bug fixes from v6.19-rc7. Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Diffstat (limited to 'drivers/gpu/drm/xe')
-rw-r--r--drivers/gpu/drm/xe/Kconfig5
-rw-r--r--drivers/gpu/drm/xe/Makefile32
-rw-r--r--drivers/gpu/drm/xe/abi/guc_actions_abi.h6
-rw-r--r--drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h67
-rw-r--r--drivers/gpu/drm/xe/abi/guc_klvs_abi.h73
-rw-r--r--drivers/gpu/drm/xe/abi/guc_lfd_abi.h170
-rw-r--r--drivers/gpu/drm/xe/abi/guc_lic_abi.h77
-rw-r--r--drivers/gpu/drm/xe/abi/guc_log_abi.h42
-rw-r--r--drivers/gpu/drm/xe/abi/guc_scheduler_abi.h57
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h13
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h40
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h11
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_active.h22
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_active_types.h13
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_config.h5
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h37
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_irq.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h13
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_vgpu.h18
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h4
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h11
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_wakeref.h10
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/pxp/intel_pxp.h29
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/soc/intel_dram.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/soc/intel_gmch.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/soc/intel_rom.h6
-rw-r--r--drivers/gpu/drm/xe/display/ext/i915_irq.c85
-rw-r--r--drivers/gpu/drm/xe/display/intel_bo.c8
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.c30
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_misc.c16
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_rpm.c3
-rw-r--r--drivers/gpu/drm/xe/display/xe_dsb_buffer.c49
-rw-r--r--drivers/gpu/drm/xe/display/xe_fb_pin.c113
-rw-r--r--drivers/gpu/drm/xe/display/xe_hdcp_gsc.c58
-rw-r--r--drivers/gpu/drm/xe/display/xe_hdcp_gsc.h9
-rw-r--r--drivers/gpu/drm/xe/display/xe_initial_plane.c189
-rw-r--r--drivers/gpu/drm/xe/display/xe_initial_plane.h9
-rw-r--r--drivers/gpu/drm/xe/display/xe_panic.c16
-rw-r--r--drivers/gpu/drm/xe/display/xe_panic.h9
-rw-r--r--drivers/gpu/drm/xe/display/xe_plane_initial.c319
-rw-r--r--drivers/gpu/drm/xe/display/xe_stolen.c64
-rw-r--r--drivers/gpu/drm/xe/display/xe_stolen.h9
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_gpu_commands.h1
-rw-r--r--drivers/gpu/drm/xe/regs/xe_gt_regs.h9
-rw-r--r--drivers/gpu/drm/xe/regs/xe_gtt_defs.h1
-rw-r--r--drivers/gpu/drm/xe/regs/xe_guc_regs.h3
-rw-r--r--drivers/gpu/drm/xe/regs/xe_irq_regs.h1
-rw-r--r--drivers/gpu/drm/xe/regs/xe_mert_regs.h23
-rw-r--r--drivers/gpu/drm/xe/regs/xe_oa_regs.h17
-rw-r--r--drivers/gpu/drm/xe/regs/xe_pcode_regs.h3
-rw-r--r--drivers/gpu/drm/xe/regs/xe_pmt.h3
-rw-r--r--drivers/gpu/drm/xe/regs/xe_soc_remapper_regs.h14
-rw-r--r--drivers/gpu/drm/xe/tests/xe_args_test.c54
-rw-r--r--drivers/gpu/drm/xe/tests/xe_bo.c11
-rw-r--r--drivers/gpu/drm/xe/tests/xe_dma_buf.c3
-rw-r--r--drivers/gpu/drm/xe/tests/xe_guc_buf_kunit.c2
-rw-r--r--drivers/gpu/drm/xe/tests/xe_migrate.c10
-rw-r--r--drivers/gpu/drm/xe/tests/xe_mocs.c27
-rw-r--r--drivers/gpu/drm/xe/xe_args.h27
-rw-r--r--drivers/gpu/drm/xe/xe_bb.c3
-rw-r--r--drivers/gpu/drm/xe/xe_bo.c64
-rw-r--r--drivers/gpu/drm/xe/xe_bo.h9
-rw-r--r--drivers/gpu/drm/xe/xe_debugfs.c216
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump.c33
-rw-r--r--drivers/gpu/drm/xe/xe_device.c255
-rw-r--r--drivers/gpu/drm/xe/xe_device.h9
-rw-r--r--drivers/gpu/drm/xe/xe_device_sysfs.c33
-rw-r--r--drivers/gpu/drm/xe/xe_device_types.h80
-rw-r--r--drivers/gpu/drm/xe/xe_dma_buf.c2
-rw-r--r--drivers/gpu/drm/xe/xe_drm_client.c67
-rw-r--r--drivers/gpu/drm/xe/xe_eu_stall.c2
-rw-r--r--drivers/gpu/drm/xe/xe_exec.c13
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.c454
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.h67
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue_types.h64
-rw-r--r--drivers/gpu/drm/xe/xe_execlist.c5
-rw-r--r--drivers/gpu/drm/xe/xe_force_wake.c7
-rw-r--r--drivers/gpu/drm/xe/xe_force_wake.h40
-rw-r--r--drivers/gpu/drm/xe/xe_gen_wa_oob.c3
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.c222
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.h14
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt_types.h32
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.c9
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.h37
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_gsc.c34
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_debugfs.c8
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_proxy.c19
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_submit.c2
-rw-r--r--drivers/gpu/drm/xe/xe_gt.c213
-rw-r--r--drivers/gpu/drm/xe/xe_gt.h14
-rw-r--r--drivers/gpu/drm/xe/xe_gt_ccs_mode.c8
-rw-r--r--drivers/gpu/drm/xe/xe_gt_ccs_mode.h2
-rw-r--r--drivers/gpu/drm/xe/xe_gt_clock.c6
-rw-r--r--drivers/gpu/drm/xe/xe_gt_debugfs.c31
-rw-r--r--drivers/gpu/drm/xe/xe_gt_freq.c31
-rw-r--r--drivers/gpu/drm/xe/xe_gt_idle.c50
-rw-r--r--drivers/gpu/drm/xe/xe_gt_mcr.c1
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf.c20
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf.h8
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c319
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h10
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h5
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c317
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c4
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c330
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h10
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h39
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c5
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.c240
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.h1
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c12
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h19
-rw-r--r--drivers/gpu/drm/xe/xe_gt_stats.c17
-rw-r--r--drivers/gpu/drm/xe/xe_gt_stats.h32
-rw-r--r--drivers/gpu/drm/xe/xe_gt_stats_types.h10
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sysfs.c2
-rw-r--r--drivers/gpu/drm/xe/xe_gt_throttle.c13
-rw-r--r--drivers/gpu/drm/xe/xe_gt_types.h5
-rw-r--r--drivers/gpu/drm/xe/xe_guc.c82
-rw-r--r--drivers/gpu/drm/xe/xe_guc.h23
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.c24
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.h5
-rw-r--r--drivers/gpu/drm/xe/xe_guc_buf.c3
-rw-r--r--drivers/gpu/drm/xe/xe_guc_capture.c30
-rw-r--r--drivers/gpu/drm/xe/xe_guc_capture.h2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.c294
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.h8
-rw-r--r--drivers/gpu/drm/xe/xe_guc_debugfs.c20
-rw-r--r--drivers/gpu/drm/xe/xe_guc_exec_queue_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_fwif.h60
-rw-r--r--drivers/gpu/drm/xe/xe_guc_hwconfig.c4
-rw-r--r--drivers/gpu/drm/xe/xe_guc_klv_helpers.c9
-rw-r--r--drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h6
-rw-r--r--drivers/gpu/drm/xe/xe_guc_log.c509
-rw-r--r--drivers/gpu/drm/xe/xe_guc_log.h30
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc.c98
-rw-r--r--drivers/gpu/drm/xe/xe_guc_relay.c3
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.c1100
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.h11
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit_types.h22
-rw-r--r--drivers/gpu/drm/xe/xe_guc_tlb_inval.c44
-rw-r--r--drivers/gpu/drm/xe/xe_heci_gsc.c5
-rw-r--r--drivers/gpu/drm/xe/xe_huc.c8
-rw-r--r--drivers/gpu/drm/xe/xe_huc_debugfs.c8
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine.c1
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c18
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_group.c81
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_group.h4
-rw-r--r--drivers/gpu/drm/xe/xe_hw_fence.c20
-rw-r--r--drivers/gpu/drm/xe/xe_hw_fence.h2
-rw-r--r--drivers/gpu/drm/xe/xe_hwmon.c331
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.c15
-rw-r--r--drivers/gpu/drm/xe/xe_irq.c3
-rw-r--r--drivers/gpu/drm/xe/xe_late_bind_fw_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_lmtt.c25
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.c118
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.h10
-rw-r--r--drivers/gpu/drm/xe/xe_lrc_types.h3
-rw-r--r--drivers/gpu/drm/xe/xe_memirq.c2
-rw-r--r--drivers/gpu/drm/xe/xe_mert.c118
-rw-r--r--drivers/gpu/drm/xe/xe_mert.h35
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.c93
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.h9
-rw-r--r--drivers/gpu/drm/xe/xe_mmio.c4
-rw-r--r--drivers/gpu/drm/xe/xe_mocs.c20
-rw-r--r--drivers/gpu/drm/xe/xe_module.c10
-rw-r--r--drivers/gpu/drm/xe/xe_module.h2
-rw-r--r--drivers/gpu/drm/xe/xe_nvm.c35
-rw-r--r--drivers/gpu/drm/xe/xe_oa.c106
-rw-r--r--drivers/gpu/drm/xe/xe_oa_types.h1
-rw-r--r--drivers/gpu/drm/xe/xe_page_reclaim.c132
-rw-r--r--drivers/gpu/drm/xe/xe_page_reclaim.h125
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault.c36
-rw-r--r--drivers/gpu/drm/xe/xe_pat.c272
-rw-r--r--drivers/gpu/drm/xe/xe_pat.h23
-rw-r--r--drivers/gpu/drm/xe/xe_pci.c42
-rw-r--r--drivers/gpu/drm/xe/xe_pci_rebar.c106
-rw-r--r--drivers/gpu/drm/xe/xe_pci_rebar.h13
-rw-r--r--drivers/gpu/drm/xe/xe_pci_sriov.c10
-rw-r--r--drivers/gpu/drm/xe/xe_pci_types.h9
-rw-r--r--drivers/gpu/drm/xe/xe_pcode_api.h9
-rw-r--r--drivers/gpu/drm/xe/xe_pm.c34
-rw-r--r--drivers/gpu/drm/xe/xe_pmu.c2
-rw-r--r--drivers/gpu/drm/xe/xe_psmi.c2
-rw-r--r--drivers/gpu/drm/xe/xe_pt.c167
-rw-r--r--drivers/gpu/drm/xe/xe_pt_types.h6
-rw-r--r--drivers/gpu/drm/xe/xe_pxp.c56
-rw-r--r--drivers/gpu/drm/xe/xe_pxp_debugfs.c2
-rw-r--r--drivers/gpu/drm/xe/xe_query.c23
-rw-r--r--drivers/gpu/drm/xe/xe_reg_sr.c22
-rw-r--r--drivers/gpu/drm/xe/xe_reg_whitelist.c82
-rw-r--r--drivers/gpu/drm/xe/xe_ring_ops.c97
-rw-r--r--drivers/gpu/drm/xe/xe_rtp.c1
-rw-r--r--drivers/gpu/drm/xe/xe_sa.c69
-rw-r--r--drivers/gpu/drm/xe/xe_sa.h20
-rw-r--r--drivers/gpu/drm/xe/xe_sa_types.h3
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.c3
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_soc_remapper.c52
-rw-r--r--drivers/gpu/drm/xe/xe_soc_remapper.h13
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_packet.c3
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf.c2
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c7
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c16
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf.c85
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.c29
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.h1
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vfio.c2
-rw-r--r--drivers/gpu/drm/xe/xe_step.c3
-rw-r--r--drivers/gpu/drm/xe/xe_survivability_mode.c276
-rw-r--r--drivers/gpu/drm/xe/xe_survivability_mode_types.h28
-rw-r--r--drivers/gpu/drm/xe/xe_svm.c799
-rw-r--r--drivers/gpu/drm/xe/xe_svm.h87
-rw-r--r--drivers/gpu/drm/xe/xe_sync.c33
-rw-r--r--drivers/gpu/drm/xe/xe_sync.h2
-rw-r--r--drivers/gpu/drm/xe/xe_tile.c42
-rw-r--r--drivers/gpu/drm/xe/xe_tile.h21
-rw-r--r--drivers/gpu/drm/xe/xe_tile_debugfs.c17
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c3
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval.c44
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval.h2
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_job.c36
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_job.h4
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_types.h7
-rw-r--r--drivers/gpu/drm/xe/xe_trace.h47
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c1
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_sys_mgr.c1
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_vram_mgr.c1
-rw-r--r--drivers/gpu/drm/xe/xe_uc.c37
-rw-r--r--drivers/gpu/drm/xe/xe_uc.h2
-rw-r--r--drivers/gpu/drm/xe/xe_uc_debugfs.c2
-rw-r--r--drivers/gpu/drm/xe/xe_uc_fw.c24
-rw-r--r--drivers/gpu/drm/xe/xe_userptr.c2
-rw-r--r--drivers/gpu/drm/xe/xe_validation.c1
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c258
-rw-r--r--drivers/gpu/drm/xe/xe_vm.h6
-rw-r--r--drivers/gpu/drm/xe/xe_vm_madvise.c106
-rw-r--r--drivers/gpu/drm/xe/xe_vm_types.h24
-rw-r--r--drivers/gpu/drm/xe/xe_vram.c101
-rw-r--r--drivers/gpu/drm/xe/xe_vram.h1
-rw-r--r--drivers/gpu/drm/xe/xe_vram_freq.c1
-rw-r--r--drivers/gpu/drm/xe/xe_vram_types.h15
-rw-r--r--drivers/gpu/drm/xe/xe_vsec.c7
-rw-r--r--drivers/gpu/drm/xe/xe_wa.c62
-rw-r--r--drivers/gpu/drm/xe/xe_wa_oob.rules7
-rw-r--r--drivers/gpu/drm/xe/xe_wait_user_fence.c1
-rw-r--r--drivers/gpu/drm/xe/xe_wopcm.c2
248 files changed, 9266 insertions, 3458 deletions
diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig
index 4b288eb3f5b0..4d7dcaff2b91 100644
--- a/drivers/gpu/drm/xe/Kconfig
+++ b/drivers/gpu/drm/xe/Kconfig
@@ -39,7 +39,7 @@ config DRM_XE
select DRM_TTM
select DRM_TTM_HELPER
select DRM_EXEC
- select DRM_GPUSVM if !UML && DEVICE_PRIVATE
+ select DRM_GPUSVM if !UML
select DRM_GPUVM
select DRM_SCHED
select MMU_NOTIFIER
@@ -80,8 +80,9 @@ config DRM_XE_GPUSVM
bool "Enable CPU to GPU address mirroring"
depends on DRM_XE
depends on !UML
- depends on DEVICE_PRIVATE
+ depends on ZONE_DEVICE
default y
+ select DEVICE_PRIVATE
select DRM_GPUSVM
help
Enable this option if you want support for CPU to GPU address
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 62be4a5227e4..b39cbb756232 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -95,8 +95,10 @@ xe-y += xe_bb.o \
xe_oa.o \
xe_observation.o \
xe_pagefault.o \
+ xe_page_reclaim.o \
xe_pat.o \
xe_pci.o \
+ xe_pci_rebar.o \
xe_pcode.o \
xe_pm.o \
xe_preempt_fence.o \
@@ -115,6 +117,7 @@ xe-y += xe_bb.o \
xe_sa.o \
xe_sched_job.o \
xe_shrinker.o \
+ xe_soc_remapper.o \
xe_step.o \
xe_survivability_mode.o \
xe_sync.o \
@@ -173,6 +176,7 @@ xe-$(CONFIG_PCI_IOV) += \
xe_lmtt.o \
xe_lmtt_2l.o \
xe_lmtt_ml.o \
+ xe_mert.o \
xe_pci_sriov.o \
xe_sriov_packet.o \
xe_sriov_pf.o \
@@ -195,15 +199,8 @@ endif
# i915 Display compat #defines and #includes
subdir-ccflags-$(CONFIG_DRM_XE_DISPLAY) += \
- -I$(src)/display/ext \
-I$(src)/compat-i915-headers \
- -I$(srctree)/drivers/gpu/drm/i915/display/ \
- -Ddrm_i915_private=xe_device
-
-# Rule to build SOC code shared with i915
-$(obj)/i915-soc/%.o: $(srctree)/drivers/gpu/drm/i915/soc/%.c FORCE
- $(call cmd,force_checksrc)
- $(call if_changed_rule,cc_o_c)
+ -I$(srctree)/drivers/gpu/drm/i915/display/
# Rule to build display code shared with i915
$(obj)/i915-display/%.o: $(srctree)/drivers/gpu/drm/i915/display/%.c FORCE
@@ -212,27 +209,20 @@ $(obj)/i915-display/%.o: $(srctree)/drivers/gpu/drm/i915/display/%.c FORCE
# Display code specific to xe
xe-$(CONFIG_DRM_XE_DISPLAY) += \
- display/ext/i915_irq.o \
display/intel_bo.o \
display/intel_fb_bo.o \
display/intel_fbdev_fb.o \
display/xe_display.o \
- display/xe_display_misc.o \
display/xe_display_rpm.o \
display/xe_display_wa.o \
display/xe_dsb_buffer.o \
display/xe_fb_pin.o \
display/xe_hdcp_gsc.o \
+ display/xe_initial_plane.o \
display/xe_panic.o \
- display/xe_plane_initial.o \
display/xe_stolen.o \
display/xe_tdf.o
-# SOC code shared with i915
-xe-$(CONFIG_DRM_XE_DISPLAY) += \
- i915-soc/intel_dram.o \
- i915-soc/intel_rom.o
-
# Display code shared with i915
xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/icl_dsi.o \
@@ -267,6 +257,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_display_power_map.o \
i915-display/intel_display_power_well.o \
i915-display/intel_display_rpm.o \
+ i915-display/intel_display_rps.o \
i915-display/intel_display_trace.o \
i915-display/intel_display_utils.o \
i915-display/intel_display_wa.o \
@@ -283,6 +274,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_dpll.o \
i915-display/intel_dpll_mgr.o \
i915-display/intel_dpt_common.o \
+ i915-display/intel_dram.o \
i915-display/intel_drrs.o \
i915-display/intel_dsb.o \
i915-display/intel_dsi.o \
@@ -303,6 +295,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_hotplug.o \
i915-display/intel_hotplug_irq.o \
i915-display/intel_hti.o \
+ i915-display/intel_initial_plane.o \
i915-display/intel_link_bw.o \
i915-display/intel_lspcon.o \
i915-display/intel_lt_phy.o \
@@ -310,14 +303,16 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_modeset_setup.o \
i915-display/intel_modeset_verify.o \
i915-display/intel_panel.o \
+ i915-display/intel_parent.o \
+ i915-display/intel_pch.o \
i915-display/intel_pfit.o \
i915-display/intel_plane.o \
i915-display/intel_pmdemand.o \
- i915-display/intel_pch.o \
i915-display/intel_pps.o \
i915-display/intel_psr.o \
i915-display/intel_qp_tables.o \
i915-display/intel_quirks.o \
+ i915-display/intel_rom.o \
i915-display/intel_snps_hdmi_pll.o \
i915-display/intel_snps_phy.o \
i915-display/intel_tc.o \
@@ -375,7 +370,8 @@ always-$(CONFIG_DRM_XE_WERROR) += \
$(patsubst %.h,%.hdrtest, $(shell cd $(src) && find * -name '*.h' $(hdrtest_find_args)))
quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@)
- cmd_hdrtest = $(CC) -DHDRTEST $(filter-out $(CFLAGS_GCOV), $(c_flags)) -S -o /dev/null -x c /dev/null -include $<; touch $@
+ cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_GCOV), $(c_flags)) -S -o /dev/null -x c /dev/null -include $< -include $<; \
+ $(srctree)/scripts/kernel-doc -none -Werror $<; touch $@
$(obj)/%.hdrtest: $(src)/%.h FORCE
$(call if_changed_dep,hdrtest)
diff --git a/drivers/gpu/drm/xe/abi/guc_actions_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
index 47756e4674a1..83a6e7794982 100644
--- a/drivers/gpu/drm/xe/abi/guc_actions_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
@@ -139,6 +139,10 @@ enum xe_guc_action {
XE_GUC_ACTION_DEREGISTER_G2G = 0x4508,
XE_GUC_ACTION_DEREGISTER_CONTEXT_DONE = 0x4600,
XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC = 0x4601,
+ XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_QUEUE = 0x4602,
+ XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC = 0x4603,
+ XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE = 0x4604,
+ XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CGP_CONTEXT_ERROR = 0x4605,
XE_GUC_ACTION_CLIENT_SOFT_RESET = 0x5507,
XE_GUC_ACTION_SET_ENG_UTIL_BUFF = 0x550A,
XE_GUC_ACTION_SET_DEVICE_ENGINE_ACTIVITY_BUFFER = 0x550C,
@@ -151,6 +155,8 @@ enum xe_guc_action {
XE_GUC_ACTION_TLB_INVALIDATION = 0x7000,
XE_GUC_ACTION_TLB_INVALIDATION_DONE = 0x7001,
XE_GUC_ACTION_TLB_INVALIDATION_ALL = 0x7002,
+ XE_GUC_ACTION_PAGE_RECLAMATION = 0x7003,
+ XE_GUC_ACTION_PAGE_RECLAMATION_DONE = 0x7004,
XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION = 0x8002,
XE_GUC_ACTION_NOTIFY_FLUSH_LOG_BUFFER_TO_FILE = 0x8003,
XE_GUC_ACTION_NOTIFY_CRASH_DUMP_POSTED = 0x8004,
diff --git a/drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h
index 0b28659d94e9..d9f21202e1a9 100644
--- a/drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h
@@ -502,13 +502,17 @@
#define VF2GUC_VF_RESET_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
/**
- * DOC: VF2GUC_NOTIFY_RESFIX_DONE
+ * DOC: VF2GUC_RESFIX_DONE
*
- * This action is used by VF to notify the GuC that the VF KMD has completed
- * post-migration recovery steps.
+ * This action is used by VF to inform the GuC that the VF KMD has completed
+ * post-migration recovery steps. From GuC VF compatibility 1.27.0 onwards, it
+ * shall only be sent after posting RESFIX_START and that both @MARKER fields
+ * must match.
*
* This message must be sent as `MMIO HXG Message`_.
*
+ * Updated since GuC VF compatibility 1.27.0.
+ *
* +---+-------+--------------------------------------------------------------+
* | | Bits | Description |
* +===+=======+==============================================================+
@@ -516,9 +520,11 @@
* | +-------+--------------------------------------------------------------+
* | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ |
* | +-------+--------------------------------------------------------------+
- * | | 27:16 | DATA0 = MBZ |
+ * | | 27:16 | DATA0 = MARKER = MBZ (only prior 1.27.0) |
* | +-------+--------------------------------------------------------------+
- * | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE` = 0x5508 |
+ * | | 27:16 | DATA0 = MARKER - can't be zero (1.27.0+) |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_RESFIX_DONE` = 0x5508 |
* +---+-------+--------------------------------------------------------------+
*
* +---+-------+--------------------------------------------------------------+
@@ -531,13 +537,13 @@
* | | 27:0 | DATA0 = MBZ |
* +---+-------+--------------------------------------------------------------+
*/
-#define GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE 0x5508u
+#define GUC_ACTION_VF2GUC_RESFIX_DONE 0x5508u
-#define VF2GUC_NOTIFY_RESFIX_DONE_REQUEST_MSG_LEN GUC_HXG_REQUEST_MSG_MIN_LEN
-#define VF2GUC_NOTIFY_RESFIX_DONE_REQUEST_MSG_0_MBZ GUC_HXG_REQUEST_MSG_0_DATA0
+#define VF2GUC_RESFIX_DONE_REQUEST_MSG_LEN GUC_HXG_REQUEST_MSG_MIN_LEN
+#define VF2GUC_RESFIX_DONE_REQUEST_MSG_0_MARKER GUC_HXG_REQUEST_MSG_0_DATA0
-#define VF2GUC_NOTIFY_RESFIX_DONE_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
-#define VF2GUC_NOTIFY_RESFIX_DONE_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
+#define VF2GUC_RESFIX_DONE_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
+#define VF2GUC_RESFIX_DONE_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
/**
* DOC: VF2GUC_QUERY_SINGLE_KLV
@@ -656,4 +662,45 @@
#define PF2GUC_SAVE_RESTORE_VF_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
#define PF2GUC_SAVE_RESTORE_VF_RESPONSE_MSG_0_USED GUC_HXG_RESPONSE_MSG_0_DATA0
+/**
+ * DOC: VF2GUC_RESFIX_START
+ *
+ * This action is used by VF to inform the GuC that the VF KMD will be starting
+ * post-migration recovery fixups. The @MARKER sent with this action must match
+ * with the MARKER posted in the VF2GUC_RESFIX_DONE message.
+ *
+ * This message must be sent as `MMIO HXG Message`_.
+ *
+ * Available since GuC VF compatibility 1.27.0.
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:16 | DATA0 = MARKER - can't be zero |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_RESFIX_START` = 0x550F |
+ * +---+-------+--------------------------------------------------------------+
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_GUC_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_RESPONSE_SUCCESS_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:0 | DATA0 = MBZ |
+ * +---+-------+--------------------------------------------------------------+
+ */
+#define GUC_ACTION_VF2GUC_RESFIX_START 0x550Fu
+
+#define VF2GUC_RESFIX_START_REQUEST_MSG_LEN GUC_HXG_REQUEST_MSG_MIN_LEN
+#define VF2GUC_RESFIX_START_REQUEST_MSG_0_MARKER GUC_HXG_REQUEST_MSG_0_DATA0
+
+#define VF2GUC_RESFIX_START_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
+#define VF2GUC_RESFIX_START_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
+
#endif
diff --git a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
index 265a135e7061..e33bd622ab44 100644
--- a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
@@ -8,6 +8,8 @@
#include <linux/types.h>
+#include "abi/guc_scheduler_abi.h"
+
/**
* DOC: GuC KLV
*
@@ -46,11 +48,18 @@
* Refers to 32 bit architecture version as reported by the HW IP.
* This key is supported on MTL+ platforms only.
* Requires GuC ABI 1.2+.
+ *
+ * _`GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE` : 0x3001
+ * Tells the driver whether scheduler groups are enabled or not.
+ * Requires GuC ABI 1.26+
*/
#define GUC_KLV_GLOBAL_CFG_GMD_ID_KEY 0x3000u
#define GUC_KLV_GLOBAL_CFG_GMD_ID_LEN 1u
+#define GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE_KEY 0x3001u
+#define GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE_LEN 1u
+
/**
* DOC: GuC Self Config KLVs
*
@@ -200,6 +209,20 @@ enum {
* :0: adverse events are not counted (default)
* :n: sample period in milliseconds
*
+ * _`GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG` : 0x8004
+ * This config allows the PF to split the engines across scheduling groups.
+ * Each group is independently timesliced across VFs, allowing different
+ * VFs to be active on the HW at the same time. When enabling this feature,
+ * all engines must be assigned to a group (and only one group), or they
+ * will be excluded from scheduling after this KLV is sent. To enable
+ * the groups, the driver must provide a masks array with
+ * GUC_MAX_ENGINE_CLASSES entries for each group, with each mask indicating
+ * which logical instances of that class belong to the group. Therefore,
+ * the length of this KLV when enabling groups is
+ * num_groups * GUC_MAX_ENGINE_CLASSES. To disable the groups, the driver
+ * must send the KLV without any payload (i.e. len = 0). The maximum
+ * number of groups is 8.
+ *
* _`GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH` : 0x8D00
* This enum is to reset utilized HW engine after VF Switch (i.e to clean
* up Stale HW register left behind by previous VF)
@@ -214,6 +237,12 @@ enum {
#define GUC_KLV_VGT_POLICY_ADVERSE_SAMPLE_PERIOD_KEY 0x8002
#define GUC_KLV_VGT_POLICY_ADVERSE_SAMPLE_PERIOD_LEN 1u
+#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_KEY 0x8004
+#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_MAX_COUNT GUC_MAX_SCHED_GROUPS
+#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_MIN_LEN 0
+#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_MAX_LEN \
+ (GUC_KLV_VGT_POLICY_ENGINE_GROUP_MAX_COUNT * GUC_MAX_ENGINE_CLASSES)
+
#define GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH_KEY 0x8D00
#define GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH_LEN 1u
@@ -268,6 +297,10 @@ enum {
* it to take effect. Such cases might typically happen on a 1PF+1VF
* Virtualization config enabled for heavier workloads like AI/ML.
*
+ * If scheduling groups are supported, the provided value is applied to all
+ * groups (even if they've not yet been enabled). Support for this feature
+ * is available from GuC 70.53.0.
+ *
* The max value for this KLV is 100 seconds, anything exceeding that
* will be clamped to the max.
*
@@ -290,6 +323,10 @@ enum {
* on a 1PF+1VF Virtualization config enabled for heavier workloads like
* AI/ML.
*
+ * If scheduling groups are supported, the provided value is applied to all
+ * groups (even if they've not yet been enabled). Support for this feature
+ * is available from GuC 70.53.0.
+ *
* The max value for this KLV is 100 seconds, anything exceeding that
* will be clamped to the max.
*
@@ -352,6 +389,32 @@ enum {
* :1: NORMAL = schedule VF always, irrespective of whether it has work or not
* :2: HIGH = schedule VF in the next time-slice after current active
* time-slice completes if it has active work
+ *
+ * _`GUC_KLV_VF_CFG_THRESHOLD_MULTI_LRC_COUNT` : 0x8A0D
+ * Given that multi-LRC contexts are incompatible with SRIOV scheduler
+ * groups and cause the latter to be turned off when registered with the
+ * GuC, this config allows the PF to set a threshold for multi-LRC context
+ * registrations by VFs to monitor their behavior.
+ *
+ * _`GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM' : 0x8A0E
+ * This config sets the VFs-execution-quantum for each scheduling group in
+ * milliseconds. The driver must provide an array of values, with each of
+ * them matching the respective group index (first value goes to group 0,
+ * second to group 1, etc). The setting of group values follows the same
+ * behavior and rules as setting via GUC_KLV_VF_CFG_EXEC_QUANTUM. Note that
+ * the GuC always sets the EQ for all groups (even the non-enabled ones),
+ * so if we provide fewer values than the max the GuC will use 0 for the
+ * remaining groups. This KLV is available starting from GuC 70.53.0.
+ *
+ * _`GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT' : 0x8A0F
+ * This config sets the VFs-preemption-timeout for each scheduling group in
+ * microseconds. The driver must provide an array of values, with each of
+ * them matching the respective group index (first value goes to group 0,
+ * second to group 1, etc). The setting of group values follows the same
+ * behavior and rules as setting via GUC_KLV_VF_CFG_PREEMPT_TIMEOUT. Note
+ * that the GuC always sets the EQ for all groups (even the non-enabled
+ * ones), so if we provide fewer values than the max the GuC will use 0 for
+ * the remaining groups. This KLV is available starting from GuC 70.53.0.
*/
#define GUC_KLV_VF_CFG_GGTT_START_KEY 0x0001
@@ -410,6 +473,16 @@ enum {
#define GUC_SCHED_PRIORITY_NORMAL 1u
#define GUC_SCHED_PRIORITY_HIGH 2u
+#define GUC_KLV_VF_CFG_THRESHOLD_MULTI_LRC_COUNT_KEY 0x8a0d
+#define GUC_KLV_VF_CFG_THRESHOLD_MULTI_LRC_COUNT_LEN 1u
+
+#define GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY 0x8a0e
+#define GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_MIN_LEN 1u
+#define GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_MAX_LEN GUC_MAX_SCHED_GROUPS
+
+#define GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY 0x8a0f
+#define GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_MIN_LEN 1u
+#define GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_MAX_LEN GUC_MAX_SCHED_GROUPS
/*
* Workaround keys:
*/
diff --git a/drivers/gpu/drm/xe/abi/guc_lfd_abi.h b/drivers/gpu/drm/xe/abi/guc_lfd_abi.h
new file mode 100644
index 000000000000..5c48459f365f
--- /dev/null
+++ b/drivers/gpu/drm/xe/abi/guc_lfd_abi.h
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _ABI_GUC_LFD_ABI_H_
+#define _ABI_GUC_LFD_ABI_H_
+
+#include <linux/types.h>
+
+#include "guc_lic_abi.h"
+
+/* The current major version of GuC-Log-File format. */
+#define GUC_LFD_FORMAT_VERSION_MAJOR 0x0001
+/* The current minor version of GuC-Log-File format. */
+#define GUC_LFD_FORMAT_VERSION_MINOR 0x0000
+
+/** enum guc_lfd_type - Log format descriptor type */
+enum guc_lfd_type {
+ /**
+ * @GUC_LFD_TYPE_FW_REQUIRED_RANGE_START: Start of range for
+ * required LFDs from GuC
+ * @GUC_LFD_TYPE_FW_VERSION: GuC Firmware Version structure.
+ * @GUC_LFD_TYPE_GUC_DEVICE_ID: GuC microcontroller device ID.
+ * @GUC_LFD_TYPE_TSC_FREQUENCY: Frequency of GuC timestamps.
+ * @GUC_LFD_TYPE_GMD_ID: HW GMD ID.
+ * @GUC_LFD_TYPE_BUILD_PLATFORM_ID: GuC build platform ID.
+ * @GUC_LFD_TYPE_FW_REQUIRED_RANGE_END: End of range for
+ * required LFDs from GuC
+ */
+ GUC_LFD_TYPE_FW_REQUIRED_RANGE_START = 0x1,
+ GUC_LFD_TYPE_FW_VERSION = 0x1,
+ GUC_LFD_TYPE_GUC_DEVICE_ID = 0x2,
+ GUC_LFD_TYPE_TSC_FREQUENCY = 0x3,
+ GUC_LFD_TYPE_GMD_ID = 0x4,
+ GUC_LFD_TYPE_BUILD_PLATFORM_ID = 0x5,
+ GUC_LFD_TYPE_FW_REQUIRED_RANGE_END = 0x1FFF,
+
+ /**
+ * @GUC_LFD_TYPE_FW_OPTIONAL_RANGE_START: Start of range for
+ * optional LFDs from GuC
+ * @GUC_LFD_TYPE_LOG_EVENTS_BUFFER: Log-event-entries buffer.
+ * @GUC_LFD_TYPE_FW_CRASH_DUMP: GuC generated crash-dump blob.
+ * @GUC_LFD_TYPE_FW_OPTIONAL_RANGE_END: End of range for
+ * optional LFDs from GuC
+ */
+ GUC_LFD_TYPE_FW_OPTIONAL_RANGE_START = 0x2000,
+ GUC_LFD_TYPE_LOG_EVENTS_BUFFER = 0x2000,
+ GUC_LFD_TYPE_FW_CRASH_DUMP = 0x2001,
+ GUC_LFD_TYPE_FW_OPTIONAL_RANGE_END = 0x3FFF,
+
+ /**
+ * @GUC_LFD_TYPE_KMD_REQUIRED_RANGE_START: Start of range for
+ * required KMD LFDs
+ * @GUC_LFD_TYPE_OS_ID: An identifier for the OS.
+ * @GUC_LFD_TYPE_KMD_REQUIRED_RANGE_END: End of this range for
+ * required KMD LFDs
+ */
+ GUC_LFD_TYPE_KMD_REQUIRED_RANGE_START = 0x4000,
+ GUC_LFD_TYPE_OS_ID = 0x4000,
+ GUC_LFD_TYPE_KMD_REQUIRED_RANGE_END = 0x5FFF,
+
+ /**
+ * @GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_START: Start of range for
+ * optional KMD LFDs
+ * @GUC_LFD_TYPE_BINARY_SCHEMA_FORMAT: Binary representation of
+ * GuC log-events schema.
+ * @GUC_LFD_TYPE_HOST_COMMENT: ASCII string containing comments
+ * from the host/KMD.
+ * @GUC_LFD_TYPE_TIMESTAMP_ANCHOR: A timestamp anchor, to convert
+ * between host and GuC timestamp.
+ * @GUC_LFD_TYPE_TIMESTAMP_ANCHOR_CONFIG: Timestamp anchor
+ * configuration, definition of timestamp frequency and bit width.
+ * @GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_END: End of this range for
+ * optional KMD LFDs
+ */
+ GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_START = 0x6000,
+ GUC_LFD_TYPE_BINARY_SCHEMA_FORMAT = 0x6000,
+ GUC_LFD_TYPE_HOST_COMMENT = 0x6001,
+ GUC_LFD_TYPE_TIMESTAMP_ANCHOR = 0x6002,
+ GUC_LFD_TYPE_TIMESTAMP_ANCHOR_CONFIG = 0x6003,
+ GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_END = 0x7FFF,
+
+ /*
+ * @GUC_LFD_TYPE_RESERVED_RANGE_START: Start of reserved range
+ * @GUC_LFD_TYPE_RESERVED_RANGE_END: End of reserved range
+ */
+ GUC_LFD_TYPE_RESERVED_RANGE_START = 0x8000,
+ GUC_LFD_TYPE_RESERVED_RANGE_END = 0xFFFF,
+};
+
+/** enum guc_lfd_os_type - OS Type LFD-ID */
+enum guc_lfd_os_type {
+ /** @GUC_LFD_OS_TYPE_OSID_WIN: Windows OS */
+ GUC_LFD_OS_TYPE_OSID_WIN = 0x1,
+ /** @GUC_LFD_OS_TYPE_OSID_LIN: Linux OS */
+ GUC_LFD_OS_TYPE_OSID_LIN = 0x2,
+ /** @GUC_LFD_OS_TYPE_OSID_VMW: VMWare OS */
+ GUC_LFD_OS_TYPE_OSID_VMW = 0x3,
+ /** @GUC_LFD_OS_TYPE_OSID_OTHER: Other */
+ GUC_LFD_OS_TYPE_OSID_OTHER = 0x4,
+};
+
+/** struct guc_lfd_data - A generic header structure for all LFD blocks */
+struct guc_lfd_data {
+ /** @header: A 32 bits dword, contains multiple bit fields */
+ u32 header;
+ /* LFD type. See guc_lfd_type */
+#define GUC_LFD_DATA_HEADER_MASK_TYPE GENMASK(31, 16)
+#define GUC_LFD_DATA_HEADER_MASK_MAGIC GENMASK(15, 0)
+
+ /** @data_count: Number of dwords the `data` field contains. */
+ u32 data_count;
+ /** @data: Data defined by GUC_LFD_DATA_HEADER_MASK_TYPE */
+ u32 data[] __counted_by(data_count);
+} __packed;
+
+/**
+ * struct guc_lfd_data_log_events_buf - GuC Log Events Buffer.
+ * This is optional fw LFD data
+ */
+struct guc_lfd_data_log_events_buf {
+ /**
+ * @log_events_format_version: version of GuC log format of buffer
+ */
+ u32 log_events_format_version;
+ /**
+ * @log_event: The log event data.
+ * Size in dwords is LFD block size - 1.
+ */
+ u32 log_event[];
+} __packed;
+
+/** struct guc_lfd_data_os_info - OS Version Information. */
+struct guc_lfd_data_os_info {
+ /**
+ * @os_id: enum values to identify the OS brand.
+ * See guc_lfd_os_type for the range of types
+ */
+ u32 os_id;
+ /**
+ * @build_version: ASCII string containing OS build version
+ * information based on os_id. String is padded with null
+ * characters to ensure its DWORD aligned.
+ * Size in dwords is LFD block size - 1.
+ */
+ char build_version[];
+} __packed;
+
+/**
+ * struct guc_lfd_file_header - Header of GuC Log Streaming-LFD-File Format.
+ * This structure encapsulates the layout of the guc-log-file format
+ */
+struct guc_lfd_file_header {
+ /**
+ * @magic: A magic number set by producer of a GuC log file to
+ * identify that file is a valid guc-log-file containing a stream
+ * of LFDs.
+ */
+ u64 magic;
+ /** @version: Version of this file format layout */
+ u32 version;
+#define GUC_LFD_FILE_HEADER_VERSION_MASK_MAJOR GENMASK(31, 16)
+#define GUC_LFD_FILE_HEADER_VERSION_MASK_MINOR GENMASK(15, 0)
+
+ /** @stream: A stream of one or more guc_lfd_data LFD blocks */
+ u32 stream[];
+} __packed;
+
+#endif
diff --git a/drivers/gpu/drm/xe/abi/guc_lic_abi.h b/drivers/gpu/drm/xe/abi/guc_lic_abi.h
new file mode 100644
index 000000000000..9169644093a2
--- /dev/null
+++ b/drivers/gpu/drm/xe/abi/guc_lic_abi.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _ABI_GUC_LIC_ABI_H_
+#define _ABI_GUC_LIC_ABI_H_
+
+#include <linux/types.h>
+
+/**
+ * enum guc_lic_type - Log Init Config KLV IDs.
+ */
+enum guc_lic_type {
+ /**
+ * @GUC_LIC_TYPE_GUC_SW_VERSION: GuC firmware version. Value
+ * is a 32 bit number represented by guc_sw_version.
+ */
+ GUC_LIC_TYPE_GUC_SW_VERSION = 0x1,
+ /**
+ * @GUC_LIC_TYPE_GUC_DEVICE_ID: GuC device id. Value is a 32
+ * bit.
+ */
+ GUC_LIC_TYPE_GUC_DEVICE_ID = 0x2,
+ /**
+ * @GUC_LIC_TYPE_TSC_FREQUENCY: GuC timestamp counter
+ * frequency. Value is a 32 bit number representing frequency in
+ * kHz. This timestamp is utilized in log entries, timer and
+ * for engine utilization tracking.
+ */
+ GUC_LIC_TYPE_TSC_FREQUENCY = 0x3,
+ /**
+ * @GUC_LIC_TYPE_GMD_ID: HW GMD ID. Value is a 32 bit number
+ * representing graphics, media and display HW architecture IDs.
+ */
+ GUC_LIC_TYPE_GMD_ID = 0x4,
+ /**
+ * @GUC_LIC_TYPE_BUILD_PLATFORM_ID: GuC build platform ID.
+ * Value is 32 bits.
+ */
+ GUC_LIC_TYPE_BUILD_PLATFORM_ID = 0x5,
+};
+
+/**
+ * struct guc_lic - GuC LIC (Log-Init-Config) structure.
+ *
+ * This is populated by the GUC at log init time and is located in the log
+ * buffer memory allocation.
+ */
+struct guc_lic {
+ /**
+ * @magic: A magic number set by GuC to identify that this
+ * structure contains valid information: magic = GUC_LIC_MAGIC.
+ */
+ u32 magic;
+#define GUC_LIC_MAGIC 0x8086900D
+ /**
+ * @version: The version of the this structure.
+ * Major and minor version number are represented as bit fields.
+ */
+ u32 version;
+#define GUC_LIC_VERSION_MASK_MAJOR GENMASK(31, 16)
+#define GUC_LIC_VERSION_MASK_MINOR GENMASK(15, 0)
+
+#define GUC_LIC_VERSION_MAJOR 1u
+#define GUC_LIC_VERSION_MINOR 0u
+
+ /** @data_count: Number of dwords the `data` array contains. */
+ u32 data_count;
+ /**
+ * @data: Array of dwords representing a list of LIC KLVs of
+ * type guc_klv_generic with keys represented by guc_lic_type
+ */
+ u32 data[] __counted_by(data_count);
+} __packed;
+
+#endif
diff --git a/drivers/gpu/drm/xe/abi/guc_log_abi.h b/drivers/gpu/drm/xe/abi/guc_log_abi.h
index 554630b7ccd9..fbf212d59a40 100644
--- a/drivers/gpu/drm/xe/abi/guc_log_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_log_abi.h
@@ -8,11 +8,45 @@
#include <linux/types.h>
+/**
+ * DOC: GuC Log buffer Layout
+ *
+ * The in-memory log buffer layout is as follows::
+ *
+ * +===============================+ 0000h
+ * | Crash dump state header | ^
+ * +-------------------------------+ 32B |
+ * | Debug state header | |
+ * +-------------------------------+ 64B 4KB
+ * | Capture state header | |
+ * +-------------------------------+ 96B |
+ * | | v
+ * +===============================+ <--- EVENT_DATA_OFFSET
+ * | Event logs(raw data) | ^
+ * | | |
+ * | | EVENT_DATA_BUFFER_SIZE
+ * | | |
+ * | | v
+ * +===============================+ <--- CRASH_DUMP_OFFSET
+ * | Crash Dump(raw data) | ^
+ * | | |
+ * | | CRASH_DUMP_BUFFER_SIZE
+ * | | |
+ * | | v
+ * +===============================+ <--- STATE_CAPTURE_OFFSET
+ * | Error state capture(raw data) | ^
+ * | | |
+ * | | STATE_CAPTURE_BUFFER_SIZE
+ * | | |
+ * | | v
+ * +===============================+ Total: GUC_LOG_SIZE
+ */
+
/* GuC logging buffer types */
-enum guc_log_buffer_type {
- GUC_LOG_BUFFER_CRASH_DUMP,
- GUC_LOG_BUFFER_DEBUG,
- GUC_LOG_BUFFER_CAPTURE,
+enum guc_log_type {
+ GUC_LOG_TYPE_EVENT_DATA,
+ GUC_LOG_TYPE_CRASH_DUMP,
+ GUC_LOG_TYPE_STATE_CAPTURE,
};
#define GUC_LOG_BUFFER_TYPE_MAX 3
diff --git a/drivers/gpu/drm/xe/abi/guc_scheduler_abi.h b/drivers/gpu/drm/xe/abi/guc_scheduler_abi.h
new file mode 100644
index 000000000000..513b22a87428
--- /dev/null
+++ b/drivers/gpu/drm/xe/abi/guc_scheduler_abi.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _ABI_GUC_SCHEDULER_ABI_H
+#define _ABI_GUC_SCHEDULER_ABI_H
+
+#include <linux/types.h>
+
+/**
+ * Generic defines required for registration with and submissions to the GuC
+ * scheduler. Includes engine class/instance defines and context attributes
+ * (id, priority, etc)
+ */
+
+/* Engine classes/instances */
+#define GUC_RENDER_CLASS 0
+#define GUC_VIDEO_CLASS 1
+#define GUC_VIDEOENHANCE_CLASS 2
+#define GUC_BLITTER_CLASS 3
+#define GUC_COMPUTE_CLASS 4
+#define GUC_GSC_OTHER_CLASS 5
+#define GUC_LAST_ENGINE_CLASS GUC_GSC_OTHER_CLASS
+#define GUC_MAX_ENGINE_CLASSES 16
+#define GUC_MAX_INSTANCES_PER_CLASS 32
+
+/* context priority values */
+#define GUC_CLIENT_PRIORITY_KMD_HIGH 0
+#define GUC_CLIENT_PRIORITY_HIGH 1
+#define GUC_CLIENT_PRIORITY_KMD_NORMAL 2
+#define GUC_CLIENT_PRIORITY_NORMAL 3
+#define GUC_CLIENT_PRIORITY_NUM 4
+
+/* Context registration */
+#define GUC_ID_MAX 65535
+#define GUC_ID_UNKNOWN 0xffffffff
+
+#define CONTEXT_REGISTRATION_FLAG_KMD BIT(0)
+#define CONTEXT_REGISTRATION_FLAG_TYPE GENMASK(2, 1)
+#define GUC_CONTEXT_NORMAL 0
+#define GUC_CONTEXT_COMPRESSION_SAVE 1
+#define GUC_CONTEXT_COMPRESSION_RESTORE 2
+#define GUC_CONTEXT_COUNT (GUC_CONTEXT_COMPRESSION_RESTORE + 1)
+
+/* context enable/disable */
+#define GUC_CONTEXT_DISABLE 0
+#define GUC_CONTEXT_ENABLE 1
+
+/* scheduler groups */
+#define GUC_MAX_SCHED_GROUPS 8
+
+struct guc_sched_group {
+ u32 engines[GUC_MAX_ENGINE_CLASSES];
+} __packed;
+
+#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h
deleted file mode 100644
index 0548b2e0316f..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Copyright © 2025 Intel Corporation */
-
-#ifndef __I915_GEM_OBJECT_H__
-#define __I915_GEM_OBJECT_H__
-
-struct dma_fence;
-
-static inline void i915_gem_fence_wait_priority_display(struct dma_fence *fence)
-{
-}
-
-#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h
deleted file mode 100644
index 48e3256ba37e..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2024 Intel Corporation
- */
-
-#ifndef _I915_GEM_STOLEN_H_
-#define _I915_GEM_STOLEN_H_
-
-#include <linux/types.h>
-
-struct drm_device;
-struct intel_stolen_node;
-
-int i915_gem_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size,
- unsigned int align, u64 start, u64 end);
-
-int i915_gem_stolen_insert_node(struct intel_stolen_node *node, u64 size,
- unsigned int align);
-
-void i915_gem_stolen_remove_node(struct intel_stolen_node *node);
-
-bool i915_gem_stolen_initialized(struct drm_device *drm);
-
-bool i915_gem_stolen_node_allocated(const struct intel_stolen_node *node);
-
-u32 i915_gem_stolen_node_offset(struct intel_stolen_node *node);
-
-u64 i915_gem_stolen_area_address(struct drm_device *drm);
-
-u64 i915_gem_stolen_area_size(struct drm_device *drm);
-
-u64 i915_gem_stolen_node_address(struct intel_stolen_node *node);
-
-u64 i915_gem_stolen_node_size(const struct intel_stolen_node *node);
-
-struct intel_stolen_node *i915_gem_stolen_node_alloc(struct drm_device *drm);
-
-void i915_gem_stolen_node_free(const struct intel_stolen_node *node);
-
-#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h b/drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h
deleted file mode 100644
index c15806d6c4f7..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#ifndef __INTEL_GT_TYPES__
-#define __INTEL_GT_TYPES__
-
-#define intel_gt_support_legacy_fencing(gt) 0
-
-#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_active.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_active.h
deleted file mode 100644
index 6f0ab3753563..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_active.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2022 Intel Corporation
- */
-
-#ifndef _I915_ACTIVE_H_
-#define _I915_ACTIVE_H_
-
-#include "i915_active_types.h"
-
-static inline void i915_active_init(struct i915_active *ref,
- int (*active)(struct i915_active *ref),
- void (*retire)(struct i915_active *ref),
- unsigned long flags)
-{
- (void) active;
- (void) retire;
-}
-
-#define i915_active_fini(active) do { } while (0)
-
-#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_active_types.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_active_types.h
deleted file mode 100644
index 8c31f9a8b168..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_active_types.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright © 2019 Intel Corporation
- */
-
-#ifndef _I915_ACTIVE_TYPES_H_
-#define _I915_ACTIVE_TYPES_H_
-
-struct i915_active {};
-#define I915_ACTIVE_RETIRE_SLEEPS 0
-
-#endif /* _I915_ACTIVE_TYPES_H_ */
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_config.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_config.h
index e835bea08d1b..d4522203e2dd 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_config.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/i915_config.h
@@ -8,10 +8,7 @@
#include <linux/sched.h>
-struct drm_i915_private;
-
-static inline unsigned long
-i915_fence_timeout(const struct drm_i915_private *i915)
+static inline unsigned long i915_fence_timeout(void)
{
return MAX_SCHEDULE_TIMEOUT;
}
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h
deleted file mode 100644
index 3e79a74ff7de..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-#ifndef _XE_I915_DRV_H_
-#define _XE_I915_DRV_H_
-
-/*
- * "Adaptation header" to allow i915 display to also build for xe driver.
- * TODO: refactor i915 and xe so this can cease to exist
- */
-
-#include <drm/drm_drv.h>
-
-#include "xe_device_types.h"
-
-static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
-{
- return container_of(dev, struct drm_i915_private, drm);
-}
-
-/* compat platform checks only for soc/ usage */
-#define IS_PLATFORM(xe, x) ((xe)->info.platform == x)
-#define IS_I915G(dev_priv) (dev_priv && 0)
-#define IS_I915GM(dev_priv) (dev_priv && 0)
-#define IS_PINEVIEW(dev_priv) (dev_priv && 0)
-#define IS_VALLEYVIEW(dev_priv) (dev_priv && 0)
-#define IS_CHERRYVIEW(dev_priv) (dev_priv && 0)
-#define IS_HASWELL(dev_priv) (dev_priv && 0)
-#define IS_BROADWELL(dev_priv) (dev_priv && 0)
-#define IS_BROXTON(dev_priv) (dev_priv && 0)
-#define IS_GEMINILAKE(dev_priv) (dev_priv && 0)
-#define IS_DG2(dev_priv) IS_PLATFORM(dev_priv, XE_DG2)
-
-#define IS_MOBILE(xe) (xe && 0)
-
-#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_irq.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_irq.h
deleted file mode 100644
index 61707a07f91f..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_irq.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#include "../../i915/i915_irq.h"
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h
deleted file mode 100644
index bcd441dc0fce..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-/* for soc/ */
-#ifndef MISSING_CASE
-#define MISSING_CASE(x) WARN(1, "Missing case (%s == %ld)\n", \
- __stringify(x), (long)(x))
-#endif
-
-/* for a couple of users under i915/display */
-#define i915_inject_probe_failure(unused) ((unused) && 0)
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_vgpu.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_vgpu.h
deleted file mode 100644
index 4931c7198f13..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_vgpu.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#ifndef _I915_VGPU_H_
-#define _I915_VGPU_H_
-
-#include <linux/types.h>
-
-struct drm_i915_private;
-
-static inline bool intel_vgpu_active(struct drm_i915_private *i915)
-{
- return false;
-}
-
-#endif /* _I915_VGPU_H_ */
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h
index b17e3bab23d5..c4b5adaaa99a 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h
@@ -8,7 +8,7 @@
#include <uapi/drm/i915_drm.h>
-#include "xe_ggtt_types.h"
+#include "xe_ggtt.h"
#include <linux/refcount.h>
@@ -30,7 +30,7 @@ struct i915_vma {
static inline u32 i915_ggtt_offset(const struct i915_vma *vma)
{
- return vma->node->base.start;
+ return xe_ggtt_node_addr(vma->node);
}
#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h
index d93ddacdf743..c05d4c4292d3 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h
@@ -10,8 +10,6 @@
#include "xe_device_types.h"
#include "xe_mmio.h"
-#define FORCEWAKE_ALL XE_FORCEWAKE_ALL
-
static inline struct intel_uncore *to_intel_uncore(struct drm_device *drm)
{
return &to_xe_device(drm)->uncore;
@@ -154,9 +152,10 @@ static inline void intel_uncore_write_notrace(struct intel_uncore *uncore,
xe_mmio_write32(__compat_uncore_to_mmio(uncore), reg, val);
}
-#define intel_uncore_forcewake_get(x, y) do { } while (0)
-#define intel_uncore_forcewake_put(x, y) do { } while (0)
-
-#define intel_uncore_arm_unclaimed_mmio_detection(x) do { } while (0)
+static inline bool
+intel_uncore_arm_unclaimed_mmio_detection(struct intel_uncore *uncore)
+{
+ return false;
+}
#endif /* __INTEL_UNCORE_H__ */
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_wakeref.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_wakeref.h
deleted file mode 100644
index 2a32faea9db5..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/intel_wakeref.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#include <linux/types.h>
-
-typedef struct ref_tracker *intel_wakeref_t;
-
-#define INTEL_WAKEREF_DEF ERR_PTR(-ENOENT)
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/pxp/intel_pxp.h b/drivers/gpu/drm/xe/compat-i915-headers/pxp/intel_pxp.h
deleted file mode 100644
index 97fd0ddf0b3a..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/pxp/intel_pxp.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#ifndef __INTEL_PXP_H__
-#define __INTEL_PXP_H__
-
-#include <linux/errno.h>
-#include <linux/types.h>
-
-#include "xe_pxp.h"
-
-struct drm_gem_object;
-
-static inline int intel_pxp_key_check(struct drm_gem_object *obj, bool assign)
-{
- /*
- * The assign variable is used in i915 to assign the key to the BO at
- * first submission time. In Xe the key is instead assigned at BO
- * creation time, so the assign variable must always be false.
- */
- if (assign)
- return -EINVAL;
-
- return xe_pxp_obj_key_check(obj);
-}
-
-#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_dram.h b/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_dram.h
deleted file mode 100644
index 65707e20c557..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_dram.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#include "../../../i915/soc/intel_dram.h"
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_gmch.h b/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_gmch.h
deleted file mode 100644
index 33c5257b3a71..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_gmch.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#include "../../../i915/soc/intel_gmch.h"
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_rom.h b/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_rom.h
deleted file mode 100644
index 05cbfb697b2b..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_rom.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2024 Intel Corporation
- */
-
-#include "../../../i915/soc/intel_rom.h"
diff --git a/drivers/gpu/drm/xe/display/ext/i915_irq.c b/drivers/gpu/drm/xe/display/ext/i915_irq.c
deleted file mode 100644
index 3c6bca66ddab..000000000000
--- a/drivers/gpu/drm/xe/display/ext/i915_irq.c
+++ /dev/null
@@ -1,85 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#include "i915_irq.h"
-#include "i915_reg.h"
-#include "intel_uncore.h"
-
-void gen2_irq_reset(struct intel_uncore *uncore, struct i915_irq_regs regs)
-{
- intel_uncore_write(uncore, regs.imr, 0xffffffff);
- intel_uncore_posting_read(uncore, regs.imr);
-
- intel_uncore_write(uncore, regs.ier, 0);
-
- /* IIR can theoretically queue up two events. Be paranoid. */
- intel_uncore_write(uncore, regs.iir, 0xffffffff);
- intel_uncore_posting_read(uncore, regs.iir);
- intel_uncore_write(uncore, regs.iir, 0xffffffff);
- intel_uncore_posting_read(uncore, regs.iir);
-}
-
-/*
- * We should clear IMR at preinstall/uninstall, and just check at postinstall.
- */
-void gen2_assert_iir_is_zero(struct intel_uncore *uncore, i915_reg_t reg)
-{
- struct xe_device *xe = container_of(uncore, struct xe_device, uncore);
- u32 val = intel_uncore_read(uncore, reg);
-
- if (val == 0)
- return;
-
- drm_WARN(&xe->drm, 1,
- "Interrupt register 0x%x is not zero: 0x%08x\n",
- i915_mmio_reg_offset(reg), val);
- intel_uncore_write(uncore, reg, 0xffffffff);
- intel_uncore_posting_read(uncore, reg);
- intel_uncore_write(uncore, reg, 0xffffffff);
- intel_uncore_posting_read(uncore, reg);
-}
-
-void gen2_irq_init(struct intel_uncore *uncore, struct i915_irq_regs regs,
- u32 imr_val, u32 ier_val)
-{
- gen2_assert_iir_is_zero(uncore, regs.iir);
-
- intel_uncore_write(uncore, regs.ier, ier_val);
- intel_uncore_write(uncore, regs.imr, imr_val);
- intel_uncore_posting_read(uncore, regs.imr);
-}
-
-void gen2_error_reset(struct intel_uncore *uncore, struct i915_error_regs regs)
-{
- intel_uncore_write(uncore, regs.emr, 0xffffffff);
- intel_uncore_posting_read(uncore, regs.emr);
-
- intel_uncore_write(uncore, regs.eir, 0xffffffff);
- intel_uncore_posting_read(uncore, regs.eir);
- intel_uncore_write(uncore, regs.eir, 0xffffffff);
- intel_uncore_posting_read(uncore, regs.eir);
-}
-
-void gen2_error_init(struct intel_uncore *uncore, struct i915_error_regs regs,
- u32 emr_val)
-{
- intel_uncore_write(uncore, regs.eir, 0xffffffff);
- intel_uncore_posting_read(uncore, regs.eir);
- intel_uncore_write(uncore, regs.eir, 0xffffffff);
- intel_uncore_posting_read(uncore, regs.eir);
-
- intel_uncore_write(uncore, regs.emr, emr_val);
- intel_uncore_posting_read(uncore, regs.emr);
-}
-
-bool intel_irqs_enabled(struct xe_device *xe)
-{
- return atomic_read(&xe->irq.enabled);
-}
-
-void intel_synchronize_irq(struct xe_device *xe)
-{
- synchronize_irq(to_pci_dev(xe->drm.dev)->irq);
-}
diff --git a/drivers/gpu/drm/xe/display/intel_bo.c b/drivers/gpu/drm/xe/display/intel_bo.c
index bad2243b9114..e8049a255d21 100644
--- a/drivers/gpu/drm/xe/display/intel_bo.c
+++ b/drivers/gpu/drm/xe/display/intel_bo.c
@@ -3,9 +3,10 @@
#include <drm/drm_gem.h>
-#include "xe_bo.h"
#include "intel_bo.h"
#include "intel_frontbuffer.h"
+#include "xe_bo.h"
+#include "xe_pxp.h"
bool intel_bo_is_tiled(struct drm_gem_object *obj)
{
@@ -29,6 +30,11 @@ bool intel_bo_is_protected(struct drm_gem_object *obj)
return xe_bo_is_protected(gem_to_xe_bo(obj));
}
+int intel_bo_key_check(struct drm_gem_object *obj)
+{
+ return xe_pxp_obj_key_check(obj);
+}
+
int intel_bo_fb_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
{
return drm_gem_prime_mmap(obj, vma);
diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c
index 8b0afa270216..f8a831b5dc7d 100644
--- a/drivers/gpu/drm/xe/display/xe_display.c
+++ b/drivers/gpu/drm/xe/display/xe_display.c
@@ -17,7 +17,6 @@
#include <drm/intel/display_parent_interface.h>
#include <uapi/drm/xe_drm.h>
-#include "soc/intel_dram.h"
#include "intel_acpi.h"
#include "intel_audio.h"
#include "intel_bw.h"
@@ -29,6 +28,7 @@
#include "intel_dmc.h"
#include "intel_dmc_wl.h"
#include "intel_dp.h"
+#include "intel_dram.h"
#include "intel_encoder.h"
#include "intel_fbdev.h"
#include "intel_hdcp.h"
@@ -36,7 +36,11 @@
#include "intel_opregion.h"
#include "skl_watermark.h"
#include "xe_display_rpm.h"
+#include "xe_hdcp_gsc.h"
+#include "xe_initial_plane.h"
#include "xe_module.h"
+#include "xe_panic.h"
+#include "xe_stolen.h"
/* Ensure drm and display members are placed properly. */
INTEL_DISPLAY_MEMBER_STATIC_ASSERT(struct xe_device, drm, display);
@@ -122,7 +126,7 @@ int xe_display_init_early(struct xe_device *xe)
* Fill the dram structure to get the system dram info. This will be
* used for memory latency calculation.
*/
- err = intel_dram_detect(xe);
+ err = intel_dram_detect(display);
if (err)
goto err_opregion;
@@ -516,8 +520,30 @@ static void display_device_remove(struct drm_device *dev, void *arg)
intel_display_device_remove(display);
}
+static bool irq_enabled(struct drm_device *drm)
+{
+ struct xe_device *xe = to_xe_device(drm);
+
+ return atomic_read(&xe->irq.enabled);
+}
+
+static void irq_synchronize(struct drm_device *drm)
+{
+ synchronize_irq(to_pci_dev(drm->dev)->irq);
+}
+
+static const struct intel_display_irq_interface xe_display_irq_interface = {
+ .enabled = irq_enabled,
+ .synchronize = irq_synchronize,
+};
+
static const struct intel_display_parent_interface parent = {
+ .hdcp = &xe_display_hdcp_interface,
+ .initial_plane = &xe_display_initial_plane_interface,
+ .irq = &xe_display_irq_interface,
+ .panic = &xe_display_panic_interface,
.rpm = &xe_display_rpm_interface,
+ .stolen = &xe_display_stolen_interface,
};
/**
diff --git a/drivers/gpu/drm/xe/display/xe_display_misc.c b/drivers/gpu/drm/xe/display/xe_display_misc.c
deleted file mode 100644
index 242c2ef4ca93..000000000000
--- a/drivers/gpu/drm/xe/display/xe_display_misc.c
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#include "intel_display_types.h"
-
-struct pci_dev;
-
-unsigned int intel_gmch_vga_set_decode(struct pci_dev *pdev, bool enable_decode);
-
-unsigned int intel_gmch_vga_set_decode(struct pci_dev *pdev, bool enable_decode)
-{
- /* ToDo: Implement the actual handling of vga decode */
- return 0;
-}
diff --git a/drivers/gpu/drm/xe/display/xe_display_rpm.c b/drivers/gpu/drm/xe/display/xe_display_rpm.c
index 340f65884812..b3db40035499 100644
--- a/drivers/gpu/drm/xe/display/xe_display_rpm.c
+++ b/drivers/gpu/drm/xe/display/xe_display_rpm.c
@@ -9,6 +9,9 @@
#include "xe_device_types.h"
#include "xe_pm.h"
+/* -ENOENT means we got the ref, but there's no tracking */
+#define INTEL_WAKEREF_DEF ERR_PTR(-ENOENT)
+
static struct ref_tracker *xe_display_rpm_get(const struct drm_device *drm)
{
return xe_pm_runtime_resume_and_get(to_xe_device(drm)) ? INTEL_WAKEREF_DEF : NULL;
diff --git a/drivers/gpu/drm/xe/display/xe_dsb_buffer.c b/drivers/gpu/drm/xe/display/xe_dsb_buffer.c
index 58581d7aaae6..fa0acb11eaad 100644
--- a/drivers/gpu/drm/xe/display/xe_dsb_buffer.c
+++ b/drivers/gpu/drm/xe/display/xe_dsb_buffer.c
@@ -3,44 +3,49 @@
* Copyright 2023, Intel Corporation.
*/
-#include "i915_vma.h"
-#include "intel_display_types.h"
#include "intel_dsb_buffer.h"
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_device_types.h"
+struct intel_dsb_buffer {
+ u32 *cmd_buf;
+ struct xe_bo *bo;
+ size_t buf_size;
+};
+
u32 intel_dsb_buffer_ggtt_offset(struct intel_dsb_buffer *dsb_buf)
{
- return xe_bo_ggtt_addr(dsb_buf->vma->bo);
+ return xe_bo_ggtt_addr(dsb_buf->bo);
}
void intel_dsb_buffer_write(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val)
{
- iosys_map_wr(&dsb_buf->vma->bo->vmap, idx * 4, u32, val);
+ iosys_map_wr(&dsb_buf->bo->vmap, idx * 4, u32, val);
}
u32 intel_dsb_buffer_read(struct intel_dsb_buffer *dsb_buf, u32 idx)
{
- return iosys_map_rd(&dsb_buf->vma->bo->vmap, idx * 4, u32);
+ return iosys_map_rd(&dsb_buf->bo->vmap, idx * 4, u32);
}
void intel_dsb_buffer_memset(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val, size_t size)
{
WARN_ON(idx > (dsb_buf->buf_size - size) / sizeof(*dsb_buf->cmd_buf));
- iosys_map_memset(&dsb_buf->vma->bo->vmap, idx * 4, val, size);
+ iosys_map_memset(&dsb_buf->bo->vmap, idx * 4, val, size);
}
-bool intel_dsb_buffer_create(struct intel_crtc *crtc, struct intel_dsb_buffer *dsb_buf, size_t size)
+struct intel_dsb_buffer *intel_dsb_buffer_create(struct drm_device *drm, size_t size)
{
- struct xe_device *xe = to_xe_device(crtc->base.dev);
+ struct xe_device *xe = to_xe_device(drm);
+ struct intel_dsb_buffer *dsb_buf;
struct xe_bo *obj;
- struct i915_vma *vma;
+ int ret;
- vma = kzalloc(sizeof(*vma), GFP_KERNEL);
- if (!vma)
- return false;
+ dsb_buf = kzalloc(sizeof(*dsb_buf), GFP_KERNEL);
+ if (!dsb_buf)
+ return ERR_PTR(-ENOMEM);
/* Set scanout flag for WC mapping */
obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe),
@@ -49,26 +54,30 @@ bool intel_dsb_buffer_create(struct intel_crtc *crtc, struct intel_dsb_buffer *d
XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) |
XE_BO_FLAG_SCANOUT | XE_BO_FLAG_GGTT, false);
if (IS_ERR(obj)) {
- kfree(vma);
- return false;
+ ret = PTR_ERR(obj);
+ goto err_pin_map;
}
- vma->bo = obj;
- dsb_buf->vma = vma;
+ dsb_buf->bo = obj;
dsb_buf->buf_size = size;
- return true;
+ return dsb_buf;
+
+err_pin_map:
+ kfree(dsb_buf);
+
+ return ERR_PTR(ret);
}
void intel_dsb_buffer_cleanup(struct intel_dsb_buffer *dsb_buf)
{
- xe_bo_unpin_map_no_vm(dsb_buf->vma->bo);
- kfree(dsb_buf->vma);
+ xe_bo_unpin_map_no_vm(dsb_buf->bo);
+ kfree(dsb_buf);
}
void intel_dsb_buffer_flush_map(struct intel_dsb_buffer *dsb_buf)
{
- struct xe_device *xe = dsb_buf->vma->bo->tile->xe;
+ struct xe_device *xe = dsb_buf->bo->tile->xe;
/*
* The memory barrier here is to ensure coherency of DSB vs MMIO,
diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c
index 1fd4a815e784..d2c4e94180fa 100644
--- a/drivers/gpu/drm/xe/display/xe_fb_pin.c
+++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c
@@ -171,12 +171,13 @@ static int __xe_pin_fb_vma_dpt(const struct intel_framebuffer *fb,
}
static void
-write_ggtt_rotated(struct xe_bo *bo, struct xe_ggtt *ggtt, u32 *ggtt_ofs, u32 bo_ofs,
+write_ggtt_rotated(struct xe_ggtt *ggtt, u32 *ggtt_ofs,
+ u64 pte_flags,
+ xe_ggtt_set_pte_fn write_pte,
+ struct xe_bo *bo, u32 bo_ofs,
u32 width, u32 height, u32 src_stride, u32 dst_stride)
{
- struct xe_device *xe = xe_bo_device(bo);
u32 column, row;
- u64 pte = ggtt->pt_ops->pte_encode_flags(bo, xe->pat.idx[XE_CACHE_NONE]);
for (column = 0; column < width; column++) {
u32 src_idx = src_stride * (height - 1) + column + bo_ofs;
@@ -184,7 +185,7 @@ write_ggtt_rotated(struct xe_bo *bo, struct xe_ggtt *ggtt, u32 *ggtt_ofs, u32 bo
for (row = 0; row < height; row++) {
u64 addr = xe_bo_addr(bo, src_idx * XE_PAGE_SIZE, XE_PAGE_SIZE);
- ggtt->pt_ops->ggtt_set_pte(ggtt, *ggtt_ofs, pte | addr);
+ write_pte(ggtt, *ggtt_ofs, pte_flags | addr);
*ggtt_ofs += XE_PAGE_SIZE;
src_idx -= src_stride;
}
@@ -194,6 +195,28 @@ write_ggtt_rotated(struct xe_bo *bo, struct xe_ggtt *ggtt, u32 *ggtt_ofs, u32 bo
}
}
+struct fb_rotate_args {
+ const struct i915_gtt_view *view;
+ struct xe_bo *bo;
+};
+
+static void write_ggtt_rotated_node(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
+ u64 pte_flags, xe_ggtt_set_pte_fn write_pte, void *data)
+{
+ struct fb_rotate_args *args = data;
+ struct xe_bo *bo = args->bo;
+ const struct intel_rotation_info *rot_info = &args->view->rotated;
+ u32 ggtt_ofs = xe_ggtt_node_addr(node);
+
+ for (u32 i = 0; i < ARRAY_SIZE(rot_info->plane); i++)
+ write_ggtt_rotated(ggtt, &ggtt_ofs, pte_flags, write_pte,
+ bo, rot_info->plane[i].offset,
+ rot_info->plane[i].width,
+ rot_info->plane[i].height,
+ rot_info->plane[i].src_stride,
+ rot_info->plane[i].dst_stride);
+}
+
static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
const struct i915_gtt_view *view,
struct i915_vma *vma,
@@ -204,71 +227,43 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
struct xe_device *xe = to_xe_device(fb->base.dev);
struct xe_tile *tile0 = xe_device_get_root_tile(xe);
struct xe_ggtt *ggtt = tile0->mem.ggtt;
+ u64 pte, size;
u32 align;
- int ret;
+ int ret = 0;
/* TODO: Consider sharing framebuffer mapping?
* embed i915_vma inside intel_framebuffer
*/
- xe_pm_runtime_get_noresume(xe);
- ret = mutex_lock_interruptible(&ggtt->lock);
- if (ret)
- goto out;
+ guard(xe_pm_runtime_noresume)(xe);
align = XE_PAGE_SIZE;
- if (xe_bo_is_vram(bo) && ggtt->flags & XE_GGTT_FLAGS_64K)
- align = max_t(u32, align, SZ_64K);
+ if (xe_bo_is_vram(bo) && xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K)
+ align = max(align, SZ_64K);
+ /* Fast case, preallocated GGTT view? */
if (bo->ggtt_node[tile0->id] && view->type == I915_GTT_VIEW_NORMAL) {
vma->node = bo->ggtt_node[tile0->id];
- } else if (view->type == I915_GTT_VIEW_NORMAL) {
- vma->node = xe_ggtt_node_init(ggtt);
- if (IS_ERR(vma->node)) {
- ret = PTR_ERR(vma->node);
- goto out_unlock;
- }
-
- ret = xe_ggtt_node_insert_locked(vma->node, xe_bo_size(bo), align, 0);
- if (ret) {
- xe_ggtt_node_fini(vma->node);
- goto out_unlock;
- }
-
- xe_ggtt_map_bo(ggtt, vma->node, bo, xe->pat.idx[XE_CACHE_NONE]);
- } else {
- u32 i, ggtt_ofs;
- const struct intel_rotation_info *rot_info = &view->rotated;
-
- /* display seems to use tiles instead of bytes here, so convert it back.. */
- u32 size = intel_rotation_info_size(rot_info) * XE_PAGE_SIZE;
-
- vma->node = xe_ggtt_node_init(ggtt);
- if (IS_ERR(vma->node)) {
- ret = PTR_ERR(vma->node);
- goto out_unlock;
- }
-
- ret = xe_ggtt_node_insert_locked(vma->node, size, align, 0);
- if (ret) {
- xe_ggtt_node_fini(vma->node);
- goto out_unlock;
- }
-
- ggtt_ofs = vma->node->base.start;
-
- for (i = 0; i < ARRAY_SIZE(rot_info->plane); i++)
- write_ggtt_rotated(bo, ggtt, &ggtt_ofs,
- rot_info->plane[i].offset,
- rot_info->plane[i].width,
- rot_info->plane[i].height,
- rot_info->plane[i].src_stride,
- rot_info->plane[i].dst_stride);
+ return 0;
}
-out_unlock:
- mutex_unlock(&ggtt->lock);
-out:
- xe_pm_runtime_put(xe);
+ /* TODO: Consider sharing framebuffer mapping?
+ * embed i915_vma inside intel_framebuffer
+ */
+ if (view->type == I915_GTT_VIEW_NORMAL)
+ size = xe_bo_size(bo);
+ else
+ /* display uses tiles instead of bytes here, so convert it back.. */
+ size = intel_rotation_info_size(&view->rotated) * XE_PAGE_SIZE;
+
+ pte = xe_ggtt_encode_pte_flags(ggtt, bo, xe->pat.idx[XE_CACHE_NONE]);
+ vma->node = xe_ggtt_node_insert_transform(ggtt, bo, pte,
+ ALIGN(size, align), align,
+ view->type == I915_GTT_VIEW_NORMAL ?
+ NULL : write_ggtt_rotated_node,
+ &(struct fb_rotate_args){view, bo});
+ if (IS_ERR(vma->node))
+ ret = PTR_ERR(vma->node);
+
return ret;
}
@@ -358,7 +353,7 @@ static void __xe_unpin_fb_vma(struct i915_vma *vma)
if (vma->dpt)
xe_bo_unpin_map_no_vm(vma->dpt);
else if (!xe_ggtt_node_allocated(vma->bo->ggtt_node[tile_id]) ||
- vma->bo->ggtt_node[tile_id]->base.start != vma->node->base.start)
+ vma->bo->ggtt_node[tile_id] != vma->node)
xe_ggtt_node_remove(vma->node, false);
ttm_bo_reserve(&vma->bo->ttm, false, false, NULL);
@@ -378,7 +373,7 @@ intel_fb_pin_to_ggtt(const struct drm_framebuffer *fb,
{
*out_flags = 0;
- return __xe_pin_fb_vma(to_intel_framebuffer(fb), view, phys_alignment);
+ return __xe_pin_fb_vma(to_intel_framebuffer(fb), view, alignment);
}
void intel_fb_unpin_vma(struct i915_vma *vma, unsigned long flags)
diff --git a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
index 4ae847b628e2..ed1f65f5ef4d 100644
--- a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
+++ b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
@@ -3,18 +3,20 @@
* Copyright 2023, Intel Corporation.
*/
+#include <linux/delay.h>
+
#include <drm/drm_print.h>
+#include <drm/intel/display_parent_interface.h>
#include <drm/intel/i915_hdcp_interface.h>
-#include <linux/delay.h>
#include "abi/gsc_command_header_abi.h"
-#include "intel_hdcp_gsc.h"
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_force_wake.h"
#include "xe_gsc_proxy.h"
#include "xe_gsc_submit.h"
+#include "xe_hdcp_gsc.h"
#include "xe_map.h"
#include "xe_pm.h"
#include "xe_uc_fw.h"
@@ -30,37 +32,28 @@ struct intel_hdcp_gsc_context {
#define HDCP_GSC_HEADER_SIZE sizeof(struct intel_gsc_mtl_header)
-bool intel_hdcp_gsc_check_status(struct drm_device *drm)
+static bool intel_hdcp_gsc_check_status(struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
struct xe_tile *tile = xe_device_get_root_tile(xe);
struct xe_gt *gt = tile->media_gt;
struct xe_gsc *gsc = &gt->uc.gsc;
- bool ret = true;
- unsigned int fw_ref;
- if (!gsc || !xe_uc_fw_is_enabled(&gsc->fw)) {
+ if (!gsc || !xe_uc_fw_is_available(&gsc->fw)) {
drm_dbg_kms(&xe->drm,
"GSC Components not ready for HDCP2.x\n");
return false;
}
- xe_pm_runtime_get(xe);
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
- if (!fw_ref) {
+ guard(xe_pm_runtime)(xe);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC);
+ if (!fw_ref.domains) {
drm_dbg_kms(&xe->drm,
"failed to get forcewake to check proxy status\n");
- ret = false;
- goto out;
+ return false;
}
- if (!xe_gsc_proxy_init_done(gsc))
- ret = false;
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
-out:
- xe_pm_runtime_put(xe);
- return ret;
+ return xe_gsc_proxy_init_done(gsc);
}
/*This function helps allocate memory for the command that we will send to gsc cs */
@@ -96,7 +89,7 @@ out:
return ret;
}
-struct intel_hdcp_gsc_context *intel_hdcp_gsc_context_alloc(struct drm_device *drm)
+static struct intel_hdcp_gsc_context *intel_hdcp_gsc_context_alloc(struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
struct intel_hdcp_gsc_context *gsc_context;
@@ -120,7 +113,7 @@ struct intel_hdcp_gsc_context *intel_hdcp_gsc_context_alloc(struct drm_device *d
return gsc_context;
}
-void intel_hdcp_gsc_context_free(struct intel_hdcp_gsc_context *gsc_context)
+static void intel_hdcp_gsc_context_free(struct intel_hdcp_gsc_context *gsc_context)
{
if (!gsc_context)
return;
@@ -155,9 +148,9 @@ static int xe_gsc_send_sync(struct xe_device *xe,
return ret;
}
-ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_context,
- void *msg_in, size_t msg_in_len,
- void *msg_out, size_t msg_out_len)
+static ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_context,
+ void *msg_in, size_t msg_in_len,
+ void *msg_out, size_t msg_out_len)
{
struct xe_device *xe = gsc_context->xe;
const size_t max_msg_size = PAGE_SIZE - HDCP_GSC_HEADER_SIZE;
@@ -166,17 +159,15 @@ ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_context,
u32 addr_out_off, addr_in_wr_off = 0;
int ret, tries = 0;
- if (msg_in_len > max_msg_size || msg_out_len > max_msg_size) {
- ret = -ENOSPC;
- goto out;
- }
+ if (msg_in_len > max_msg_size || msg_out_len > max_msg_size)
+ return -ENOSPC;
msg_size_in = msg_in_len + HDCP_GSC_HEADER_SIZE;
msg_size_out = msg_out_len + HDCP_GSC_HEADER_SIZE;
addr_out_off = PAGE_SIZE;
host_session_id = xe_gsc_create_host_session_id();
- xe_pm_runtime_get_noresume(xe);
+ guard(xe_pm_runtime_noresume)(xe);
addr_in_wr_off = xe_gsc_emit_header(xe, &gsc_context->hdcp_bo->vmap,
addr_in_wr_off, HECI_MEADDRESS_HDCP,
host_session_id, msg_in_len);
@@ -201,13 +192,18 @@ ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_context,
} while (++tries < 20);
if (ret)
- goto out;
+ return ret;
xe_map_memcpy_from(xe, msg_out, &gsc_context->hdcp_bo->vmap,
addr_out_off + HDCP_GSC_HEADER_SIZE,
msg_out_len);
-out:
- xe_pm_runtime_put(xe);
return ret;
}
+
+const struct intel_display_hdcp_interface xe_display_hdcp_interface = {
+ .gsc_msg_send = intel_hdcp_gsc_msg_send,
+ .gsc_check_status = intel_hdcp_gsc_check_status,
+ .gsc_context_alloc = intel_hdcp_gsc_context_alloc,
+ .gsc_context_free = intel_hdcp_gsc_context_free,
+};
diff --git a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.h b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.h
new file mode 100644
index 000000000000..c1062e4b62f7
--- /dev/null
+++ b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2025 Intel Corporation */
+
+#ifndef __XE_HDCP_GSC_H__
+#define __XE_HDCP_GSC_H__
+
+extern const struct intel_display_hdcp_interface xe_display_hdcp_interface;
+
+#endif
diff --git a/drivers/gpu/drm/xe/display/xe_initial_plane.c b/drivers/gpu/drm/xe/display/xe_initial_plane.c
new file mode 100644
index 000000000000..4cfeafcc158d
--- /dev/null
+++ b/drivers/gpu/drm/xe/display/xe_initial_plane.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2021 Intel Corporation
+ */
+
+/* for ioread64 */
+#include <linux/io-64-nonatomic-lo-hi.h>
+
+#include <drm/intel/display_parent_interface.h>
+
+#include "regs/xe_gtt_defs.h"
+#include "xe_ggtt.h"
+#include "xe_mmio.h"
+
+#include "i915_vma.h"
+#include "intel_crtc.h"
+#include "intel_display_regs.h"
+#include "intel_display_types.h"
+#include "intel_fb.h"
+#include "intel_fb_pin.h"
+#include "xe_bo.h"
+#include "xe_vram_types.h"
+#include "xe_wa.h"
+
+#include <generated/xe_device_wa_oob.h>
+
+/* Early xe has no irq */
+static void xe_initial_plane_vblank_wait(struct drm_crtc *_crtc)
+{
+ struct intel_crtc *crtc = to_intel_crtc(_crtc);
+ struct xe_device *xe = to_xe_device(crtc->base.dev);
+ struct xe_reg pipe_frmtmstmp = XE_REG(i915_mmio_reg_offset(PIPE_FRMTMSTMP(crtc->pipe)));
+ u32 timestamp;
+ int ret;
+
+ timestamp = xe_mmio_read32(xe_root_tile_mmio(xe), pipe_frmtmstmp);
+
+ ret = xe_mmio_wait32_not(xe_root_tile_mmio(xe), pipe_frmtmstmp, ~0U, timestamp, 40000U, &timestamp, false);
+ if (ret < 0)
+ drm_warn(&xe->drm, "waiting for early vblank failed with %i\n", ret);
+}
+
+static struct xe_bo *
+initial_plane_bo(struct xe_device *xe,
+ struct intel_initial_plane_config *plane_config)
+{
+ struct xe_tile *tile0 = xe_device_get_root_tile(xe);
+ struct xe_bo *bo;
+ resource_size_t phys_base;
+ u32 base, size, flags;
+ u64 page_size = xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : SZ_4K;
+
+ if (plane_config->size == 0)
+ return NULL;
+
+ flags = XE_BO_FLAG_SCANOUT | XE_BO_FLAG_GGTT;
+
+ base = round_down(plane_config->base, page_size);
+ if (IS_DGFX(xe)) {
+ u64 pte = xe_ggtt_read_pte(tile0->mem.ggtt, base);
+
+ if (!(pte & XE_GGTT_PTE_DM)) {
+ drm_err(&xe->drm,
+ "Initial plane programming missing DM bit\n");
+ return NULL;
+ }
+
+ phys_base = pte & ~(page_size - 1);
+ flags |= XE_BO_FLAG_VRAM0;
+
+ /*
+ * We don't currently expect this to ever be placed in the
+ * stolen portion.
+ */
+ if (phys_base >= xe_vram_region_usable_size(tile0->mem.vram)) {
+ drm_err(&xe->drm,
+ "Initial plane programming using invalid range, phys_base=%pa\n",
+ &phys_base);
+ return NULL;
+ }
+
+ drm_dbg(&xe->drm,
+ "Using phys_base=%pa, based on initial plane programming\n",
+ &phys_base);
+ } else {
+ struct ttm_resource_manager *stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN);
+
+ if (!stolen)
+ return NULL;
+ phys_base = base;
+ flags |= XE_BO_FLAG_STOLEN;
+
+ if (XE_DEVICE_WA(xe, 22019338487_display))
+ return NULL;
+
+ /*
+ * If the FB is too big, just don't use it since fbdev is not very
+ * important and we should probably use that space with FBC or other
+ * features.
+ */
+ if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE) &&
+ plane_config->size * 2 >> PAGE_SHIFT >= stolen->size)
+ return NULL;
+ }
+
+ size = round_up(plane_config->base + plane_config->size,
+ page_size);
+ size -= base;
+
+ bo = xe_bo_create_pin_map_at_novm(xe, tile0, size, phys_base,
+ ttm_bo_type_kernel, flags, 0, false);
+ if (IS_ERR(bo)) {
+ drm_dbg(&xe->drm,
+ "Failed to create bo phys_base=%pa size %u with flags %x: %li\n",
+ &phys_base, size, flags, PTR_ERR(bo));
+ return NULL;
+ }
+
+ return bo;
+}
+
+static struct drm_gem_object *
+xe_alloc_initial_plane_obj(struct drm_device *drm,
+ struct intel_initial_plane_config *plane_config)
+{
+ struct xe_device *xe = to_xe_device(drm);
+ struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+ struct drm_framebuffer *fb = &plane_config->fb->base;
+ struct xe_bo *bo;
+
+ mode_cmd.pixel_format = fb->format->format;
+ mode_cmd.width = fb->width;
+ mode_cmd.height = fb->height;
+ mode_cmd.pitches[0] = fb->pitches[0];
+ mode_cmd.modifier[0] = fb->modifier;
+ mode_cmd.flags = DRM_MODE_FB_MODIFIERS;
+
+ bo = initial_plane_bo(xe, plane_config);
+ if (!bo)
+ return NULL;
+
+ if (intel_framebuffer_init(to_intel_framebuffer(fb),
+ &bo->ttm.base, fb->format, &mode_cmd)) {
+ drm_dbg_kms(&xe->drm, "intel fb init failed\n");
+ goto err_bo;
+ }
+ /* Reference handed over to fb */
+ xe_bo_put(bo);
+
+ return &bo->ttm.base;
+
+err_bo:
+ xe_bo_unpin_map_no_vm(bo);
+ return NULL;
+}
+
+static int
+xe_initial_plane_setup(struct drm_plane_state *_plane_state,
+ struct intel_initial_plane_config *plane_config,
+ struct drm_framebuffer *fb,
+ struct i915_vma *_unused)
+{
+ struct intel_plane_state *plane_state = to_intel_plane_state(_plane_state);
+ struct i915_vma *vma;
+
+ vma = intel_fb_pin_to_ggtt(fb, &plane_state->view.gtt,
+ 0, 0, 0, false, &plane_state->flags);
+ if (IS_ERR(vma))
+ return PTR_ERR(vma);
+
+ plane_state->ggtt_vma = vma;
+
+ plane_state->surf = i915_ggtt_offset(plane_state->ggtt_vma);
+
+ plane_config->vma = vma;
+
+ return 0;
+}
+
+static void xe_plane_config_fini(struct intel_initial_plane_config *plane_config)
+{
+}
+
+const struct intel_display_initial_plane_interface xe_display_initial_plane_interface = {
+ .vblank_wait = xe_initial_plane_vblank_wait,
+ .alloc_obj = xe_alloc_initial_plane_obj,
+ .setup = xe_initial_plane_setup,
+ .config_fini = xe_plane_config_fini,
+};
diff --git a/drivers/gpu/drm/xe/display/xe_initial_plane.h b/drivers/gpu/drm/xe/display/xe_initial_plane.h
new file mode 100644
index 000000000000..399d15f14441
--- /dev/null
+++ b/drivers/gpu/drm/xe/display/xe_initial_plane.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2025 Intel Corporation */
+
+#ifndef __XE_INITIAL_PLANE_H__
+#define __XE_INITIAL_PLANE_H__
+
+extern const struct intel_display_initial_plane_interface xe_display_initial_plane_interface;
+
+#endif
diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c
index df663286092a..e078494dc8ba 100644
--- a/drivers/gpu/drm/xe/display/xe_panic.c
+++ b/drivers/gpu/drm/xe/display/xe_panic.c
@@ -3,11 +3,12 @@
#include <drm/drm_cache.h>
#include <drm/drm_panic.h>
+#include <drm/intel/display_parent_interface.h>
#include "intel_display_types.h"
#include "intel_fb.h"
-#include "intel_panic.h"
#include "xe_bo.h"
+#include "xe_panic.h"
#include "xe_res_cursor.h"
struct intel_panic {
@@ -74,7 +75,7 @@ static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int
iosys_map_wr(&panic->vmap, offset, u32, color);
}
-struct intel_panic *intel_panic_alloc(void)
+static struct intel_panic *xe_panic_alloc(void)
{
struct intel_panic *panic;
@@ -83,7 +84,7 @@ struct intel_panic *intel_panic_alloc(void)
return panic;
}
-int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
+static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
{
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base));
@@ -96,7 +97,8 @@ int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
return 0;
}
-void intel_panic_finish(struct intel_panic *panic)
-{
- xe_panic_kunmap(panic);
-}
+const struct intel_display_panic_interface xe_display_panic_interface = {
+ .alloc = xe_panic_alloc,
+ .setup = xe_panic_setup,
+ .finish = xe_panic_kunmap,
+};
diff --git a/drivers/gpu/drm/xe/display/xe_panic.h b/drivers/gpu/drm/xe/display/xe_panic.h
new file mode 100644
index 000000000000..3054b511011b
--- /dev/null
+++ b/drivers/gpu/drm/xe/display/xe_panic.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2025 Intel Corporation */
+
+#ifndef __XE_PANIC_H__
+#define __XE_PANIC_H__
+
+extern const struct intel_display_panic_interface xe_display_panic_interface;
+
+#endif
diff --git a/drivers/gpu/drm/xe/display/xe_plane_initial.c b/drivers/gpu/drm/xe/display/xe_plane_initial.c
deleted file mode 100644
index 12d25c5290fd..000000000000
--- a/drivers/gpu/drm/xe/display/xe_plane_initial.c
+++ /dev/null
@@ -1,319 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2021 Intel Corporation
- */
-
-/* for ioread64 */
-#include <linux/io-64-nonatomic-lo-hi.h>
-
-#include "regs/xe_gtt_defs.h"
-#include "xe_ggtt.h"
-#include "xe_mmio.h"
-
-#include "i915_vma.h"
-#include "intel_crtc.h"
-#include "intel_display.h"
-#include "intel_display_core.h"
-#include "intel_display_regs.h"
-#include "intel_display_types.h"
-#include "intel_fb.h"
-#include "intel_fb_pin.h"
-#include "intel_frontbuffer.h"
-#include "intel_plane.h"
-#include "intel_plane_initial.h"
-#include "xe_bo.h"
-#include "xe_vram_types.h"
-#include "xe_wa.h"
-
-#include <generated/xe_device_wa_oob.h>
-
-void intel_plane_initial_vblank_wait(struct intel_crtc *crtc)
-{
- /* Early xe has no irq */
- struct xe_device *xe = to_xe_device(crtc->base.dev);
- struct xe_reg pipe_frmtmstmp = XE_REG(i915_mmio_reg_offset(PIPE_FRMTMSTMP(crtc->pipe)));
- u32 timestamp;
- int ret;
-
- timestamp = xe_mmio_read32(xe_root_tile_mmio(xe), pipe_frmtmstmp);
-
- ret = xe_mmio_wait32_not(xe_root_tile_mmio(xe), pipe_frmtmstmp, ~0U, timestamp, 40000U, &timestamp, false);
- if (ret < 0)
- drm_warn(&xe->drm, "waiting for early vblank failed with %i\n", ret);
-}
-
-static bool
-intel_reuse_initial_plane_obj(struct intel_crtc *this,
- const struct intel_initial_plane_config plane_configs[],
- struct drm_framebuffer **fb)
-{
- struct xe_device *xe = to_xe_device(this->base.dev);
- struct intel_crtc *crtc;
-
- for_each_intel_crtc(&xe->drm, crtc) {
- struct intel_plane *plane =
- to_intel_plane(crtc->base.primary);
- const struct intel_plane_state *plane_state =
- to_intel_plane_state(plane->base.state);
- const struct intel_crtc_state *crtc_state =
- to_intel_crtc_state(crtc->base.state);
-
- if (!crtc_state->uapi.active)
- continue;
-
- if (!plane_state->ggtt_vma)
- continue;
-
- if (plane_configs[this->pipe].base == plane_configs[crtc->pipe].base) {
- *fb = plane_state->hw.fb;
- return true;
- }
- }
-
- return false;
-}
-
-static struct xe_bo *
-initial_plane_bo(struct xe_device *xe,
- struct intel_initial_plane_config *plane_config)
-{
- struct xe_tile *tile0 = xe_device_get_root_tile(xe);
- struct xe_bo *bo;
- resource_size_t phys_base;
- u32 base, size, flags;
- u64 page_size = xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : SZ_4K;
-
- if (plane_config->size == 0)
- return NULL;
-
- flags = XE_BO_FLAG_SCANOUT | XE_BO_FLAG_GGTT;
-
- base = round_down(plane_config->base, page_size);
- if (IS_DGFX(xe)) {
- u64 pte = xe_ggtt_read_pte(tile0->mem.ggtt, base);
-
- if (!(pte & XE_GGTT_PTE_DM)) {
- drm_err(&xe->drm,
- "Initial plane programming missing DM bit\n");
- return NULL;
- }
-
- phys_base = pte & ~(page_size - 1);
- flags |= XE_BO_FLAG_VRAM0;
-
- /*
- * We don't currently expect this to ever be placed in the
- * stolen portion.
- */
- if (phys_base >= xe_vram_region_usable_size(tile0->mem.vram)) {
- drm_err(&xe->drm,
- "Initial plane programming using invalid range, phys_base=%pa\n",
- &phys_base);
- return NULL;
- }
-
- drm_dbg(&xe->drm,
- "Using phys_base=%pa, based on initial plane programming\n",
- &phys_base);
- } else {
- struct ttm_resource_manager *stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN);
-
- if (!stolen)
- return NULL;
- phys_base = base;
- flags |= XE_BO_FLAG_STOLEN;
-
- if (XE_DEVICE_WA(xe, 22019338487_display))
- return NULL;
-
- /*
- * If the FB is too big, just don't use it since fbdev is not very
- * important and we should probably use that space with FBC or other
- * features.
- */
- if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE) &&
- plane_config->size * 2 >> PAGE_SHIFT >= stolen->size)
- return NULL;
- }
-
- size = round_up(plane_config->base + plane_config->size,
- page_size);
- size -= base;
-
- bo = xe_bo_create_pin_map_at_novm(xe, tile0, size, phys_base,
- ttm_bo_type_kernel, flags, 0, false);
- if (IS_ERR(bo)) {
- drm_dbg(&xe->drm,
- "Failed to create bo phys_base=%pa size %u with flags %x: %li\n",
- &phys_base, size, flags, PTR_ERR(bo));
- return NULL;
- }
-
- return bo;
-}
-
-static bool
-intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
- struct intel_initial_plane_config *plane_config)
-{
- struct xe_device *xe = to_xe_device(crtc->base.dev);
- struct drm_mode_fb_cmd2 mode_cmd = { 0 };
- struct drm_framebuffer *fb = &plane_config->fb->base;
- struct xe_bo *bo;
-
- switch (fb->modifier) {
- case DRM_FORMAT_MOD_LINEAR:
- case I915_FORMAT_MOD_X_TILED:
- case I915_FORMAT_MOD_Y_TILED:
- case I915_FORMAT_MOD_4_TILED:
- break;
- default:
- drm_dbg_kms(&xe->drm,
- "Unsupported modifier for initial FB: 0x%llx\n",
- fb->modifier);
- return false;
- }
-
- mode_cmd.pixel_format = fb->format->format;
- mode_cmd.width = fb->width;
- mode_cmd.height = fb->height;
- mode_cmd.pitches[0] = fb->pitches[0];
- mode_cmd.modifier[0] = fb->modifier;
- mode_cmd.flags = DRM_MODE_FB_MODIFIERS;
-
- bo = initial_plane_bo(xe, plane_config);
- if (!bo)
- return false;
-
- if (intel_framebuffer_init(to_intel_framebuffer(fb),
- &bo->ttm.base, fb->format, &mode_cmd)) {
- drm_dbg_kms(&xe->drm, "intel fb init failed\n");
- goto err_bo;
- }
- /* Reference handed over to fb */
- xe_bo_put(bo);
-
- return true;
-
-err_bo:
- xe_bo_unpin_map_no_vm(bo);
- return false;
-}
-
-static void
-intel_find_initial_plane_obj(struct intel_crtc *crtc,
- struct intel_initial_plane_config plane_configs[])
-{
- struct intel_initial_plane_config *plane_config =
- &plane_configs[crtc->pipe];
- struct intel_plane *plane =
- to_intel_plane(crtc->base.primary);
- struct intel_plane_state *plane_state =
- to_intel_plane_state(plane->base.state);
- struct drm_framebuffer *fb;
- struct i915_vma *vma;
-
- /*
- * TODO:
- * Disable planes if get_initial_plane_config() failed.
- * Make sure things work if the surface base is not page aligned.
- */
- if (!plane_config->fb)
- return;
-
- if (intel_alloc_initial_plane_obj(crtc, plane_config))
- fb = &plane_config->fb->base;
- else if (!intel_reuse_initial_plane_obj(crtc, plane_configs, &fb))
- goto nofb;
-
- plane_state->uapi.rotation = plane_config->rotation;
- intel_fb_fill_view(to_intel_framebuffer(fb),
- plane_state->uapi.rotation, &plane_state->view);
-
- vma = intel_fb_pin_to_ggtt(fb, &plane_state->view.gtt,
- 0, 0, 0, false, &plane_state->flags);
- if (IS_ERR(vma))
- goto nofb;
-
- plane_state->ggtt_vma = vma;
-
- plane_state->surf = i915_ggtt_offset(plane_state->ggtt_vma);
-
- plane_state->uapi.src_x = 0;
- plane_state->uapi.src_y = 0;
- plane_state->uapi.src_w = fb->width << 16;
- plane_state->uapi.src_h = fb->height << 16;
-
- plane_state->uapi.crtc_x = 0;
- plane_state->uapi.crtc_y = 0;
- plane_state->uapi.crtc_w = fb->width;
- plane_state->uapi.crtc_h = fb->height;
-
- plane_state->uapi.fb = fb;
- drm_framebuffer_get(fb);
-
- plane_state->uapi.crtc = &crtc->base;
- intel_plane_copy_uapi_to_hw_state(plane_state, plane_state, crtc);
-
- atomic_or(plane->frontbuffer_bit, &to_intel_frontbuffer(fb)->bits);
-
- plane_config->vma = vma;
- return;
-
-nofb:
- /*
- * We've failed to reconstruct the BIOS FB. Current display state
- * indicates that the primary plane is visible, but has a NULL FB,
- * which will lead to problems later if we don't fix it up. The
- * simplest solution is to just disable the primary plane now and
- * pretend the BIOS never had it enabled.
- */
- intel_plane_disable_noatomic(crtc, plane);
-}
-
-static void plane_config_fini(struct intel_initial_plane_config *plane_config)
-{
- if (plane_config->fb) {
- struct drm_framebuffer *fb = &plane_config->fb->base;
-
- /* We may only have the stub and not a full framebuffer */
- if (drm_framebuffer_read_refcount(fb))
- drm_framebuffer_put(fb);
- else
- kfree(fb);
- }
-}
-
-void intel_initial_plane_config(struct intel_display *display)
-{
- struct intel_initial_plane_config plane_configs[I915_MAX_PIPES] = {};
- struct intel_crtc *crtc;
-
- for_each_intel_crtc(display->drm, crtc) {
- struct intel_initial_plane_config *plane_config =
- &plane_configs[crtc->pipe];
-
- if (!to_intel_crtc_state(crtc->base.state)->uapi.active)
- continue;
-
- /*
- * Note that reserving the BIOS fb up front prevents us
- * from stuffing other stolen allocations like the ring
- * on top. This prevents some ugliness at boot time, and
- * can even allow for smooth boot transitions if the BIOS
- * fb is large enough for the active pipe configuration.
- */
- display->funcs.display->get_initial_plane_config(crtc, plane_config);
-
- /*
- * If the fb is shared between multiple heads, we'll
- * just get the first one.
- */
- intel_find_initial_plane_obj(crtc, plane_configs);
-
- if (display->funcs.display->fixup_initial_plane_config(crtc, plane_config))
- intel_plane_initial_vblank_wait(crtc);
-
- plane_config_fini(plane_config);
- }
-}
diff --git a/drivers/gpu/drm/xe/display/xe_stolen.c b/drivers/gpu/drm/xe/display/xe_stolen.c
index 9f04ba36e930..8dc2f86ec602 100644
--- a/drivers/gpu/drm/xe/display/xe_stolen.c
+++ b/drivers/gpu/drm/xe/display/xe_stolen.c
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */
-#include "gem/i915_gem_stolen.h"
+#include <drm/intel/display_parent_interface.h>
+
#include "xe_res_cursor.h"
+#include "xe_stolen.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_validation.h"
@@ -11,8 +13,8 @@ struct intel_stolen_node {
struct xe_bo *bo;
};
-int i915_gem_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size,
- unsigned int align, u64 start, u64 end)
+static int xe_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size,
+ unsigned int align, u64 start, u64 end)
{
struct xe_device *xe = node->xe;
@@ -41,33 +43,25 @@ int i915_gem_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 siz
return err;
}
-int i915_gem_stolen_insert_node(struct intel_stolen_node *node, u64 size, unsigned int align)
-{
- /* Not used on xe */
- WARN_ON(1);
-
- return -ENODEV;
-}
-
-void i915_gem_stolen_remove_node(struct intel_stolen_node *node)
+static void xe_stolen_remove_node(struct intel_stolen_node *node)
{
xe_bo_unpin_map_no_vm(node->bo);
node->bo = NULL;
}
-bool i915_gem_stolen_initialized(struct drm_device *drm)
+static bool xe_stolen_initialized(struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
return ttm_manager_type(&xe->ttm, XE_PL_STOLEN);
}
-bool i915_gem_stolen_node_allocated(const struct intel_stolen_node *node)
+static bool xe_stolen_node_allocated(const struct intel_stolen_node *node)
{
return node->bo;
}
-u32 i915_gem_stolen_node_offset(struct intel_stolen_node *node)
+static u64 xe_stolen_node_offset(const struct intel_stolen_node *node)
{
struct xe_res_cursor res;
@@ -75,35 +69,19 @@ u32 i915_gem_stolen_node_offset(struct intel_stolen_node *node)
return res.start;
}
-/* Used for < gen4. These are not supported by Xe */
-u64 i915_gem_stolen_area_address(struct drm_device *drm)
-{
- WARN_ON(1);
-
- return 0;
-}
-
-/* Used for gen9 specific WA. Gen9 is not supported by Xe */
-u64 i915_gem_stolen_area_size(struct drm_device *drm)
-{
- WARN_ON(1);
-
- return 0;
-}
-
-u64 i915_gem_stolen_node_address(struct intel_stolen_node *node)
+static u64 xe_stolen_node_address(const struct intel_stolen_node *node)
{
struct xe_device *xe = node->xe;
- return xe_ttm_stolen_gpu_offset(xe) + i915_gem_stolen_node_offset(node);
+ return xe_ttm_stolen_gpu_offset(xe) + xe_stolen_node_offset(node);
}
-u64 i915_gem_stolen_node_size(const struct intel_stolen_node *node)
+static u64 xe_stolen_node_size(const struct intel_stolen_node *node)
{
- return node->bo->ttm.base.size;
+ return xe_bo_size(node->bo);
}
-struct intel_stolen_node *i915_gem_stolen_node_alloc(struct drm_device *drm)
+static struct intel_stolen_node *xe_stolen_node_alloc(struct drm_device *drm)
{
struct xe_device *xe = to_xe_device(drm);
struct intel_stolen_node *node;
@@ -117,7 +95,19 @@ struct intel_stolen_node *i915_gem_stolen_node_alloc(struct drm_device *drm)
return node;
}
-void i915_gem_stolen_node_free(const struct intel_stolen_node *node)
+static void xe_stolen_node_free(const struct intel_stolen_node *node)
{
kfree(node);
}
+
+const struct intel_display_stolen_interface xe_display_stolen_interface = {
+ .insert_node_in_range = xe_stolen_insert_node_in_range,
+ .remove_node = xe_stolen_remove_node,
+ .initialized = xe_stolen_initialized,
+ .node_allocated = xe_stolen_node_allocated,
+ .node_offset = xe_stolen_node_offset,
+ .node_address = xe_stolen_node_address,
+ .node_size = xe_stolen_node_size,
+ .node_alloc = xe_stolen_node_alloc,
+ .node_free = xe_stolen_node_free,
+};
diff --git a/drivers/gpu/drm/xe/display/xe_stolen.h b/drivers/gpu/drm/xe/display/xe_stolen.h
new file mode 100644
index 000000000000..db86b9e01242
--- /dev/null
+++ b/drivers/gpu/drm/xe/display/xe_stolen.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2025 Intel Corporation */
+
+#ifndef __XE_STOLEN_H__
+#define __XE_STOLEN_H__
+
+extern const struct intel_display_stolen_interface xe_display_stolen_interface;
+
+#endif
diff --git a/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
index 5d41ca297447..885fcf211e6d 100644
--- a/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
+++ b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
@@ -47,6 +47,7 @@
#define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|((len)-2))
+#define PIPE_CONTROL0_QUEUE_DRAIN_MODE BIT(12)
#define PIPE_CONTROL0_L3_READ_ONLY_CACHE_INVALIDATE BIT(10) /* gen12 */
#define PIPE_CONTROL0_HDC_PIPELINE_FLUSH BIT(9) /* gen12 */
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 917a088c28f2..24fc64fc832e 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -89,6 +89,7 @@
#define UNIFIED_COMPRESSION_FORMAT REG_GENMASK(3, 0)
#define XE2_GAMREQSTRM_CTRL XE_REG_MCR(0x4194)
+#define EN_CMP_1WCOH REG_BIT(15)
#define CG_DIS_CNTLBUS REG_BIT(6)
#define CCS_AUX_INV XE_REG(0x4208)
@@ -101,6 +102,11 @@
#define XE2_LMEM_CFG XE_REG(0x48b0)
+#define XE2_GAMWALK_CTRL 0x47e4
+#define XE2_GAMWALK_CTRL_MEDIA XE_REG(XE2_GAMWALK_CTRL + MEDIA_GT_GSI_OFFSET)
+#define XE2_GAMWALK_CTRL_3D XE_REG_MCR(XE2_GAMWALK_CTRL)
+#define EN_CMP_1WCOH_GW REG_BIT(14)
+
#define XEHP_FLAT_CCS_BASE_ADDR XE_REG_MCR(0x4910)
#define XEHP_FLAT_CCS_PTR REG_GENMASK(31, 8)
@@ -227,6 +233,9 @@
#define MIRROR_FUSE1 XE_REG(0x911c)
+#define FUSE2 XE_REG(0x9120)
+#define PRODUCTION_HW REG_BIT(2)
+
#define MIRROR_L3BANK_ENABLE XE_REG(0x9130)
#define XE3_L3BANK_ENABLE REG_GENMASK(31, 0)
diff --git a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
index 4389e5a76f89..4d83461e538b 100644
--- a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
@@ -9,6 +9,7 @@
#define XELPG_GGTT_PTE_PAT0 BIT_ULL(52)
#define XELPG_GGTT_PTE_PAT1 BIT_ULL(53)
+#define XE_PTE_ADDR_MASK GENMASK_ULL(51, 12)
#define GGTT_PTE_VFID GENMASK_ULL(11, 2)
#define GUC_GGTT_TOP 0xFEE00000
diff --git a/drivers/gpu/drm/xe/regs/xe_guc_regs.h b/drivers/gpu/drm/xe/regs/xe_guc_regs.h
index 2118f7dec287..87984713dd12 100644
--- a/drivers/gpu/drm/xe/regs/xe_guc_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_guc_regs.h
@@ -90,6 +90,9 @@
#define GUC_SEND_INTERRUPT XE_REG(0xc4c8)
#define GUC_SEND_TRIGGER REG_BIT(0)
+#define GUC_INTR_CHICKEN XE_REG(0xc50c)
+#define DISABLE_SIGNALING_ENGINES REG_BIT(1)
+
#define GUC_BCS_RCS_IER XE_REG(0xc550)
#define GUC_VCS2_VCS1_IER XE_REG(0xc554)
#define GUC_WD_VECS_IER XE_REG(0xc558)
diff --git a/drivers/gpu/drm/xe/regs/xe_irq_regs.h b/drivers/gpu/drm/xe/regs/xe_irq_regs.h
index 2f97662d958d..9d74f454d3ff 100644
--- a/drivers/gpu/drm/xe/regs/xe_irq_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_irq_regs.h
@@ -20,6 +20,7 @@
#define GU_MISC_IRQ REG_BIT(29)
#define ERROR_IRQ(x) REG_BIT(26 + (x))
#define DISPLAY_IRQ REG_BIT(16)
+#define SOC_H2DMEMINT_IRQ REG_BIT(13)
#define I2C_IRQ REG_BIT(12)
#define GT_DW_IRQ(x) REG_BIT(x)
diff --git a/drivers/gpu/drm/xe/regs/xe_mert_regs.h b/drivers/gpu/drm/xe/regs/xe_mert_regs.h
new file mode 100644
index 000000000000..99e5a26da657
--- /dev/null
+++ b/drivers/gpu/drm/xe/regs/xe_mert_regs.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_MERT_REGS_H_
+#define _XE_MERT_REGS_H_
+
+#include "regs/xe_reg_defs.h"
+
+#define MERT_LMEM_CFG XE_REG(0x1448b0)
+
+#define MERT_TLB_CT_INTR_ERR_ID_PORT XE_REG(0x145190)
+#define CATERR_VFID REG_GENMASK(16, 9)
+#define CATERR_CODES REG_GENMASK(5, 0)
+#define CATERR_NO_ERROR 0x00
+#define CATERR_UNMAPPED_GGTT 0x01
+#define CATERR_LMTT_FAULT 0x05
+
+#define MERT_TLB_INV_DESC_A XE_REG(0x14cf7c)
+#define MERT_TLB_INV_DESC_A_VALID REG_BIT(0)
+
+#endif
diff --git a/drivers/gpu/drm/xe/regs/xe_oa_regs.h b/drivers/gpu/drm/xe/regs/xe_oa_regs.h
index e693a50706f8..04a729e610aa 100644
--- a/drivers/gpu/drm/xe/regs/xe_oa_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_oa_regs.h
@@ -100,4 +100,21 @@
#define OAM_COMPRESSION_T3_CONTROL XE_REG(0x1c2e00)
#define OAM_LAT_MEASURE_ENABLE REG_BIT(4)
+/* Actual address is MEDIA_GT_GSI_OFFSET + the base addr below */
+#define XE_OAM_SAG_BASE 0x13000
+#define XE_OAM_SCMI_0_BASE 0x14000
+#define XE_OAM_SCMI_1_BASE 0x14800
+#define XE_OAM_SAG_BASE_ADJ (MEDIA_GT_GSI_OFFSET + XE_OAM_SAG_BASE)
+#define XE_OAM_SCMI_0_BASE_ADJ (MEDIA_GT_GSI_OFFSET + XE_OAM_SCMI_0_BASE)
+#define XE_OAM_SCMI_1_BASE_ADJ (MEDIA_GT_GSI_OFFSET + XE_OAM_SCMI_1_BASE)
+
+#define OAMERT_CONTROL XE_REG(0x1453a0)
+#define OAMERT_DEBUG XE_REG(0x1453a4)
+#define OAMERT_STATUS XE_REG(0x1453a8)
+#define OAMERT_HEAD_POINTER XE_REG(0x1453ac)
+#define OAMERT_TAIL_POINTER XE_REG(0x1453b0)
+#define OAMERT_BUFFER XE_REG(0x1453b4)
+#define OAMERT_CONTEXT_CONTROL XE_REG(0x1453c8)
+#define OAMERT_MMIO_TRG XE_REG(0x1453cc)
+
#endif
diff --git a/drivers/gpu/drm/xe/regs/xe_pcode_regs.h b/drivers/gpu/drm/xe/regs/xe_pcode_regs.h
index fb097607b86c..4b3c46eb858f 100644
--- a/drivers/gpu/drm/xe/regs/xe_pcode_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_pcode_regs.h
@@ -21,7 +21,10 @@
#define BMG_FAN_1_SPEED XE_REG(0x138140)
#define BMG_FAN_2_SPEED XE_REG(0x138170)
#define BMG_FAN_3_SPEED XE_REG(0x1381a0)
+#define BMG_VRAM_TEMPERATURE_N(n) XE_REG(0x138260 + ((n) * (sizeof(u32))))
#define BMG_VRAM_TEMPERATURE XE_REG(0x1382c0)
+#define TEMP_MASK_VRAM_N REG_GENMASK(30, 8)
+#define TEMP_SIGN_MASK REG_BIT(31)
#define BMG_PACKAGE_TEMPERATURE XE_REG(0x138434)
#endif /* _XE_PCODE_REGS_H_ */
diff --git a/drivers/gpu/drm/xe/regs/xe_pmt.h b/drivers/gpu/drm/xe/regs/xe_pmt.h
index 0f79c0714454..240d57993ea6 100644
--- a/drivers/gpu/drm/xe/regs/xe_pmt.h
+++ b/drivers/gpu/drm/xe/regs/xe_pmt.h
@@ -18,9 +18,6 @@
#define BMG_TELEMETRY_BASE_OFFSET 0xE0000
#define BMG_TELEMETRY_OFFSET (SOC_BASE + BMG_TELEMETRY_BASE_OFFSET)
-#define SG_REMAP_INDEX1 XE_REG(SOC_BASE + 0x08)
-#define SG_REMAP_BITS REG_GENMASK(31, 24)
-
#define BMG_MODS_RESIDENCY_OFFSET (0x4D0)
#define BMG_G2_RESIDENCY_OFFSET (0x530)
#define BMG_G6_RESIDENCY_OFFSET (0x538)
diff --git a/drivers/gpu/drm/xe/regs/xe_soc_remapper_regs.h b/drivers/gpu/drm/xe/regs/xe_soc_remapper_regs.h
new file mode 100644
index 000000000000..be0eb37e73ad
--- /dev/null
+++ b/drivers/gpu/drm/xe/regs/xe_soc_remapper_regs.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+#ifndef _XE_SOC_REMAPPER_REGS_H_
+#define _XE_SOC_REMAPPER_REGS_H_
+
+#include "xe_regs.h"
+
+#define SG_REMAP_INDEX1 XE_REG(SOC_BASE + 0x08)
+#define SG_REMAP_TELEM_MASK REG_GENMASK(31, 24)
+#define SG_REMAP_SYSCTRL_MASK REG_GENMASK(23, 16)
+
+#endif
diff --git a/drivers/gpu/drm/xe/tests/xe_args_test.c b/drivers/gpu/drm/xe/tests/xe_args_test.c
index f3fb23aa5d2e..2687a1b054dd 100644
--- a/drivers/gpu/drm/xe/tests/xe_args_test.c
+++ b/drivers/gpu/drm/xe/tests/xe_args_test.c
@@ -78,6 +78,24 @@ static void pick_arg_example(struct kunit *test)
#undef buz
}
+static void if_args_example(struct kunit *test)
+{
+ enum { Z = 1, Q };
+
+#define foo X, Y
+#define bar IF_ARGS(Z, Q, foo)
+#define buz IF_ARGS(Z, Q, DROP_FIRST_ARG(FIRST_ARG(foo)))
+
+ KUNIT_EXPECT_EQ(test, bar, Z);
+ KUNIT_EXPECT_EQ(test, buz, Q);
+ KUNIT_EXPECT_STREQ(test, __stringify(bar), "Z");
+ KUNIT_EXPECT_STREQ(test, __stringify(buz), "Q");
+
+#undef foo
+#undef bar
+#undef buz
+}
+
static void sep_comma_example(struct kunit *test)
{
#define foo(f) f(X) f(Y) f(Z) f(Q)
@@ -198,6 +216,40 @@ static void last_arg_test(struct kunit *test)
KUNIT_EXPECT_STREQ(test, __stringify(LAST_ARG(MAX_ARGS)), "-12");
}
+static void if_args_test(struct kunit *test)
+{
+ bool with_args = true;
+ bool no_args = false;
+ enum { X = 100 };
+
+ KUNIT_EXPECT_TRUE(test, IF_ARGS(true, false, FOO_ARGS));
+ KUNIT_EXPECT_FALSE(test, IF_ARGS(true, false, NO_ARGS));
+
+ KUNIT_EXPECT_TRUE(test, CONCATENATE(IF_ARGS(with, no, FOO_ARGS), _args));
+ KUNIT_EXPECT_FALSE(test, CONCATENATE(IF_ARGS(with, no, NO_ARGS), _args));
+
+ KUNIT_EXPECT_STREQ(test, __stringify(IF_ARGS(yes, no, FOO_ARGS)), "yes");
+ KUNIT_EXPECT_STREQ(test, __stringify(IF_ARGS(yes, no, NO_ARGS)), "no");
+
+ KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, FOO_ARGS), -1, FOO_ARGS), 4);
+ KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, FOO_ARGS), -1, NO_ARGS), -1);
+ KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, NO_ARGS), -1, FOO_ARGS), 0);
+ KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, NO_ARGS), -1, NO_ARGS), -1);
+
+ KUNIT_EXPECT_EQ(test,
+ CALL_ARGS(FIRST_ARG,
+ CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, FOO_ARGS), _ARGS)), X);
+ KUNIT_EXPECT_EQ(test,
+ CALL_ARGS(FIRST_ARG,
+ CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, NO_ARGS), _ARGS)), -1);
+ KUNIT_EXPECT_EQ(test,
+ CALL_ARGS(COUNT_ARGS,
+ CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, FOO_ARGS), _ARGS)), 4);
+ KUNIT_EXPECT_EQ(test,
+ CALL_ARGS(COUNT_ARGS,
+ CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, NO_ARGS), _ARGS)), 12);
+}
+
static struct kunit_case args_tests[] = {
KUNIT_CASE(count_args_test),
KUNIT_CASE(call_args_example),
@@ -209,6 +261,8 @@ static struct kunit_case args_tests[] = {
KUNIT_CASE(last_arg_example),
KUNIT_CASE(last_arg_test),
KUNIT_CASE(pick_arg_example),
+ KUNIT_CASE(if_args_example),
+ KUNIT_CASE(if_args_test),
KUNIT_CASE(sep_comma_example),
{}
};
diff --git a/drivers/gpu/drm/xe/tests/xe_bo.c b/drivers/gpu/drm/xe/tests/xe_bo.c
index 2294cf89f3e1..b7d8e45804cf 100644
--- a/drivers/gpu/drm/xe/tests/xe_bo.c
+++ b/drivers/gpu/drm/xe/tests/xe_bo.c
@@ -18,6 +18,7 @@
#include "tests/xe_test.h"
#include "xe_bo_evict.h"
+#include "xe_gt.h"
#include "xe_pci.h"
#include "xe_pm.h"
@@ -185,8 +186,7 @@ static int ccs_test_run_device(struct xe_device *xe)
return 0;
}
- xe_pm_runtime_get(xe);
-
+ guard(xe_pm_runtime)(xe);
for_each_tile(tile, xe, id) {
/* For igfx run only for primary tile */
if (!IS_DGFX(xe) && id > 0)
@@ -194,8 +194,6 @@ static int ccs_test_run_device(struct xe_device *xe)
ccs_test_run_tile(xe, tile, test);
}
- xe_pm_runtime_put(xe);
-
return 0;
}
@@ -356,13 +354,10 @@ static int evict_test_run_device(struct xe_device *xe)
return 0;
}
- xe_pm_runtime_get(xe);
-
+ guard(xe_pm_runtime)(xe);
for_each_tile(tile, xe, id)
evict_test_run_tile(xe, tile, test);
- xe_pm_runtime_put(xe);
-
return 0;
}
diff --git a/drivers/gpu/drm/xe/tests/xe_dma_buf.c b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
index c107687ef3c0..0be8440b3976 100644
--- a/drivers/gpu/drm/xe/tests/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
@@ -265,7 +265,7 @@ static int dma_buf_run_device(struct xe_device *xe)
const struct dma_buf_test_params *params;
struct kunit *test = kunit_get_current_test();
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
for (params = test_params; params->mem_mask; ++params) {
struct dma_buf_test_params p = *params;
@@ -273,7 +273,6 @@ static int dma_buf_run_device(struct xe_device *xe)
test->priv = &p;
xe_test_dmabuf_import_same_driver(xe);
}
- xe_pm_runtime_put(xe);
/* A non-zero return would halt iteration over driver devices */
return 0;
diff --git a/drivers/gpu/drm/xe/tests/xe_guc_buf_kunit.c b/drivers/gpu/drm/xe/tests/xe_guc_buf_kunit.c
index d266882adc0e..acddbedcf17c 100644
--- a/drivers/gpu/drm/xe/tests/xe_guc_buf_kunit.c
+++ b/drivers/gpu/drm/xe/tests/xe_guc_buf_kunit.c
@@ -67,7 +67,7 @@ static int guc_buf_test_init(struct kunit *test)
KUNIT_ASSERT_EQ(test, 0,
xe_ggtt_init_kunit(ggtt, DUT_GGTT_START,
- DUT_GGTT_START + DUT_GGTT_SIZE));
+ DUT_GGTT_SIZE));
kunit_activate_static_stub(test, xe_managed_bo_create_pin_map,
replacement_xe_managed_bo_create_pin_map);
diff --git a/drivers/gpu/drm/xe/tests/xe_migrate.c b/drivers/gpu/drm/xe/tests/xe_migrate.c
index 5904d658d1f2..34e2f0f4631f 100644
--- a/drivers/gpu/drm/xe/tests/xe_migrate.c
+++ b/drivers/gpu/drm/xe/tests/xe_migrate.c
@@ -344,8 +344,7 @@ static int migrate_test_run_device(struct xe_device *xe)
struct xe_tile *tile;
int id;
- xe_pm_runtime_get(xe);
-
+ guard(xe_pm_runtime)(xe);
for_each_tile(tile, xe, id) {
struct xe_migrate *m = tile->migrate;
struct drm_exec *exec = XE_VALIDATION_OPT_OUT;
@@ -356,8 +355,6 @@ static int migrate_test_run_device(struct xe_device *xe)
xe_vm_unlock(m->q->vm);
}
- xe_pm_runtime_put(xe);
-
return 0;
}
@@ -759,13 +756,10 @@ static int validate_ccs_test_run_device(struct xe_device *xe)
return 0;
}
- xe_pm_runtime_get(xe);
-
+ guard(xe_pm_runtime)(xe);
for_each_tile(tile, xe, id)
validate_ccs_test_run_tile(xe, tile, test);
- xe_pm_runtime_put(xe);
-
return 0;
}
diff --git a/drivers/gpu/drm/xe/tests/xe_mocs.c b/drivers/gpu/drm/xe/tests/xe_mocs.c
index 6bb278167aaf..daf3c6836c75 100644
--- a/drivers/gpu/drm/xe/tests/xe_mocs.c
+++ b/drivers/gpu/drm/xe/tests/xe_mocs.c
@@ -43,14 +43,12 @@ static void read_l3cc_table(struct xe_gt *gt,
{
struct kunit *test = kunit_get_current_test();
u32 l3cc, l3cc_expected;
- unsigned int fw_ref, i;
+ unsigned int i;
u32 reg_val;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) {
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
KUNIT_FAIL_AND_ABORT(test, "Forcewake Failed.\n");
- }
for (i = 0; i < info->num_mocs_regs; i++) {
if (!(i & 1)) {
@@ -74,7 +72,6 @@ static void read_l3cc_table(struct xe_gt *gt,
KUNIT_EXPECT_EQ_MSG(test, l3cc_expected, l3cc,
"l3cc idx=%u has incorrect val.\n", i);
}
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
static void read_mocs_table(struct xe_gt *gt,
@@ -82,14 +79,14 @@ static void read_mocs_table(struct xe_gt *gt,
{
struct kunit *test = kunit_get_current_test();
u32 mocs, mocs_expected;
- unsigned int fw_ref, i;
+ unsigned int i;
u32 reg_val;
KUNIT_EXPECT_TRUE_MSG(test, info->unused_entries_index,
"Unused entries index should have been defined\n");
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- KUNIT_ASSERT_NE_MSG(test, fw_ref, 0, "Forcewake Failed.\n");
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ KUNIT_ASSERT_NE_MSG(test, fw_ref.domains, 0, "Forcewake Failed.\n");
for (i = 0; i < info->num_mocs_regs; i++) {
if (regs_are_mcr(gt))
@@ -106,8 +103,6 @@ static void read_mocs_table(struct xe_gt *gt,
KUNIT_EXPECT_EQ_MSG(test, mocs_expected, mocs,
"mocs reg 0x%x has incorrect val.\n", i);
}
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
static int mocs_kernel_test_run_device(struct xe_device *xe)
@@ -120,8 +115,7 @@ static int mocs_kernel_test_run_device(struct xe_device *xe)
unsigned int flags;
int id;
- xe_pm_runtime_get(xe);
-
+ guard(xe_pm_runtime)(xe);
for_each_gt(gt, xe, id) {
flags = live_mocs_init(&mocs, gt);
if (flags & HAS_GLOBAL_MOCS)
@@ -130,8 +124,6 @@ static int mocs_kernel_test_run_device(struct xe_device *xe)
read_l3cc_table(gt, &mocs.table);
}
- xe_pm_runtime_put(xe);
-
return 0;
}
@@ -155,8 +147,7 @@ static int mocs_reset_test_run_device(struct xe_device *xe)
int id;
struct kunit *test = kunit_get_current_test();
- xe_pm_runtime_get(xe);
-
+ guard(xe_pm_runtime)(xe);
for_each_gt(gt, xe, id) {
flags = live_mocs_init(&mocs, gt);
kunit_info(test, "mocs_reset_test before reset\n");
@@ -174,8 +165,6 @@ static int mocs_reset_test_run_device(struct xe_device *xe)
read_l3cc_table(gt, &mocs.table);
}
- xe_pm_runtime_put(xe);
-
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_args.h b/drivers/gpu/drm/xe/xe_args.h
index 4dbc7e53c624..f550b5e3b993 100644
--- a/drivers/gpu/drm/xe/xe_args.h
+++ b/drivers/gpu/drm/xe/xe_args.h
@@ -122,6 +122,33 @@
#define PICK_ARG12(args...) PICK_ARG11(DROP_FIRST_ARG(args))
/**
+ * IF_ARGS() - Make selection based on optional argument list.
+ * @then: token to return if arguments are present
+ * @else: token to return if arguments are empty
+ * @...: arguments to check (optional)
+ *
+ * This macro allows to select a token based on the presence of the argument list.
+ *
+ * Example:
+ *
+ * #define foo X, Y
+ * #define bar IF_ARGS(Z, Q, foo)
+ * #define buz IF_ARGS(Z, Q, DROP_FIRST_ARG(FIRST_ARG(foo)))
+ *
+ * With above definitions bar expands to Z while buz expands to Q.
+ */
+#if defined(CONFIG_CC_IS_CLANG) || GCC_VERSION >= 100100
+#define IF_ARGS(then, else, ...) FIRST_ARG(__VA_OPT__(then,) else)
+#else
+#define IF_ARGS(then, else, ...) _IF_ARGS(then, else, CALL_ARGS(FIRST_ARG, __VA_ARGS__))
+#define _IF_ARGS(then, else, ...) __IF_ARGS(then, else, CALL_ARGS(COUNT_ARGS, __VA_ARGS__))
+#define __IF_ARGS(then, else, n) ___IF_ARGS(then, else, CALL_ARGS(CONCATENATE, ___IF_ARG, n))
+#define ___IF_ARGS(then, else, if) CALL_ARGS(if, then, else)
+#define ___IF_ARG1(then, else) then
+#define ___IF_ARG0(then, else) else
+#endif
+
+/**
* ARGS_SEP_COMMA - Definition of a comma character.
*
* This definition can be used in cases where any intermediate macro expects
diff --git a/drivers/gpu/drm/xe/xe_bb.c b/drivers/gpu/drm/xe/xe_bb.c
index 6d20229c11de..8b678297aaa2 100644
--- a/drivers/gpu/drm/xe/xe_bb.c
+++ b/drivers/gpu/drm/xe/xe_bb.c
@@ -7,10 +7,9 @@
#include "instructions/xe_mi_commands.h"
#include "xe_assert.h"
-#include "xe_device.h"
+#include "xe_device_types.h"
#include "xe_exec_queue_types.h"
#include "xe_gt.h"
-#include "xe_hw_fence.h"
#include "xe_sa.h"
#include "xe_sched_job.h"
#include "xe_vm_types.h"
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 94712b05edff..8bf16d60b9a5 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -26,9 +26,9 @@
#include "xe_dma_buf.h"
#include "xe_drm_client.h"
#include "xe_ggtt.h"
-#include "xe_gt.h"
#include "xe_map.h"
#include "xe_migrate.h"
+#include "xe_pat.h"
#include "xe_pm.h"
#include "xe_preempt_fence.h"
#include "xe_pxp.h"
@@ -516,8 +516,7 @@ static struct ttm_tt *xe_ttm_tt_create(struct ttm_buffer_object *ttm_bo,
* non-coherent and require a CPU:WC mapping.
*/
if ((!bo->cpu_caching && bo->flags & XE_BO_FLAG_SCANOUT) ||
- (xe->info.graphics_verx100 >= 1270 &&
- bo->flags & XE_BO_FLAG_PAGETABLE))
+ (!xe->info.has_cached_pt && bo->flags & XE_BO_FLAG_PAGETABLE))
caching = ttm_write_combined;
}
@@ -1055,6 +1054,7 @@ static long xe_bo_shrink_purge(struct ttm_operation_ctx *ctx,
unsigned long *scanned)
{
struct xe_device *xe = ttm_to_xe_device(bo->bdev);
+ struct ttm_tt *tt = bo->ttm;
long lret;
/* Fake move to system, without copying data. */
@@ -1079,8 +1079,10 @@ static long xe_bo_shrink_purge(struct ttm_operation_ctx *ctx,
.writeback = false,
.allow_move = false});
- if (lret > 0)
+ if (lret > 0) {
xe_ttm_tt_account_subtract(xe, bo->ttm);
+ update_global_total_pages(bo->bdev, -(long)tt->num_pages);
+ }
return lret;
}
@@ -1166,8 +1168,10 @@ long xe_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo,
if (needs_rpm)
xe_pm_runtime_put(xe);
- if (lret > 0)
+ if (lret > 0) {
xe_ttm_tt_account_subtract(xe, tt);
+ update_global_total_pages(bo->bdev, -(long)tt->num_pages);
+ }
out_unref:
xe_bo_put(xe_bo);
@@ -1527,7 +1531,7 @@ static bool xe_ttm_bo_lock_in_destructor(struct ttm_buffer_object *ttm_bo)
* always succeed here, as long as we hold the lru lock.
*/
spin_lock(&ttm_bo->bdev->lru_lock);
- locked = dma_resv_trylock(ttm_bo->base.resv);
+ locked = dma_resv_trylock(&ttm_bo->base._resv);
spin_unlock(&ttm_bo->bdev->lru_lock);
xe_assert(xe, locked);
@@ -1547,13 +1551,6 @@ static void xe_ttm_bo_release_notify(struct ttm_buffer_object *ttm_bo)
bo = ttm_to_xe_bo(ttm_bo);
xe_assert(xe_bo_device(bo), !(bo->created && kref_read(&ttm_bo->base.refcount)));
- /*
- * Corner case where TTM fails to allocate memory and this BOs resv
- * still points the VMs resv
- */
- if (ttm_bo->base.resv != &ttm_bo->base._resv)
- return;
-
if (!xe_ttm_bo_lock_in_destructor(ttm_bo))
return;
@@ -1563,14 +1560,14 @@ static void xe_ttm_bo_release_notify(struct ttm_buffer_object *ttm_bo)
* TODO: Don't do this for external bos once we scrub them after
* unbind.
*/
- dma_resv_for_each_fence(&cursor, ttm_bo->base.resv,
+ dma_resv_for_each_fence(&cursor, &ttm_bo->base._resv,
DMA_RESV_USAGE_BOOKKEEP, fence) {
if (xe_fence_is_xe_preempt(fence) &&
!dma_fence_is_signaled(fence)) {
if (!replacement)
replacement = dma_fence_get_stub();
- dma_resv_replace_fences(ttm_bo->base.resv,
+ dma_resv_replace_fences(&ttm_bo->base._resv,
fence->context,
replacement,
DMA_RESV_USAGE_BOOKKEEP);
@@ -1578,7 +1575,7 @@ static void xe_ttm_bo_release_notify(struct ttm_buffer_object *ttm_bo)
}
dma_fence_put(replacement);
- dma_resv_unlock(ttm_bo->base.resv);
+ dma_resv_unlock(&ttm_bo->base._resv);
}
static void xe_ttm_bo_delete_mem_notify(struct ttm_buffer_object *ttm_bo)
@@ -1717,7 +1714,7 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo)
xe_assert(xe, list_empty(&ttm_bo->base.gpuva.list));
for_each_tile(tile, xe, id)
- if (bo->ggtt_node[id] && bo->ggtt_node[id]->base.size)
+ if (bo->ggtt_node[id])
xe_ggtt_remove_bo(tile->mem.ggtt, bo);
#ifdef CONFIG_PROC_FS
@@ -2033,13 +2030,9 @@ static int xe_bo_vm_access(struct vm_area_struct *vma, unsigned long addr,
struct ttm_buffer_object *ttm_bo = vma->vm_private_data;
struct xe_bo *bo = ttm_to_xe_bo(ttm_bo);
struct xe_device *xe = xe_bo_device(bo);
- int ret;
- xe_pm_runtime_get(xe);
- ret = ttm_bo_vm_access(vma, addr, buf, len, write);
- xe_pm_runtime_put(xe);
-
- return ret;
+ guard(xe_pm_runtime)(xe);
+ return ttm_bo_vm_access(vma, addr, buf, len, write);
}
/**
@@ -3183,7 +3176,8 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data,
if (XE_IOCTL_DBG(xe, args->flags &
~(DRM_XE_GEM_CREATE_FLAG_DEFER_BACKING |
DRM_XE_GEM_CREATE_FLAG_SCANOUT |
- DRM_XE_GEM_CREATE_FLAG_NEEDS_VISIBLE_VRAM)))
+ DRM_XE_GEM_CREATE_FLAG_NEEDS_VISIBLE_VRAM |
+ DRM_XE_GEM_CREATE_FLAG_NO_COMPRESSION)))
return -EINVAL;
if (XE_IOCTL_DBG(xe, args->handle))
@@ -3205,6 +3199,12 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data,
if (args->flags & DRM_XE_GEM_CREATE_FLAG_SCANOUT)
bo_flags |= XE_BO_FLAG_SCANOUT;
+ if (args->flags & DRM_XE_GEM_CREATE_FLAG_NO_COMPRESSION) {
+ if (XE_IOCTL_DBG(xe, GRAPHICS_VER(xe) < 20))
+ return -EOPNOTSUPP;
+ bo_flags |= XE_BO_FLAG_NO_COMPRESSION;
+ }
+
bo_flags |= args->placement << (ffs(XE_BO_FLAG_SYSTEM) - 1);
/* CCS formats need physical placement at a 64K alignment in VRAM. */
@@ -3522,12 +3522,16 @@ bool xe_bo_needs_ccs_pages(struct xe_bo *bo)
if (IS_DGFX(xe) && (bo->flags & XE_BO_FLAG_SYSTEM))
return false;
+ /* Check if userspace explicitly requested no compression */
+ if (bo->flags & XE_BO_FLAG_NO_COMPRESSION)
+ return false;
+
/*
- * Compression implies coh_none, therefore we know for sure that WB
- * memory can't currently use compression, which is likely one of the
- * common cases.
+ * For WB (Write-Back) CPU caching mode, check if the device
+ * supports WB compression with coherency.
*/
- if (bo->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB)
+ if (bo->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB &&
+ xe->pat.idx[XE_CACHE_WB_COMPRESSION] == XE_PAT_INVALID_IDX)
return false;
return true;
@@ -3604,8 +3608,8 @@ void xe_bo_put(struct xe_bo *bo)
might_lock(&bo->client->bos_lock);
#endif
for_each_tile(tile, xe_bo_device(bo), id)
- if (bo->ggtt_node[id] && bo->ggtt_node[id]->ggtt)
- xe_ggtt_might_lock(bo->ggtt_node[id]->ggtt);
+ if (bo->ggtt_node[id])
+ xe_ggtt_might_lock(tile->mem.ggtt);
drm_gem_object_put(&bo->ttm.base);
}
}
diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index 911d5b90461a..c914ab719f20 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -9,6 +9,7 @@
#include <drm/ttm/ttm_tt.h>
#include "xe_bo_types.h"
+#include "xe_ggtt.h"
#include "xe_macros.h"
#include "xe_validation.h"
#include "xe_vm_types.h"
@@ -50,6 +51,7 @@
#define XE_BO_FLAG_GGTT3 BIT(23)
#define XE_BO_FLAG_CPU_ADDR_MIRROR BIT(24)
#define XE_BO_FLAG_FORCE_USER_VRAM BIT(25)
+#define XE_BO_FLAG_NO_COMPRESSION BIT(26)
/* this one is trigger internally only */
#define XE_BO_FLAG_INTERNAL_TEST BIT(30)
@@ -251,13 +253,14 @@ static inline u32
__xe_bo_ggtt_addr(struct xe_bo *bo, u8 tile_id)
{
struct xe_ggtt_node *ggtt_node = bo->ggtt_node[tile_id];
+ u64 offset;
if (XE_WARN_ON(!ggtt_node))
return 0;
- XE_WARN_ON(ggtt_node->base.size > xe_bo_size(bo));
- XE_WARN_ON(ggtt_node->base.start + ggtt_node->base.size > (1ull << 32));
- return ggtt_node->base.start;
+ offset = xe_ggtt_node_addr(ggtt_node);
+ XE_WARN_ON(offset + xe_bo_size(bo) > (1ull << 32));
+ return offset;
}
static inline u32
diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c
index e91da9589c5f..844cfafe1ec7 100644
--- a/drivers/gpu/drm/xe/xe_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_debugfs.c
@@ -68,7 +68,7 @@ static int info(struct seq_file *m, void *data)
struct xe_gt *gt;
u8 id;
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
drm_printf(&p, "graphics_verx100 %d\n", xe->info.graphics_verx100);
drm_printf(&p, "media_verx100 %d\n", xe->info.media_verx100);
@@ -93,9 +93,10 @@ static int info(struct seq_file *m, void *data)
xe_force_wake_ref(gt_to_fw(gt), XE_FW_GT));
drm_printf(&p, "gt%d engine_mask 0x%llx\n", id,
gt->info.engine_mask);
+ drm_printf(&p, "gt%d multi_queue_engine_class_mask 0x%x\n", id,
+ gt->info.multi_queue_engine_class_mask);
}
- xe_pm_runtime_put(xe);
return 0;
}
@@ -110,9 +111,8 @@ static int sriov_info(struct seq_file *m, void *data)
static int workarounds(struct xe_device *xe, struct drm_printer *p)
{
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
xe_wa_device_dump(xe, p);
- xe_pm_runtime_put(xe);
return 0;
}
@@ -134,7 +134,7 @@ static int dgfx_pkg_residencies_show(struct seq_file *m, void *data)
xe = node_to_xe(m->private);
p = drm_seq_file_printer(m);
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
mmio = xe_root_tile_mmio(xe);
static const struct {
u32 offset;
@@ -151,7 +151,6 @@ static int dgfx_pkg_residencies_show(struct seq_file *m, void *data)
for (int i = 0; i < ARRAY_SIZE(residencies); i++)
read_residency_counter(xe, mmio, residencies[i].offset, residencies[i].name, &p);
- xe_pm_runtime_put(xe);
return 0;
}
@@ -163,7 +162,7 @@ static int dgfx_pcie_link_residencies_show(struct seq_file *m, void *data)
xe = node_to_xe(m->private);
p = drm_seq_file_printer(m);
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
mmio = xe_root_tile_mmio(xe);
static const struct {
@@ -178,7 +177,6 @@ static int dgfx_pcie_link_residencies_show(struct seq_file *m, void *data)
for (int i = 0; i < ARRAY_SIZE(residencies); i++)
read_residency_counter(xe, mmio, residencies[i].offset, residencies[i].name, &p);
- xe_pm_runtime_put(xe);
return 0;
}
@@ -256,38 +254,81 @@ static ssize_t wedged_mode_show(struct file *f, char __user *ubuf,
return simple_read_from_buffer(ubuf, size, pos, buf, len);
}
+static int __wedged_mode_set_reset_policy(struct xe_gt *gt, enum xe_wedged_mode mode)
+{
+ bool enable_engine_reset;
+ int ret;
+
+ enable_engine_reset = (mode != XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET);
+ ret = xe_guc_ads_scheduler_policy_toggle_reset(&gt->uc.guc.ads,
+ enable_engine_reset);
+ if (ret)
+ xe_gt_err(gt, "Failed to update GuC ADS scheduler policy (%pe)\n", ERR_PTR(ret));
+
+ return ret;
+}
+
+static int wedged_mode_set_reset_policy(struct xe_device *xe, enum xe_wedged_mode mode)
+{
+ struct xe_gt *gt;
+ int ret;
+ u8 id;
+
+ guard(xe_pm_runtime)(xe);
+ for_each_gt(gt, xe, id) {
+ ret = __wedged_mode_set_reset_policy(gt, mode);
+ if (ret) {
+ if (id > 0) {
+ xe->wedged.inconsistent_reset = true;
+ drm_err(&xe->drm, "Inconsistent reset policy state between GTs\n");
+ }
+ return ret;
+ }
+ }
+
+ xe->wedged.inconsistent_reset = false;
+
+ return 0;
+}
+
+static bool wedged_mode_needs_policy_update(struct xe_device *xe, enum xe_wedged_mode mode)
+{
+ if (xe->wedged.inconsistent_reset)
+ return true;
+
+ if (xe->wedged.mode == mode)
+ return false;
+
+ if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET ||
+ mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)
+ return true;
+
+ return false;
+}
+
static ssize_t wedged_mode_set(struct file *f, const char __user *ubuf,
size_t size, loff_t *pos)
{
struct xe_device *xe = file_inode(f)->i_private;
- struct xe_gt *gt;
u32 wedged_mode;
ssize_t ret;
- u8 id;
ret = kstrtouint_from_user(ubuf, size, 0, &wedged_mode);
if (ret)
return ret;
- if (wedged_mode > 2)
- return -EINVAL;
+ ret = xe_device_validate_wedged_mode(xe, wedged_mode);
+ if (ret)
+ return ret;
- if (xe->wedged.mode == wedged_mode)
- return size;
+ if (wedged_mode_needs_policy_update(xe, wedged_mode)) {
+ ret = wedged_mode_set_reset_policy(xe, wedged_mode);
+ if (ret)
+ return ret;
+ }
xe->wedged.mode = wedged_mode;
- xe_pm_runtime_get(xe);
- for_each_gt(gt, xe, id) {
- ret = xe_guc_ads_scheduler_policy_toggle_reset(&gt->uc.guc.ads);
- if (ret) {
- xe_gt_err(gt, "Failed to update GuC ADS scheduler policy. GuC may still cause engine reset even with wedged_mode=2\n");
- xe_pm_runtime_put(xe);
- return -EIO;
- }
- }
- xe_pm_runtime_put(xe);
-
return size;
}
@@ -297,6 +338,39 @@ static const struct file_operations wedged_mode_fops = {
.write = wedged_mode_set,
};
+static ssize_t page_reclaim_hw_assist_show(struct file *f, char __user *ubuf,
+ size_t size, loff_t *pos)
+{
+ struct xe_device *xe = file_inode(f)->i_private;
+ char buf[8];
+ int len;
+
+ len = scnprintf(buf, sizeof(buf), "%d\n", xe->info.has_page_reclaim_hw_assist);
+ return simple_read_from_buffer(ubuf, size, pos, buf, len);
+}
+
+static ssize_t page_reclaim_hw_assist_set(struct file *f, const char __user *ubuf,
+ size_t size, loff_t *pos)
+{
+ struct xe_device *xe = file_inode(f)->i_private;
+ bool val;
+ ssize_t ret;
+
+ ret = kstrtobool_from_user(ubuf, size, &val);
+ if (ret)
+ return ret;
+
+ xe->info.has_page_reclaim_hw_assist = val;
+
+ return size;
+}
+
+static const struct file_operations page_reclaim_hw_assist_fops = {
+ .owner = THIS_MODULE,
+ .read = page_reclaim_hw_assist_show,
+ .write = page_reclaim_hw_assist_set,
+};
+
static ssize_t atomic_svm_timeslice_ms_show(struct file *f, char __user *ubuf,
size_t size, loff_t *pos)
{
@@ -332,6 +406,74 @@ static const struct file_operations atomic_svm_timeslice_ms_fops = {
.write = atomic_svm_timeslice_ms_set,
};
+static ssize_t min_run_period_lr_ms_show(struct file *f, char __user *ubuf,
+ size_t size, loff_t *pos)
+{
+ struct xe_device *xe = file_inode(f)->i_private;
+ char buf[32];
+ int len = 0;
+
+ len = scnprintf(buf, sizeof(buf), "%d\n", xe->min_run_period_lr_ms);
+
+ return simple_read_from_buffer(ubuf, size, pos, buf, len);
+}
+
+static ssize_t min_run_period_lr_ms_set(struct file *f, const char __user *ubuf,
+ size_t size, loff_t *pos)
+{
+ struct xe_device *xe = file_inode(f)->i_private;
+ u32 min_run_period_lr_ms;
+ ssize_t ret;
+
+ ret = kstrtouint_from_user(ubuf, size, 0, &min_run_period_lr_ms);
+ if (ret)
+ return ret;
+
+ xe->min_run_period_lr_ms = min_run_period_lr_ms;
+
+ return size;
+}
+
+static const struct file_operations min_run_period_lr_ms_fops = {
+ .owner = THIS_MODULE,
+ .read = min_run_period_lr_ms_show,
+ .write = min_run_period_lr_ms_set,
+};
+
+static ssize_t min_run_period_pf_ms_show(struct file *f, char __user *ubuf,
+ size_t size, loff_t *pos)
+{
+ struct xe_device *xe = file_inode(f)->i_private;
+ char buf[32];
+ int len = 0;
+
+ len = scnprintf(buf, sizeof(buf), "%d\n", xe->min_run_period_pf_ms);
+
+ return simple_read_from_buffer(ubuf, size, pos, buf, len);
+}
+
+static ssize_t min_run_period_pf_ms_set(struct file *f, const char __user *ubuf,
+ size_t size, loff_t *pos)
+{
+ struct xe_device *xe = file_inode(f)->i_private;
+ u32 min_run_period_pf_ms;
+ ssize_t ret;
+
+ ret = kstrtouint_from_user(ubuf, size, 0, &min_run_period_pf_ms);
+ if (ret)
+ return ret;
+
+ xe->min_run_period_pf_ms = min_run_period_pf_ms;
+
+ return size;
+}
+
+static const struct file_operations min_run_period_pf_ms_fops = {
+ .owner = THIS_MODULE,
+ .read = min_run_period_pf_ms_show,
+ .write = min_run_period_pf_ms_set,
+};
+
static ssize_t disable_late_binding_show(struct file *f, char __user *ubuf,
size_t size, loff_t *pos)
{
@@ -375,7 +517,6 @@ void xe_debugfs_register(struct xe_device *xe)
struct ttm_resource_manager *man;
struct xe_tile *tile;
struct xe_gt *gt;
- u32 mem_type;
u8 tile_id;
u8 id;
@@ -400,19 +541,22 @@ void xe_debugfs_register(struct xe_device *xe)
debugfs_create_file("atomic_svm_timeslice_ms", 0600, root, xe,
&atomic_svm_timeslice_ms_fops);
- debugfs_create_file("disable_late_binding", 0600, root, xe,
- &disable_late_binding_fops);
+ debugfs_create_file("min_run_period_lr_ms", 0600, root, xe,
+ &min_run_period_lr_ms_fops);
- for (mem_type = XE_PL_VRAM0; mem_type <= XE_PL_VRAM1; ++mem_type) {
- man = ttm_manager_type(bdev, mem_type);
+ debugfs_create_file("min_run_period_pf_ms", 0600, root, xe,
+ &min_run_period_pf_ms_fops);
- if (man) {
- char name[16];
+ debugfs_create_file("disable_late_binding", 0600, root, xe,
+ &disable_late_binding_fops);
- snprintf(name, sizeof(name), "vram%d_mm", mem_type - XE_PL_VRAM0);
- ttm_resource_manager_create_debugfs(man, root, name);
- }
- }
+ /*
+ * Don't expose page reclaim configuration file if not supported by the
+ * hardware initially.
+ */
+ if (xe->info.has_page_reclaim_hw_assist)
+ debugfs_create_file("page_reclaim_hw_assist", 0600, root, xe,
+ &page_reclaim_hw_assist_fops);
man = ttm_manager_type(bdev, XE_PL_TT);
ttm_resource_manager_create_debugfs(man, root, "gtt_mm");
diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c
index d444eda65ca6..cf41bb6d2172 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump.c
+++ b/drivers/gpu/drm/xe/xe_devcoredump.c
@@ -15,14 +15,13 @@
#include "xe_device.h"
#include "xe_exec_queue.h"
#include "xe_force_wake.h"
-#include "xe_gt.h"
#include "xe_gt_printk.h"
+#include "xe_gt_types.h"
#include "xe_guc_capture.h"
#include "xe_guc_ct.h"
#include "xe_guc_log.h"
#include "xe_guc_submit.h"
#include "xe_hw_engine.h"
-#include "xe_module.h"
#include "xe_pm.h"
#include "xe_sched_job.h"
#include "xe_vm.h"
@@ -276,7 +275,6 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
struct xe_devcoredump_snapshot *ss = container_of(work, typeof(*ss), work);
struct xe_devcoredump *coredump = container_of(ss, typeof(*coredump), snapshot);
struct xe_device *xe = coredump_to_xe(coredump);
- unsigned int fw_ref;
/*
* NB: Despite passing a GFP_ flags parameter here, more allocations are done
@@ -287,15 +285,15 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
xe_devcoredump_read, xe_devcoredump_free,
XE_COREDUMP_TIMEOUT_JIFFIES);
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
/* keep going if fw fails as we still want to save the memory and SW data */
- fw_ref = xe_force_wake_get(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
- xe_gt_info(ss->gt, "failed to get forcewake for coredump capture\n");
- xe_vm_snapshot_capture_delayed(ss->vm);
- xe_guc_exec_queue_snapshot_capture_delayed(ss->ge);
- xe_force_wake_put(gt_to_fw(ss->gt), fw_ref);
+ xe_with_force_wake(fw_ref, gt_to_fw(ss->gt), XE_FORCEWAKE_ALL) {
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
+ xe_gt_info(ss->gt, "failed to get forcewake for coredump capture\n");
+ xe_vm_snapshot_capture_delayed(ss->vm);
+ xe_guc_exec_queue_snapshot_capture_delayed(ss->ge);
+ }
ss->read.chunk_position = 0;
@@ -306,7 +304,7 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
ss->read.buffer = kvmalloc(XE_DEVCOREDUMP_CHUNK_MAX,
GFP_USER);
if (!ss->read.buffer)
- goto put_pm;
+ return;
__xe_devcoredump_read(ss->read.buffer,
XE_DEVCOREDUMP_CHUNK_MAX,
@@ -314,15 +312,12 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
} else {
ss->read.buffer = kvmalloc(ss->read.size, GFP_USER);
if (!ss->read.buffer)
- goto put_pm;
+ return;
__xe_devcoredump_read(ss->read.buffer, ss->read.size, 0,
coredump);
xe_devcoredump_snapshot_free(ss);
}
-
-put_pm:
- xe_pm_runtime_put(xe);
}
static void devcoredump_snapshot(struct xe_devcoredump *coredump,
@@ -332,7 +327,6 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
struct xe_devcoredump_snapshot *ss = &coredump->snapshot;
struct xe_guc *guc = exec_queue_to_guc(q);
const char *process_name = "no process";
- unsigned int fw_ref;
bool cookie;
ss->snapshot_time = ktime_get_real();
@@ -348,10 +342,10 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
ss->gt = q->gt;
INIT_WORK(&ss->work, xe_devcoredump_deferred_snap_work);
- cookie = dma_fence_begin_signalling();
-
/* keep going if fw fails as we still want to save the memory and SW data */
- fw_ref = xe_force_wake_get(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
+
+ cookie = dma_fence_begin_signalling();
ss->guc.log = xe_guc_log_snapshot_capture(&guc->log, true);
ss->guc.ct = xe_guc_ct_snapshot_capture(&guc->ct);
@@ -364,7 +358,6 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
queue_work(system_unbound_wq, &ss->work);
- xe_force_wake_put(gt_to_fw(q->gt), fw_ref);
dma_fence_end_signalling(cookie);
}
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index c7d373c70f0f..e575b02008dd 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -8,7 +8,6 @@
#include <linux/aperture.h>
#include <linux/delay.h>
#include <linux/fault-inject.h>
-#include <linux/iopoll.h>
#include <linux/units.h>
#include <drm/drm_atomic_helper.h>
@@ -16,6 +15,7 @@
#include <drm/drm_gem_ttm_helper.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_managed.h>
+#include <drm/drm_pagemap_util.h>
#include <drm/drm_print.h>
#include <uapi/drm/xe_drm.h>
@@ -35,7 +35,6 @@
#include "xe_exec_queue.h"
#include "xe_force_wake.h"
#include "xe_ggtt.h"
-#include "xe_gsc_proxy.h"
#include "xe_gt.h"
#include "xe_gt_mcr.h"
#include "xe_gt_printk.h"
@@ -61,8 +60,10 @@
#include "xe_pxp.h"
#include "xe_query.h"
#include "xe_shrinker.h"
+#include "xe_soc_remapper.h"
#include "xe_survivability_mode.h"
#include "xe_sriov.h"
+#include "xe_svm.h"
#include "xe_tile.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_ttm_sys_mgr.h"
@@ -166,7 +167,7 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file)
struct xe_exec_queue *q;
unsigned long idx;
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
/*
* No need for exec_queue.lock here as there is no contention for it
@@ -184,8 +185,6 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file)
xe_vm_close_and_put(vm);
xe_file_put(xef);
-
- xe_pm_runtime_put(xe);
}
static const struct drm_ioctl_desc xe_ioctls[] = {
@@ -209,6 +208,8 @@ static const struct drm_ioctl_desc xe_ioctls[] = {
DRM_IOCTL_DEF_DRV(XE_MADVISE, xe_vm_madvise_ioctl, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(XE_VM_QUERY_MEM_RANGE_ATTRS, xe_vm_query_vmas_attrs_ioctl,
DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XE_EXEC_QUEUE_SET_PROPERTY, xe_exec_queue_set_property_ioctl,
+ DRM_RENDER_ALLOW),
};
static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -220,10 +221,10 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (xe_device_wedged(xe))
return -ECANCELED;
- ret = xe_pm_runtime_get_ioctl(xe);
+ ACQUIRE(xe_pm_runtime_ioctl, pm)(xe);
+ ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &pm);
if (ret >= 0)
ret = drm_ioctl(file, cmd, arg);
- xe_pm_runtime_put(xe);
return ret;
}
@@ -238,10 +239,10 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
if (xe_device_wedged(xe))
return -ECANCELED;
- ret = xe_pm_runtime_get_ioctl(xe);
+ ACQUIRE(xe_pm_runtime_ioctl, pm)(xe);
+ ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &pm);
if (ret >= 0)
ret = drm_compat_ioctl(file, cmd, arg);
- xe_pm_runtime_put(xe);
return ret;
}
@@ -371,6 +372,20 @@ static const struct file_operations xe_driver_fops = {
.fop_flags = FOP_UNSIGNED_OFFSET,
};
+/**
+ * xe_is_xe_file() - Is the file an xe device file?
+ * @file: The file.
+ *
+ * Checks whether the file is opened against
+ * an xe device.
+ *
+ * Return: %true if an xe file, %false if not.
+ */
+bool xe_is_xe_file(const struct file *file)
+{
+ return file->f_op == &xe_driver_fops;
+}
+
static struct drm_driver driver = {
/* Don't use MTRRs here; the Xserver or userspace app should
* deal with them for Intel hardware.
@@ -455,6 +470,7 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
xe->info.revid = pdev->revision;
xe->info.force_execlist = xe_modparam.force_execlist;
xe->atomic_svm_timeslice_ms = 5;
+ xe->min_run_period_lr_ms = 5;
err = xe_irq_init(xe);
if (err)
@@ -466,6 +482,10 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
init_rwsem(&xe->usm.lock);
+ err = xe_pagemap_shrinker_create(xe);
+ if (err)
+ goto err;
+
xa_init_flags(&xe->usm.asid_to_vm, XA_FLAGS_ALLOC);
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
@@ -632,62 +652,14 @@ mask_err:
return err;
}
-static int lmem_initializing(struct xe_device *xe)
-{
- if (xe_mmio_read32(xe_root_tile_mmio(xe), GU_CNTL) & LMEM_INIT)
- return 0;
-
- if (signal_pending(current))
- return -EINTR;
-
- return 1;
-}
-
-static int wait_for_lmem_ready(struct xe_device *xe)
+static void assert_lmem_ready(struct xe_device *xe)
{
- const unsigned long TIMEOUT_SEC = 60;
- unsigned long prev_jiffies;
- int initializing;
-
- if (!IS_DGFX(xe))
- return 0;
-
- if (IS_SRIOV_VF(xe))
- return 0;
-
- if (!lmem_initializing(xe))
- return 0;
-
- drm_dbg(&xe->drm, "Waiting for lmem initialization\n");
- prev_jiffies = jiffies;
-
- /*
- * The boot firmware initializes local memory and
- * assesses its health. If memory training fails,
- * the punit will have been instructed to keep the GT powered
- * down.we won't be able to communicate with it
- *
- * If the status check is done before punit updates the register,
- * it can lead to the system being unusable.
- * use a timeout and defer the probe to prevent this.
- */
- poll_timeout_us(initializing = lmem_initializing(xe),
- initializing <= 0,
- 20 * USEC_PER_MSEC, TIMEOUT_SEC * USEC_PER_SEC, true);
- if (initializing < 0)
- return initializing;
-
- if (initializing) {
- drm_dbg(&xe->drm, "lmem not initialized by firmware\n");
- return -EPROBE_DEFER;
- }
-
- drm_dbg(&xe->drm, "lmem ready after %ums",
- jiffies_to_msecs(jiffies - prev_jiffies));
+ if (!IS_DGFX(xe) || IS_SRIOV_VF(xe))
+ return;
- return 0;
+ xe_assert(xe, xe_mmio_read32(xe_root_tile_mmio(xe), GU_CNTL) &
+ LMEM_INIT);
}
-ALLOW_ERROR_INJECTION(wait_for_lmem_ready, ERRNO); /* See xe_pci_probe() */
static void vf_update_device_info(struct xe_device *xe)
{
@@ -742,6 +714,11 @@ int xe_device_probe_early(struct xe_device *xe)
if (IS_SRIOV_VF(xe))
vf_update_device_info(xe);
+ /*
+ * Check for pcode uncore_init status to confirm if the SoC
+ * initialization is complete. Until done, any MMIO or lmem access from
+ * the driver will be blocked
+ */
err = xe_pcode_probe_early(xe);
if (err || xe_survivability_mode_is_requested(xe)) {
int save_err = err;
@@ -758,11 +735,17 @@ int xe_device_probe_early(struct xe_device *xe)
return save_err;
}
- err = wait_for_lmem_ready(xe);
- if (err)
- return err;
+ /*
+ * Make sure the lmem is initialized and ready to use. xe_pcode_ready()
+ * is flagged after full initialization is complete. Assert if lmem is
+ * not initialized.
+ */
+ assert_lmem_ready(xe);
- xe->wedged.mode = xe_modparam.wedged_mode;
+ xe->wedged.mode = xe_device_validate_wedged_mode(xe, xe_modparam.wedged_mode) ?
+ XE_WEDGED_MODE_DEFAULT : xe_modparam.wedged_mode;
+ drm_dbg(&xe->drm, "wedged_mode: setting mode (%u) %s\n",
+ xe->wedged.mode, xe_wedged_mode_to_string(xe->wedged.mode));
err = xe_device_vram_alloc(xe);
if (err)
@@ -775,7 +758,6 @@ ALLOW_ERROR_INJECTION(xe_device_probe_early, ERRNO); /* See xe_pci_probe() */
static int probe_has_flat_ccs(struct xe_device *xe)
{
struct xe_gt *gt;
- unsigned int fw_ref;
u32 reg;
/* Always enabled/disabled, no runtime check to do */
@@ -786,8 +768,8 @@ static int probe_has_flat_ccs(struct xe_device *xe)
if (!gt)
return 0;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
reg = xe_gt_mcr_unicast_read_any(gt, XE2_FLAT_CCS_BASE_RANGE_LOWER);
@@ -797,11 +779,64 @@ static int probe_has_flat_ccs(struct xe_device *xe)
drm_dbg(&xe->drm,
"Flat CCS has been disabled in bios, May lead to performance impact");
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
-
return 0;
}
+/*
+ * Detect if the driver is being run on pre-production hardware. We don't
+ * keep workarounds for pre-production hardware long term, so print an
+ * error and add taint if we're being loaded on a pre-production platform
+ * for which the pre-prod workarounds have already been removed.
+ *
+ * The general policy is that we'll remove any workarounds that only apply to
+ * pre-production hardware around the time force_probe restrictions are lifted
+ * for a platform of the next major IP generation (for example, Xe2 pre-prod
+ * workarounds should be removed around the time the first Xe3 platforms have
+ * force_probe lifted).
+ */
+static void detect_preproduction_hw(struct xe_device *xe)
+{
+ struct xe_gt *gt;
+ int id;
+
+ /*
+ * SR-IOV VFs don't have access to the FUSE2 register, so we can't
+ * check pre-production status there. But the host OS will notice
+ * and report the pre-production status, which should be enough to
+ * help us catch mistaken use of pre-production hardware.
+ */
+ if (IS_SRIOV_VF(xe))
+ return;
+
+ /*
+ * The "SW_CAP" fuse contains a bit indicating whether the device is a
+ * production or pre-production device. This fuse is reflected through
+ * the GT "FUSE2" register, even though the contents of the fuse are
+ * not GT-specific. Every GT's reflection of this fuse should show the
+ * same value, so we'll just use the first available GT for lookup.
+ */
+ for_each_gt(gt, xe, id)
+ break;
+
+ if (!gt)
+ return;
+
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT)) {
+ xe_gt_err(gt, "Forcewake failure; cannot determine production/pre-production hw status.\n");
+ return;
+ }
+
+ if (xe_mmio_read32(&gt->mmio, FUSE2) & PRODUCTION_HW)
+ return;
+
+ xe_info(xe, "Pre-production hardware detected.\n");
+ if (!xe->info.has_pre_prod_wa) {
+ xe_err(xe, "Pre-production workarounds for this platform have already been removed.\n");
+ add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK);
+ }
+}
+
int xe_device_probe(struct xe_device *xe)
{
struct xe_tile *tile;
@@ -911,6 +946,10 @@ int xe_device_probe(struct xe_device *xe)
xe_nvm_init(xe);
+ err = xe_soc_remapper_init(xe);
+ if (err)
+ return err;
+
err = xe_heci_gsc_init(xe);
if (err)
return err;
@@ -972,10 +1011,13 @@ int xe_device_probe(struct xe_device *xe)
if (err)
goto err_unregister_display;
+ detect_preproduction_hw(xe);
+
return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe);
err_unregister_display:
xe_display_unregister(xe);
+ drm_dev_unregister(&xe->drm);
return err;
}
@@ -1034,7 +1076,6 @@ void xe_device_wmb(struct xe_device *xe)
*/
static void tdf_request_sync(struct xe_device *xe)
{
- unsigned int fw_ref;
struct xe_gt *gt;
u8 id;
@@ -1042,8 +1083,8 @@ static void tdf_request_sync(struct xe_device *xe)
if (xe_gt_is_media_type(gt))
continue;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return;
xe_mmio_write32(&gt->mmio, XE2_TDF_CTRL, TRANSIENT_FLUSH_REQUEST);
@@ -1056,17 +1097,14 @@ static void tdf_request_sync(struct xe_device *xe)
* transient and need to be flushed..
*/
if (xe_mmio_wait32(&gt->mmio, XE2_TDF_CTRL, TRANSIENT_FLUSH_REQUEST, 0,
- 150, NULL, false))
+ 300, NULL, false))
xe_gt_err_once(gt, "TD flush timeout\n");
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
}
void xe_device_l2_flush(struct xe_device *xe)
{
struct xe_gt *gt;
- unsigned int fw_ref;
gt = xe_root_mmio_gt(xe);
if (!gt)
@@ -1075,8 +1113,8 @@ void xe_device_l2_flush(struct xe_device *xe)
if (!XE_GT_WA(gt, 16023588340))
return;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return;
spin_lock(&gt->global_invl_lock);
@@ -1086,8 +1124,6 @@ void xe_device_l2_flush(struct xe_device *xe)
xe_gt_err_once(gt, "Global invalidation timeout\n");
spin_unlock(&gt->global_invl_lock);
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
/**
@@ -1193,10 +1229,10 @@ static void xe_device_wedged_fini(struct drm_device *drm, void *arg)
* DOC: Xe Device Wedging
*
* Xe driver uses drm device wedged uevent as documented in Documentation/gpu/drm-uapi.rst.
- * When device is in wedged state, every IOCTL will be blocked and GT cannot be
- * used. Certain critical errors like gt reset failure, firmware failures can cause
- * the device to be wedged. The default recovery method for a wedged state
- * is rebind/bus-reset.
+ * When device is in wedged state, every IOCTL will be blocked and GT cannot
+ * be used. The conditions under which the driver declares the device wedged
+ * depend on the wedged mode configuration (see &enum xe_wedged_mode). The
+ * default recovery method for a wedged state is rebind/bus-reset.
*
* Another recovery method is vendor-specific. Below are the cases that send
* ``WEDGED=vendor-specific`` recovery method in drm device wedged uevent.
@@ -1261,7 +1297,7 @@ void xe_device_declare_wedged(struct xe_device *xe)
struct xe_gt *gt;
u8 id;
- if (xe->wedged.mode == 0) {
+ if (xe->wedged.mode == XE_WEDGED_MODE_NEVER) {
drm_dbg(&xe->drm, "Wedged mode is forcibly disabled\n");
return;
}
@@ -1295,3 +1331,48 @@ void xe_device_declare_wedged(struct xe_device *xe)
drm_dev_wedged_event(&xe->drm, xe->wedged.method, NULL);
}
}
+
+/**
+ * xe_device_validate_wedged_mode - Check if given mode is supported
+ * @xe: the &xe_device
+ * @mode: requested mode to validate
+ *
+ * Check whether the provided wedged mode is supported.
+ *
+ * Return: 0 if mode is supported, error code otherwise.
+ */
+int xe_device_validate_wedged_mode(struct xe_device *xe, unsigned int mode)
+{
+ if (mode > XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) {
+ drm_dbg(&xe->drm, "wedged_mode: invalid value (%u)\n", mode);
+ return -EINVAL;
+ } else if (mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET && (IS_SRIOV_VF(xe) ||
+ (IS_SRIOV_PF(xe) && !IS_ENABLED(CONFIG_DRM_XE_DEBUG)))) {
+ drm_dbg(&xe->drm, "wedged_mode: (%u) %s mode is not supported for %s\n",
+ mode, xe_wedged_mode_to_string(mode),
+ xe_sriov_mode_to_string(xe_device_sriov_mode(xe)));
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/**
+ * xe_wedged_mode_to_string - Convert enum value to string.
+ * @mode: the &xe_wedged_mode to convert
+ *
+ * Returns: wedged mode as a user friendly string.
+ */
+const char *xe_wedged_mode_to_string(enum xe_wedged_mode mode)
+{
+ switch (mode) {
+ case XE_WEDGED_MODE_NEVER:
+ return "never";
+ case XE_WEDGED_MODE_UPON_CRITICAL_ERROR:
+ return "upon-critical-error";
+ case XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET:
+ return "upon-any-hang-no-reset";
+ default:
+ return "<invalid>";
+ }
+}
diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
index 32cc6323b7f6..3740143790db 100644
--- a/drivers/gpu/drm/xe/xe_device.h
+++ b/drivers/gpu/drm/xe/xe_device.h
@@ -172,6 +172,11 @@ static inline bool xe_device_has_lmtt(struct xe_device *xe)
return IS_DGFX(xe);
}
+static inline bool xe_device_has_mert(struct xe_device *xe)
+{
+ return xe->info.has_mert;
+}
+
u32 xe_device_ccs_bytes(struct xe_device *xe, u64 size);
void xe_device_snapshot_print(struct xe_device *xe, struct drm_printer *p);
@@ -189,12 +194,16 @@ static inline bool xe_device_wedged(struct xe_device *xe)
void xe_device_set_wedged_method(struct xe_device *xe, unsigned long method);
void xe_device_declare_wedged(struct xe_device *xe);
+int xe_device_validate_wedged_mode(struct xe_device *xe, unsigned int mode);
+const char *xe_wedged_mode_to_string(enum xe_wedged_mode mode);
struct xe_file *xe_file_get(struct xe_file *xef);
void xe_file_put(struct xe_file *xef);
int xe_is_injection_active(void);
+bool xe_is_xe_file(const struct file *file);
+
/*
* Occasionally it is seen that the G2H worker starts running after a delay of more than
* a second even after being queued and activated by the Linux workqueue subsystem. This
diff --git a/drivers/gpu/drm/xe/xe_device_sysfs.c b/drivers/gpu/drm/xe/xe_device_sysfs.c
index ec9c06b06fb5..a73e0e957cb0 100644
--- a/drivers/gpu/drm/xe/xe_device_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_device_sysfs.c
@@ -57,9 +57,8 @@ vram_d3cold_threshold_store(struct device *dev, struct device_attribute *attr,
drm_dbg(&xe->drm, "vram_d3cold_threshold: %u\n", vram_d3cold_threshold);
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
ret = xe_pm_set_vram_threshold(xe, vram_d3cold_threshold);
- xe_pm_runtime_put(xe);
return ret ?: count;
}
@@ -84,33 +83,31 @@ lb_fan_control_version_show(struct device *dev, struct device_attribute *attr, c
u16 major = 0, minor = 0, hotfix = 0, build = 0;
int ret;
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
&cap, NULL);
if (ret)
- goto out;
+ return ret;
if (REG_FIELD_GET(V1_FAN_PROVISIONED, cap)) {
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0),
&ver_low, NULL);
if (ret)
- goto out;
+ return ret;
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0),
&ver_high, NULL);
if (ret)
- goto out;
+ return ret;
major = REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low);
minor = REG_FIELD_GET(MINOR_VERSION_MASK, ver_low);
hotfix = REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high);
build = REG_FIELD_GET(BUILD_VERSION_MASK, ver_high);
}
-out:
- xe_pm_runtime_put(xe);
- return ret ?: sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
+ return sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
}
static DEVICE_ATTR_ADMIN_RO(lb_fan_control_version);
@@ -123,33 +120,31 @@ lb_voltage_regulator_version_show(struct device *dev, struct device_attribute *a
u16 major = 0, minor = 0, hotfix = 0, build = 0;
int ret;
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
&cap, NULL);
if (ret)
- goto out;
+ return ret;
if (REG_FIELD_GET(VR_PARAMS_PROVISIONED, cap)) {
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0),
&ver_low, NULL);
if (ret)
- goto out;
+ return ret;
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0),
&ver_high, NULL);
if (ret)
- goto out;
+ return ret;
major = REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low);
minor = REG_FIELD_GET(MINOR_VERSION_MASK, ver_low);
hotfix = REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high);
build = REG_FIELD_GET(BUILD_VERSION_MASK, ver_high);
}
-out:
- xe_pm_runtime_put(xe);
- return ret ?: sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
+ return sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
}
static DEVICE_ATTR_ADMIN_RO(lb_voltage_regulator_version);
@@ -233,9 +228,8 @@ auto_link_downgrade_capable_show(struct device *dev, struct device_attribute *at
struct xe_device *xe = pdev_to_xe_device(pdev);
u32 cap, val;
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
val = xe_mmio_read32(xe_root_tile_mmio(xe), BMG_PCIE_CAP);
- xe_pm_runtime_put(xe);
cap = REG_FIELD_GET(LINK_DOWNGRADE, val);
return sysfs_emit(buf, "%u\n", cap == DOWNGRADE_CAPABLE);
@@ -251,11 +245,10 @@ auto_link_downgrade_status_show(struct device *dev, struct device_attribute *att
u32 val = 0;
int ret;
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
ret = xe_pcode_read(xe_device_get_root_tile(xe),
PCODE_MBOX(DGFX_PCODE_STATUS, DGFX_GET_INIT_STATUS, 0),
&val, NULL);
- xe_pm_runtime_put(xe);
return ret ?: sysfs_emit(buf, "%u\n", REG_FIELD_GET(DGFX_LINK_DOWNGRADE_STATUS, val));
}
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 0b2fa7c56d38..f689766adcb1 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -17,6 +17,7 @@
#include "xe_late_bind_fw_types.h"
#include "xe_lmtt_types.h"
#include "xe_memirq_types.h"
+#include "xe_mert.h"
#include "xe_oa_types.h"
#include "xe_pagefault_types.h"
#include "xe_platform_types.h"
@@ -36,6 +37,7 @@
#endif
struct dram_info;
+struct drm_pagemap_shrinker;
struct intel_display;
struct intel_dg_nvm_dev;
struct xe_ggtt;
@@ -44,6 +46,25 @@ struct xe_pat_ops;
struct xe_pxp;
struct xe_vram_region;
+/**
+ * enum xe_wedged_mode - possible wedged modes
+ * @XE_WEDGED_MODE_NEVER: Device will never be declared wedged.
+ * @XE_WEDGED_MODE_UPON_CRITICAL_ERROR: Device will be declared wedged only
+ * when critical error occurs like GT reset failure or firmware failure.
+ * This is the default mode.
+ * @XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET: Device will be declared wedged on
+ * any hang. In this mode, engine resets are disabled to avoid automatic
+ * recovery attempts. This mode is primarily intended for debugging hangs.
+ */
+enum xe_wedged_mode {
+ XE_WEDGED_MODE_NEVER = 0,
+ XE_WEDGED_MODE_UPON_CRITICAL_ERROR = 1,
+ XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET = 2,
+};
+
+#define XE_WEDGED_MODE_DEFAULT XE_WEDGED_MODE_UPON_CRITICAL_ERROR
+#define XE_WEDGED_MODE_DEFAULT_STR "upon-critical-error"
+
#define XE_BO_INVALID_OFFSET LONG_MAX
#define GRAPHICS_VER(xe) ((xe)->info.graphics_verx100 / 100)
@@ -184,6 +205,13 @@ struct xe_tile {
* Media GT shares a pool with its primary GT.
*/
struct xe_sa_manager *kernel_bb_pool;
+
+ /**
+ * @mem.reclaim_pool: Pool for PRLs allocated.
+ *
+ * Only main GT has page reclaim list allocations.
+ */
+ struct xe_sa_manager *reclaim_pool;
} mem;
/** @sriov: tile level virtualization data */
@@ -220,6 +248,9 @@ struct xe_tile {
/** @debugfs: debugfs directory associated with this tile */
struct dentry *debugfs;
+
+ /** @mert: MERT-related data */
+ struct xe_mert mert;
};
/**
@@ -286,6 +317,8 @@ struct xe_device {
u8 has_asid:1;
/** @info.has_atomic_enable_pte_bit: Device has atomic enable PTE bit */
u8 has_atomic_enable_pte_bit:1;
+ /** @info.has_cached_pt: Supports caching pagetable */
+ u8 has_cached_pt:1;
/** @info.has_device_atomics_on_smem: Supports device atomics on SMEM */
u8 has_device_atomics_on_smem:1;
/** @info.has_fan_control: Device supports fan control */
@@ -298,6 +331,8 @@ struct xe_device {
u8 has_heci_cscfi:1;
/** @info.has_heci_gscfi: device has heci gscfi */
u8 has_heci_gscfi:1;
+ /** @info.has_i2c: Device has I2C controller */
+ u8 has_i2c:1;
/** @info.has_late_bind: Device has firmware late binding support */
u8 has_late_bind:1;
/** @info.has_llc: Device has a shared CPU+GPU last level cache */
@@ -306,12 +341,24 @@ struct xe_device {
* pcode mailbox commands.
*/
u8 has_mbx_power_limits:1;
+ /** @info.has_mbx_thermal_info: Device supports thermal mailbox commands */
+ u8 has_mbx_thermal_info:1;
/** @info.has_mem_copy_instr: Device supports MEM_COPY instruction */
u8 has_mem_copy_instr:1;
+ /** @info.has_mert: Device has standalone MERT */
+ u8 has_mert:1;
+ /** @info.has_page_reclaim_hw_assist: Device supports page reclamation feature */
+ u8 has_page_reclaim_hw_assist:1;
+ /** @info.has_pre_prod_wa: Pre-production workarounds still present in driver */
+ u8 has_pre_prod_wa:1;
/** @info.has_pxp: Device has PXP support */
u8 has_pxp:1;
/** @info.has_range_tlb_inval: Has range based TLB invalidations */
u8 has_range_tlb_inval:1;
+ /** @info.has_soc_remapper_sysctrl: Has SoC remapper system controller */
+ u8 has_soc_remapper_sysctrl:1;
+ /** @info.has_soc_remapper_telem: Has SoC remapper telemetry support */
+ u8 has_soc_remapper_telem:1;
/** @info.has_sriov: Supports SR-IOV */
u8 has_sriov:1;
/** @info.has_usm: Device has unified shared memory support */
@@ -429,6 +476,10 @@ struct xe_device {
#define XE_PAGEFAULT_QUEUE_COUNT 4
/** @usm.pf_queue: Page fault queues */
struct xe_pagefault_queue pf_queue[XE_PAGEFAULT_QUEUE_COUNT];
+#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
+ /** @usm.pagemap_shrinker: Shrinker for unused pagemaps */
+ struct drm_pagemap_shrinker *dpagemap_shrinker;
+#endif
} usm;
/** @pinned: pinned BO state */
@@ -552,6 +603,18 @@ struct xe_device {
struct mutex lock;
} pmt;
+ /** @soc_remapper: SoC remapper object */
+ struct {
+ /** @soc_remapper.lock: Serialize access to SoC Remapper's index registers */
+ spinlock_t lock;
+
+ /** @soc_remapper.set_telem_region: Set telemetry index */
+ void (*set_telem_region)(struct xe_device *xe, u32 index);
+
+ /** @soc_remapper.set_sysctrl_region: Set system controller index */
+ void (*set_sysctrl_region)(struct xe_device *xe, u32 index);
+ } soc_remapper;
+
/**
* @pm_callback_task: Track the active task that is running in either
* the runtime_suspend or runtime_resume callbacks.
@@ -584,9 +647,11 @@ struct xe_device {
/** @wedged.flag: Xe device faced a critical error and is now blocked. */
atomic_t flag;
/** @wedged.mode: Mode controlled by kernel parameter and debugfs */
- int mode;
+ enum xe_wedged_mode mode;
/** @wedged.method: Recovery method to be sent in the drm device wedged uevent */
unsigned long method;
+ /** @wedged.inconsistent_reset: Inconsistent reset policy state between GTs */
+ bool inconsistent_reset;
} wedged;
/** @bo_device: Struct to control async free of BOs */
@@ -606,6 +671,12 @@ struct xe_device {
/** @atomic_svm_timeslice_ms: Atomic SVM fault timeslice MS */
u32 atomic_svm_timeslice_ms;
+ /** @min_run_period_lr_ms: LR VM (preempt fence mode) timeslice */
+ u32 min_run_period_lr_ms;
+
+ /** @min_run_period_pf_ms: LR VM (page fault mode) timeslice */
+ u32 min_run_period_pf_ms;
+
#ifdef TEST_VM_OPS_ERROR
/**
* @vm_inject_error_position: inject errors at different places in VM
@@ -648,13 +719,6 @@ struct xe_device {
* drm_i915_private during build. After cleanup these should go away,
* migrating to the right sub-structs
*/
- const struct dram_info *dram_info;
-
- /*
- * edram size in MB.
- * Cannot be determined by PCIID. You must always read a register.
- */
- u32 edram_size_mb;
struct intel_uncore {
spinlock_t lock;
diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
index 4e3a968bc6a0..ea370cd373e9 100644
--- a/drivers/gpu/drm/xe/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/xe_dma_buf.c
@@ -120,7 +120,7 @@ static struct sg_table *xe_dma_buf_map(struct dma_buf_attachment *attach,
case XE_PL_TT:
sgt = drm_prime_pages_to_sg(obj->dev,
bo->ttm.ttm->pages,
- bo->ttm.ttm->num_pages);
+ obj->size >> PAGE_SHIFT);
if (IS_ERR(sgt))
return sgt;
diff --git a/drivers/gpu/drm/xe/xe_drm_client.c b/drivers/gpu/drm/xe/xe_drm_client.c
index f931ff9b1ec0..2787bbb36141 100644
--- a/drivers/gpu/drm/xe/xe_drm_client.c
+++ b/drivers/gpu/drm/xe/xe_drm_client.c
@@ -285,32 +285,31 @@ static struct xe_hw_engine *any_engine(struct xe_device *xe)
return NULL;
}
-static bool force_wake_get_any_engine(struct xe_device *xe,
- struct xe_hw_engine **phwe,
- unsigned int *pfw_ref)
+/*
+ * Pick any engine and grab its forcewake. On error phwe will be NULL and
+ * the returned forcewake reference will be invalid. Callers should check
+ * phwe against NULL.
+ */
+static struct xe_force_wake_ref force_wake_get_any_engine(struct xe_device *xe,
+ struct xe_hw_engine **phwe)
{
enum xe_force_wake_domains domain;
- unsigned int fw_ref;
+ struct xe_force_wake_ref fw_ref = {};
struct xe_hw_engine *hwe;
- struct xe_force_wake *fw;
+
+ *phwe = NULL;
hwe = any_engine(xe);
if (!hwe)
- return false;
+ return fw_ref; /* will be invalid */
domain = xe_hw_engine_to_fw_domain(hwe);
- fw = gt_to_fw(hwe->gt);
-
- fw_ref = xe_force_wake_get(fw, domain);
- if (!xe_force_wake_ref_has_domain(fw_ref, domain)) {
- xe_force_wake_put(fw, fw_ref);
- return false;
- }
- *phwe = hwe;
- *pfw_ref = fw_ref;
+ fw_ref = xe_force_wake_constructor(gt_to_fw(hwe->gt), domain);
+ if (xe_force_wake_ref_has_domain(fw_ref.domains, domain))
+ *phwe = hwe; /* valid forcewake */
- return true;
+ return fw_ref;
}
static void show_run_ticks(struct drm_printer *p, struct drm_file *file)
@@ -322,7 +321,6 @@ static void show_run_ticks(struct drm_printer *p, struct drm_file *file)
struct xe_hw_engine *hwe;
struct xe_exec_queue *q;
u64 gpu_timestamp;
- unsigned int fw_ref;
/*
* RING_TIMESTAMP registers are inaccessible in VF mode.
@@ -339,29 +337,26 @@ static void show_run_ticks(struct drm_printer *p, struct drm_file *file)
wait_var_event(&xef->exec_queue.pending_removal,
!atomic_read(&xef->exec_queue.pending_removal));
- xe_pm_runtime_get(xe);
- if (!force_wake_get_any_engine(xe, &hwe, &fw_ref)) {
- xe_pm_runtime_put(xe);
- return;
- }
-
- /* Accumulate all the exec queues from this client */
- mutex_lock(&xef->exec_queue.lock);
- xa_for_each(&xef->exec_queue.xa, i, q) {
- xe_exec_queue_get(q);
- mutex_unlock(&xef->exec_queue.lock);
-
- xe_exec_queue_update_run_ticks(q);
+ scoped_guard(xe_pm_runtime, xe) {
+ CLASS(xe_force_wake_release_only, fw_ref)(force_wake_get_any_engine(xe, &hwe));
+ if (!hwe)
+ return;
+ /* Accumulate all the exec queues from this client */
mutex_lock(&xef->exec_queue.lock);
- xe_exec_queue_put(q);
- }
- mutex_unlock(&xef->exec_queue.lock);
+ xa_for_each(&xef->exec_queue.xa, i, q) {
+ xe_exec_queue_get(q);
+ mutex_unlock(&xef->exec_queue.lock);
- gpu_timestamp = xe_hw_engine_read_timestamp(hwe);
+ xe_exec_queue_update_run_ticks(q);
- xe_force_wake_put(gt_to_fw(hwe->gt), fw_ref);
- xe_pm_runtime_put(xe);
+ mutex_lock(&xef->exec_queue.lock);
+ xe_exec_queue_put(q);
+ }
+ mutex_unlock(&xef->exec_queue.lock);
+
+ gpu_timestamp = xe_hw_engine_read_timestamp(hwe);
+ }
for (class = 0; class < XE_ENGINE_CLASS_MAX; class++) {
const char *class_name;
diff --git a/drivers/gpu/drm/xe/xe_eu_stall.c b/drivers/gpu/drm/xe/xe_eu_stall.c
index 97dfb7945b7a..a5c36a317a70 100644
--- a/drivers/gpu/drm/xe/xe_eu_stall.c
+++ b/drivers/gpu/drm/xe/xe_eu_stall.c
@@ -315,7 +315,7 @@ static int xe_eu_stall_user_ext_set_property(struct xe_device *xe, u64 extension
return -EFAULT;
if (XE_IOCTL_DBG(xe, ext.property >= ARRAY_SIZE(xe_set_eu_stall_property_funcs)) ||
- XE_IOCTL_DBG(xe, ext.pad))
+ XE_IOCTL_DBG(xe, !ext.property) || XE_IOCTL_DBG(xe, ext.pad))
return -EINVAL;
idx = array_index_nospec(ext.property, ARRAY_SIZE(xe_set_eu_stall_property_funcs));
diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c
index 4d81210e41f5..a5485fe6e3f1 100644
--- a/drivers/gpu/drm/xe/xe_exec.c
+++ b/drivers/gpu/drm/xe/xe_exec.c
@@ -11,7 +11,6 @@
#include <uapi/drm/xe_drm.h>
#include <linux/delay.h>
-#include "xe_bo.h"
#include "xe_device.h"
#include "xe_exec_queue.h"
#include "xe_hw_engine_group.h"
@@ -121,7 +120,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
u64 addresses[XE_HW_ENGINE_MAX_INSTANCE];
struct drm_gpuvm_exec vm_exec = {.extra.fn = xe_exec_fn};
struct drm_exec *exec = &vm_exec.exec;
- u32 i, num_syncs, num_ufence = 0;
+ u32 i, num_syncs, num_in_sync = 0, num_ufence = 0;
struct xe_validation_ctx ctx;
struct xe_sched_job *job;
struct xe_vm *vm;
@@ -132,7 +131,8 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
if (XE_IOCTL_DBG(xe, args->extensions) ||
XE_IOCTL_DBG(xe, args->pad[0] || args->pad[1] || args->pad[2]) ||
- XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]))
+ XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]) ||
+ XE_IOCTL_DBG(xe, args->num_syncs > DRM_XE_MAX_SYNCS))
return -EINVAL;
q = xe_exec_queue_lookup(xef, args->exec_queue_id);
@@ -182,6 +182,9 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
if (xe_sync_is_ufence(&syncs[num_syncs]))
num_ufence++;
+
+ if (!num_in_sync && xe_sync_needs_wait(&syncs[num_syncs]))
+ num_in_sync++;
}
if (XE_IOCTL_DBG(xe, num_ufence > 1)) {
@@ -202,7 +205,9 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
mode = xe_hw_engine_group_find_exec_mode(q);
if (mode == EXEC_MODE_DMA_FENCE) {
- err = xe_hw_engine_group_get_mode(group, mode, &previous_mode);
+ err = xe_hw_engine_group_get_mode(group, mode, &previous_mode,
+ syncs, num_in_sync ?
+ num_syncs : 0);
if (err)
goto err_syncs;
}
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 8724f8de67e2..9b6311f7fd4f 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -13,19 +13,19 @@
#include <drm/drm_syncobj.h>
#include <uapi/drm/xe_drm.h>
+#include "xe_bo.h"
#include "xe_dep_scheduler.h"
#include "xe_device.h"
#include "xe_gt.h"
+#include "xe_gt_sriov_pf.h"
#include "xe_gt_sriov_vf.h"
#include "xe_hw_engine_class_sysfs.h"
#include "xe_hw_engine_group.h"
-#include "xe_hw_fence.h"
#include "xe_irq.h"
#include "xe_lrc.h"
#include "xe_macros.h"
#include "xe_migrate.h"
#include "xe_pm.h"
-#include "xe_ring_ops_types.h"
#include "xe_trace.h"
#include "xe_vm.h"
#include "xe_pxp.h"
@@ -53,6 +53,53 @@
* the ring operations the different engine classes support.
*/
+/**
+ * DOC: Multi Queue Group
+ *
+ * Multi Queue Group is another mode of execution supported by the compute
+ * and blitter copy command streamers (CCS and BCS, respectively). It is
+ * an enhancement of the existing hardware architecture and leverages the
+ * same submission model. It enables support for efficient, parallel
+ * execution of multiple queues within a single shared context. The multi
+ * queue group functionality is only supported with GuC submission backend.
+ * All the queues of a group must use the same address space (VM).
+ *
+ * The DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE execution queue property
+ * supports creating a multi queue group and adding queues to a queue group.
+ *
+ * The XE_EXEC_QUEUE_CREATE ioctl call with above property with value field
+ * set to DRM_XE_MULTI_GROUP_CREATE, will create a new multi queue group with
+ * the queue being created as the primary queue (aka q0) of the group. To add
+ * secondary queues to the group, they need to be created with the above
+ * property with id of the primary queue as the value. The properties of
+ * the primary queue (like priority, time slice) applies to the whole group.
+ * So, these properties can't be set for secondary queues of a group.
+ *
+ * The hardware does not support removing a queue from a multi-queue group.
+ * However, queues can be dynamically added to the group. A group can have
+ * up to 64 queues. To support this, XeKMD holds references to LRCs of the
+ * queues even after the queues are destroyed by the user until the whole
+ * group is destroyed. The secondary queues hold a reference to the primary
+ * queue thus preventing the group from being destroyed when user destroys
+ * the primary queue. Once the primary queue is destroyed, secondary queues
+ * can't be added to the queue group and new job submissions on existing
+ * secondary queues are not allowed.
+ *
+ * The queues of a multi queue group can set their priority within the group
+ * through the DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY property.
+ * This multi queue priority can also be set dynamically through the
+ * XE_EXEC_QUEUE_SET_PROPERTY ioctl. This is the only other property
+ * supported by the secondary queues of a multi queue group, other than
+ * DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE.
+ *
+ * When GuC reports an error on any of the queues of a multi queue group,
+ * the queue cleanup mechanism is invoked for all the queues of the group
+ * as hardware cannot make progress on the multi queue context.
+ *
+ * Refer :ref:`multi-queue-group-guc-interface` for multi queue group GuC
+ * interface.
+ */
+
enum xe_exec_queue_sched_prop {
XE_EXEC_QUEUE_JOB_TIMEOUT = 0,
XE_EXEC_QUEUE_TIMESLICE = 1,
@@ -61,7 +108,35 @@ enum xe_exec_queue_sched_prop {
};
static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
- u64 extensions, int ext_number);
+ u64 extensions);
+
+static void xe_exec_queue_group_cleanup(struct xe_exec_queue *q)
+{
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+ struct xe_lrc *lrc;
+ unsigned long idx;
+
+ if (xe_exec_queue_is_multi_queue_secondary(q)) {
+ /*
+ * Put pairs with get from xe_exec_queue_lookup() call
+ * in xe_exec_queue_group_validate().
+ */
+ xe_exec_queue_put(xe_exec_queue_multi_queue_primary(q));
+ return;
+ }
+
+ if (!group)
+ return;
+
+ /* Primary queue cleanup */
+ xa_for_each(&group->xa, idx, lrc)
+ xe_lrc_put(lrc);
+
+ xa_destroy(&group->xa);
+ mutex_destroy(&group->list_lock);
+ xe_bo_unpin_map_no_vm(group->cgp_bo);
+ kfree(group);
+}
static void __xe_exec_queue_free(struct xe_exec_queue *q)
{
@@ -73,12 +148,17 @@ static void __xe_exec_queue_free(struct xe_exec_queue *q)
if (xe_exec_queue_uses_pxp(q))
xe_pxp_exec_queue_remove(gt_to_xe(q->gt)->pxp, q);
+
+ if (xe_exec_queue_is_multi_queue(q))
+ xe_exec_queue_group_cleanup(q);
+
if (q->vm)
xe_vm_put(q->vm);
if (q->xef)
xe_file_put(q->xef);
+ kvfree(q->replay_state);
kfree(q);
}
@@ -147,6 +227,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
INIT_LIST_HEAD(&q->multi_gt_link);
INIT_LIST_HEAD(&q->hw_engine_group_link);
INIT_LIST_HEAD(&q->pxp.link);
+ q->multi_queue.priority = XE_MULTI_QUEUE_PRIORITY_NORMAL;
q->sched_props.timeslice_us = hwe->eclass->sched_props.timeslice_us;
q->sched_props.preempt_timeout_us =
@@ -175,7 +256,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
* may set q->usm, must come before xe_lrc_create(),
* may overwrite q->sched_props, must come before q->ops->init()
*/
- err = exec_queue_user_extensions(xe, q, extensions, 0);
+ err = exec_queue_user_extensions(xe, q, extensions);
if (err) {
__xe_exec_queue_free(q);
return ERR_PTR(err);
@@ -225,8 +306,8 @@ static int __xe_exec_queue_init(struct xe_exec_queue *q, u32 exec_queue_flags)
struct xe_lrc *lrc;
xe_gt_sriov_vf_wait_valid_ggtt(q->gt);
- lrc = xe_lrc_create(q->hwe, q->vm, xe_lrc_ring_size(),
- q->msix_vec, flags);
+ lrc = xe_lrc_create(q->hwe, q->vm, q->replay_state,
+ xe_lrc_ring_size(), q->msix_vec, flags);
if (IS_ERR(lrc)) {
err = PTR_ERR(lrc);
goto err_lrc;
@@ -328,6 +409,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe
* @xe: Xe device.
* @tile: tile which bind exec queue belongs to.
* @flags: exec queue creation flags
+ * @user_vm: The user VM which this exec queue belongs to
* @extensions: exec queue creation extensions
*
* Normalize bind exec queue creation. Bind exec queue is tied to migration VM
@@ -341,6 +423,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe
*/
struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
struct xe_tile *tile,
+ struct xe_vm *user_vm,
u32 flags, u64 extensions)
{
struct xe_gt *gt = tile->primary_gt;
@@ -377,6 +460,9 @@ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
xe_exec_queue_put(q);
return ERR_PTR(err);
}
+
+ if (user_vm)
+ q->user_vm = xe_vm_get(user_vm);
}
return q;
@@ -407,6 +493,11 @@ void xe_exec_queue_destroy(struct kref *ref)
xe_exec_queue_put(eq);
}
+ if (q->user_vm) {
+ xe_vm_put(q->user_vm);
+ q->user_vm = NULL;
+ }
+
q->ops->destroy(q);
}
@@ -567,6 +658,194 @@ exec_queue_set_pxp_type(struct xe_device *xe, struct xe_exec_queue *q, u64 value
return xe_pxp_exec_queue_set_type(xe->pxp, q, DRM_XE_PXP_TYPE_HWDRM);
}
+static int exec_queue_set_hang_replay_state(struct xe_device *xe,
+ struct xe_exec_queue *q,
+ u64 value)
+{
+ size_t size = xe_gt_lrc_hang_replay_size(q->gt, q->class);
+ u64 __user *address = u64_to_user_ptr(value);
+ void *ptr;
+
+ ptr = vmemdup_user(address, size);
+ if (XE_IOCTL_DBG(xe, IS_ERR(ptr)))
+ return PTR_ERR(ptr);
+
+ q->replay_state = ptr;
+
+ return 0;
+}
+
+static int xe_exec_queue_group_init(struct xe_device *xe, struct xe_exec_queue *q)
+{
+ struct xe_tile *tile = gt_to_tile(q->gt);
+ struct xe_exec_queue_group *group;
+ struct xe_bo *bo;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+
+ bo = xe_bo_create_pin_map_novm(xe, tile, SZ_4K, ttm_bo_type_kernel,
+ XE_BO_FLAG_VRAM_IF_DGFX(tile) |
+ XE_BO_FLAG_PINNED_LATE_RESTORE |
+ XE_BO_FLAG_FORCE_USER_VRAM |
+ XE_BO_FLAG_GGTT_INVALIDATE |
+ XE_BO_FLAG_GGTT, false);
+ if (IS_ERR(bo)) {
+ drm_err(&xe->drm, "CGP bo allocation for queue group failed: %ld\n",
+ PTR_ERR(bo));
+ kfree(group);
+ return PTR_ERR(bo);
+ }
+
+ xe_map_memset(xe, &bo->vmap, 0, 0, SZ_4K);
+
+ group->primary = q;
+ group->cgp_bo = bo;
+ INIT_LIST_HEAD(&group->list);
+ xa_init_flags(&group->xa, XA_FLAGS_ALLOC1);
+ mutex_init(&group->list_lock);
+ q->multi_queue.group = group;
+
+ /* group->list_lock is used in submission backend */
+ if (IS_ENABLED(CONFIG_LOCKDEP)) {
+ fs_reclaim_acquire(GFP_KERNEL);
+ might_lock(&group->list_lock);
+ fs_reclaim_release(GFP_KERNEL);
+ }
+
+ return 0;
+}
+
+static inline bool xe_exec_queue_supports_multi_queue(struct xe_exec_queue *q)
+{
+ return q->gt->info.multi_queue_engine_class_mask & BIT(q->class);
+}
+
+static int xe_exec_queue_group_validate(struct xe_device *xe, struct xe_exec_queue *q,
+ u32 primary_id)
+{
+ struct xe_exec_queue_group *group;
+ struct xe_exec_queue *primary;
+ int ret;
+
+ /*
+ * Get from below xe_exec_queue_lookup() pairs with put
+ * in xe_exec_queue_group_cleanup().
+ */
+ primary = xe_exec_queue_lookup(q->vm->xef, primary_id);
+ if (XE_IOCTL_DBG(xe, !primary))
+ return -ENOENT;
+
+ if (XE_IOCTL_DBG(xe, !xe_exec_queue_is_multi_queue_primary(primary)) ||
+ XE_IOCTL_DBG(xe, q->vm != primary->vm) ||
+ XE_IOCTL_DBG(xe, q->logical_mask != primary->logical_mask)) {
+ ret = -EINVAL;
+ goto put_primary;
+ }
+
+ group = primary->multi_queue.group;
+ q->multi_queue.valid = true;
+ q->multi_queue.group = group;
+
+ return 0;
+put_primary:
+ xe_exec_queue_put(primary);
+ return ret;
+}
+
+#define XE_MAX_GROUP_SIZE 64
+static int xe_exec_queue_group_add(struct xe_device *xe, struct xe_exec_queue *q)
+{
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+ u32 pos;
+ int err;
+
+ xe_assert(xe, xe_exec_queue_is_multi_queue_secondary(q));
+
+ /* Primary queue holds a reference to LRCs of all secondary queues */
+ err = xa_alloc(&group->xa, &pos, xe_lrc_get(q->lrc[0]),
+ XA_LIMIT(1, XE_MAX_GROUP_SIZE - 1), GFP_KERNEL);
+ if (XE_IOCTL_DBG(xe, err)) {
+ xe_lrc_put(q->lrc[0]);
+
+ /* It is invalid if queue group limit is exceeded */
+ if (err == -EBUSY)
+ err = -EINVAL;
+
+ return err;
+ }
+
+ q->multi_queue.pos = pos;
+
+ return 0;
+}
+
+static void xe_exec_queue_group_delete(struct xe_device *xe, struct xe_exec_queue *q)
+{
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+ struct xe_lrc *lrc;
+
+ xe_assert(xe, xe_exec_queue_is_multi_queue_secondary(q));
+
+ lrc = xa_erase(&group->xa, q->multi_queue.pos);
+ xe_assert(xe, lrc);
+ xe_lrc_put(lrc);
+}
+
+static int exec_queue_set_multi_group(struct xe_device *xe, struct xe_exec_queue *q,
+ u64 value)
+{
+ if (XE_IOCTL_DBG(xe, !xe_exec_queue_supports_multi_queue(q)))
+ return -ENODEV;
+
+ if (XE_IOCTL_DBG(xe, !xe_device_uc_enabled(xe)))
+ return -EOPNOTSUPP;
+
+ if (XE_IOCTL_DBG(xe, !q->vm->xef))
+ return -EINVAL;
+
+ if (XE_IOCTL_DBG(xe, xe_exec_queue_is_parallel(q)))
+ return -EINVAL;
+
+ if (XE_IOCTL_DBG(xe, xe_exec_queue_is_multi_queue(q)))
+ return -EINVAL;
+
+ if (value & DRM_XE_MULTI_GROUP_CREATE) {
+ if (XE_IOCTL_DBG(xe, value & ~DRM_XE_MULTI_GROUP_CREATE))
+ return -EINVAL;
+
+ q->multi_queue.valid = true;
+ q->multi_queue.is_primary = true;
+ q->multi_queue.pos = 0;
+ return 0;
+ }
+
+ /* While adding secondary queues, the upper 32 bits must be 0 */
+ if (XE_IOCTL_DBG(xe, value & (~0ull << 32)))
+ return -EINVAL;
+
+ return xe_exec_queue_group_validate(xe, q, value);
+}
+
+static int exec_queue_set_multi_queue_priority(struct xe_device *xe, struct xe_exec_queue *q,
+ u64 value)
+{
+ if (XE_IOCTL_DBG(xe, value > XE_MULTI_QUEUE_PRIORITY_HIGH))
+ return -EINVAL;
+
+ /* For queue creation time (!q->xef) setting, just store the priority value */
+ if (!q->xef) {
+ q->multi_queue.priority = value;
+ return 0;
+ }
+
+ if (!xe_exec_queue_is_multi_queue(q))
+ return -EINVAL;
+
+ return q->ops->set_multi_queue_priority(q, value);
+}
+
typedef int (*xe_exec_queue_set_property_fn)(struct xe_device *xe,
struct xe_exec_queue *q,
u64 value);
@@ -575,11 +854,76 @@ static const xe_exec_queue_set_property_fn exec_queue_set_property_funcs[] = {
[DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY] = exec_queue_set_priority,
[DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE] = exec_queue_set_timeslice,
[DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE] = exec_queue_set_pxp_type,
+ [DRM_XE_EXEC_QUEUE_SET_HANG_REPLAY_STATE] = exec_queue_set_hang_replay_state,
+ [DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP] = exec_queue_set_multi_group,
+ [DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY] =
+ exec_queue_set_multi_queue_priority,
};
+int xe_exec_queue_set_property_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct xe_device *xe = to_xe_device(dev);
+ struct xe_file *xef = to_xe_file(file);
+ struct drm_xe_exec_queue_set_property *args = data;
+ struct xe_exec_queue *q;
+ int ret;
+ u32 idx;
+
+ if (XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]))
+ return -EINVAL;
+
+ if (XE_IOCTL_DBG(xe, args->property !=
+ DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY))
+ return -EINVAL;
+
+ q = xe_exec_queue_lookup(xef, args->exec_queue_id);
+ if (XE_IOCTL_DBG(xe, !q))
+ return -ENOENT;
+
+ idx = array_index_nospec(args->property,
+ ARRAY_SIZE(exec_queue_set_property_funcs));
+ ret = exec_queue_set_property_funcs[idx](xe, q, args->value);
+ if (XE_IOCTL_DBG(xe, ret))
+ goto err_post_lookup;
+
+ xe_exec_queue_put(q);
+ return 0;
+
+ err_post_lookup:
+ xe_exec_queue_put(q);
+ return ret;
+}
+
+static int exec_queue_user_ext_check(struct xe_exec_queue *q, u64 properties)
+{
+ u64 secondary_queue_valid_props = BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP) |
+ BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY);
+
+ /*
+ * Only MULTI_QUEUE_PRIORITY property is valid for secondary queues of a
+ * multi-queue group.
+ */
+ if (xe_exec_queue_is_multi_queue_secondary(q) &&
+ properties & ~secondary_queue_valid_props)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int exec_queue_user_ext_check_final(struct xe_exec_queue *q, u64 properties)
+{
+ /* MULTI_QUEUE_PRIORITY only applies to multi-queue group queues */
+ if ((properties & BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY)) &&
+ !(properties & BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP)))
+ return -EINVAL;
+
+ return 0;
+}
+
static int exec_queue_user_ext_set_property(struct xe_device *xe,
struct xe_exec_queue *q,
- u64 extension)
+ u64 extension, u64 *properties)
{
u64 __user *address = u64_to_user_ptr(extension);
struct drm_xe_ext_set_property ext;
@@ -595,27 +939,35 @@ static int exec_queue_user_ext_set_property(struct xe_device *xe,
XE_IOCTL_DBG(xe, ext.pad) ||
XE_IOCTL_DBG(xe, ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY &&
ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE &&
- ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE))
+ ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE &&
+ ext.property != DRM_XE_EXEC_QUEUE_SET_HANG_REPLAY_STATE &&
+ ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP &&
+ ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY))
return -EINVAL;
idx = array_index_nospec(ext.property, ARRAY_SIZE(exec_queue_set_property_funcs));
if (!exec_queue_set_property_funcs[idx])
return -EINVAL;
+ *properties |= BIT_ULL(idx);
+ err = exec_queue_user_ext_check(q, *properties);
+ if (XE_IOCTL_DBG(xe, err))
+ return err;
+
return exec_queue_set_property_funcs[idx](xe, q, ext.value);
}
typedef int (*xe_exec_queue_user_extension_fn)(struct xe_device *xe,
struct xe_exec_queue *q,
- u64 extension);
+ u64 extension, u64 *properties);
static const xe_exec_queue_user_extension_fn exec_queue_user_extension_funcs[] = {
[DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY] = exec_queue_user_ext_set_property,
};
#define MAX_USER_EXTENSIONS 16
-static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
- u64 extensions, int ext_number)
+static int __exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
+ u64 extensions, int ext_number, u64 *properties)
{
u64 __user *address = u64_to_user_ptr(extensions);
struct drm_xe_user_extension ext;
@@ -636,13 +988,36 @@ static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue
idx = array_index_nospec(ext.name,
ARRAY_SIZE(exec_queue_user_extension_funcs));
- err = exec_queue_user_extension_funcs[idx](xe, q, extensions);
+ err = exec_queue_user_extension_funcs[idx](xe, q, extensions, properties);
if (XE_IOCTL_DBG(xe, err))
return err;
if (ext.next_extension)
- return exec_queue_user_extensions(xe, q, ext.next_extension,
- ++ext_number);
+ return __exec_queue_user_extensions(xe, q, ext.next_extension,
+ ++ext_number, properties);
+
+ return 0;
+}
+
+static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
+ u64 extensions)
+{
+ u64 properties = 0;
+ int err;
+
+ err = __exec_queue_user_extensions(xe, q, extensions, 0, &properties);
+ if (XE_IOCTL_DBG(xe, err))
+ return err;
+
+ err = exec_queue_user_ext_check_final(q, properties);
+ if (XE_IOCTL_DBG(xe, err))
+ return err;
+
+ if (xe_exec_queue_is_multi_queue_primary(q)) {
+ err = xe_exec_queue_group_init(xe, q);
+ if (XE_IOCTL_DBG(xe, err))
+ return err;
+ }
return 0;
}
@@ -698,6 +1073,17 @@ static u32 calc_validate_logical_mask(struct xe_device *xe,
return return_mask;
}
+static bool has_sched_groups(struct xe_gt *gt)
+{
+ if (IS_SRIOV_PF(gt_to_xe(gt)) && xe_gt_sriov_pf_sched_groups_enabled(gt))
+ return true;
+
+ if (IS_SRIOV_VF(gt_to_xe(gt)) && xe_gt_sriov_vf_sched_groups_enabled(gt))
+ return true;
+
+ return false;
+}
+
int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
@@ -742,6 +1128,22 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
XE_IOCTL_DBG(xe, eci[0].engine_instance != 0))
return -EINVAL;
+ vm = xe_vm_lookup(xef, args->vm_id);
+ if (XE_IOCTL_DBG(xe, !vm))
+ return -ENOENT;
+
+ err = down_read_interruptible(&vm->lock);
+ if (err) {
+ xe_vm_put(vm);
+ return err;
+ }
+
+ if (XE_IOCTL_DBG(xe, xe_vm_is_closed_or_banned(vm))) {
+ up_read(&vm->lock);
+ xe_vm_put(vm);
+ return -ENOENT;
+ }
+
for_each_tile(tile, xe, id) {
struct xe_exec_queue *new;
@@ -749,9 +1151,11 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
if (id)
flags |= EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD;
- new = xe_exec_queue_create_bind(xe, tile, flags,
+ new = xe_exec_queue_create_bind(xe, tile, vm, flags,
args->extensions);
if (IS_ERR(new)) {
+ up_read(&vm->lock);
+ xe_vm_put(vm);
err = PTR_ERR(new);
if (q)
goto put_exec_queue;
@@ -763,6 +1167,8 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
list_add_tail(&new->multi_gt_list,
&q->multi_gt_link);
}
+ up_read(&vm->lock);
+ xe_vm_put(vm);
} else {
logical_mask = calc_validate_logical_mask(xe, eci,
args->width,
@@ -790,6 +1196,13 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
return -ENOENT;
}
+ /* SRIOV sched groups are not compatible with multi-lrc */
+ if (XE_IOCTL_DBG(xe, args->width > 1 && has_sched_groups(hwe->gt))) {
+ up_read(&vm->lock);
+ xe_vm_put(vm);
+ return -EINVAL;
+ }
+
q = xe_exec_queue_create(xe, vm, logical_mask,
args->width, hwe, flags,
args->extensions);
@@ -798,12 +1211,18 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
if (IS_ERR(q))
return PTR_ERR(q);
+ if (xe_exec_queue_is_multi_queue_secondary(q)) {
+ err = xe_exec_queue_group_add(xe, q);
+ if (XE_IOCTL_DBG(xe, err))
+ goto put_exec_queue;
+ }
+
if (xe_vm_in_preempt_fence_mode(vm)) {
q->lr.context = dma_fence_context_alloc(1);
err = xe_vm_add_compute_exec_queue(vm, q);
if (XE_IOCTL_DBG(xe, err))
- goto put_exec_queue;
+ goto delete_queue_group;
}
if (q->vm && q->hwe->hw_engine_group) {
@@ -826,6 +1245,9 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
kill_exec_queue:
xe_exec_queue_kill(q);
+delete_queue_group:
+ if (xe_exec_queue_is_multi_queue_secondary(q))
+ xe_exec_queue_group_delete(xe, q);
put_exec_queue:
xe_exec_queue_put(q);
return err;
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h
index fda4d4f9bda8..c9e3a7c2d249 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue.h
@@ -28,6 +28,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe
u32 flags, u64 extensions);
struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
struct xe_tile *tile,
+ struct xe_vm *user_vm,
u32 flags, u64 extensions);
void xe_exec_queue_fini(struct xe_exec_queue *q);
@@ -66,6 +67,53 @@ static inline bool xe_exec_queue_uses_pxp(struct xe_exec_queue *q)
return q->pxp.type;
}
+/**
+ * xe_exec_queue_is_multi_queue() - Whether an exec_queue is part of a queue group.
+ * @q: The exec_queue
+ *
+ * Return: True if the exec_queue is part of a queue group, false otherwise.
+ */
+static inline bool xe_exec_queue_is_multi_queue(struct xe_exec_queue *q)
+{
+ return q->multi_queue.valid;
+}
+
+/**
+ * xe_exec_queue_is_multi_queue_primary() - Whether an exec_queue is primary queue
+ * of a multi queue group.
+ * @q: The exec_queue
+ *
+ * Return: True if @q is primary queue of a queue group, false otherwise.
+ */
+static inline bool xe_exec_queue_is_multi_queue_primary(struct xe_exec_queue *q)
+{
+ return q->multi_queue.is_primary;
+}
+
+/**
+ * xe_exec_queue_is_multi_queue_secondary() - Whether an exec_queue is secondary queue
+ * of a multi queue group.
+ * @q: The exec_queue
+ *
+ * Return: True if @q is secondary queue of a queue group, false otherwise.
+ */
+static inline bool xe_exec_queue_is_multi_queue_secondary(struct xe_exec_queue *q)
+{
+ return xe_exec_queue_is_multi_queue(q) && !xe_exec_queue_is_multi_queue_primary(q);
+}
+
+/**
+ * xe_exec_queue_multi_queue_primary() - Get multi queue group's primary queue
+ * @q: The exec_queue
+ *
+ * If @q belongs to a multi queue group, then the primary queue of the group will
+ * be returned. Otherwise, @q will be returned.
+ */
+static inline struct xe_exec_queue *xe_exec_queue_multi_queue_primary(struct xe_exec_queue *q)
+{
+ return xe_exec_queue_is_multi_queue(q) ? q->multi_queue.group->primary : q;
+}
+
bool xe_exec_queue_is_lr(struct xe_exec_queue *q);
bool xe_exec_queue_is_idle(struct xe_exec_queue *q);
@@ -78,6 +126,8 @@ int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
struct drm_file *file);
int xe_exec_queue_get_property_ioctl(struct drm_device *dev, void *data,
struct drm_file *file);
+int xe_exec_queue_set_property_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file);
enum xe_exec_queue_priority xe_exec_queue_device_get_max_priority(struct xe_device *xe);
void xe_exec_queue_last_fence_put(struct xe_exec_queue *e, struct xe_vm *vm);
@@ -111,4 +161,21 @@ int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch);
struct xe_lrc *xe_exec_queue_lrc(struct xe_exec_queue *q);
+/**
+ * xe_exec_queue_idle_skip_suspend() - Can exec queue skip suspend
+ * @q: The exec_queue
+ *
+ * If an exec queue is not parallel and is idle, the suspend steps can be
+ * skipped in the submission backend immediatley signaling the suspend fence.
+ * Parallel queues cannot skip this step due to limitations in the submission
+ * backend.
+ *
+ * Return: True if exec queue is idle and can skip suspend steps, False
+ * otherwise
+ */
+static inline bool xe_exec_queue_idle_skip_suspend(struct xe_exec_queue *q)
+{
+ return !xe_exec_queue_is_parallel(q) && xe_exec_queue_is_idle(q);
+}
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
index 771ffe35cd0c..f1142e5d3e3e 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -33,6 +33,42 @@ enum xe_exec_queue_priority {
};
/**
+ * enum xe_multi_queue_priority - Multi Queue priority values
+ *
+ * The priority values of the queues within the multi queue group.
+ */
+enum xe_multi_queue_priority {
+ /** @XE_MULTI_QUEUE_PRIORITY_LOW: Priority low */
+ XE_MULTI_QUEUE_PRIORITY_LOW = 0,
+ /** @XE_MULTI_QUEUE_PRIORITY_NORMAL: Priority normal */
+ XE_MULTI_QUEUE_PRIORITY_NORMAL,
+ /** @XE_MULTI_QUEUE_PRIORITY_HIGH: Priority high */
+ XE_MULTI_QUEUE_PRIORITY_HIGH,
+};
+
+/**
+ * struct xe_exec_queue_group - Execution multi queue group
+ *
+ * Contains multi queue group information.
+ */
+struct xe_exec_queue_group {
+ /** @primary: Primary queue of this group */
+ struct xe_exec_queue *primary;
+ /** @cgp_bo: BO for the Context Group Page */
+ struct xe_bo *cgp_bo;
+ /** @xa: xarray to store LRCs */
+ struct xarray xa;
+ /** @list: List of all secondary queues in the group */
+ struct list_head list;
+ /** @list_lock: Secondary queue list lock */
+ struct mutex list_lock;
+ /** @sync_pending: CGP_SYNC_DONE g2h response pending */
+ bool sync_pending;
+ /** @banned: Group banned */
+ bool banned;
+};
+
+/**
* struct xe_exec_queue - Execution queue
*
* Contains all state necessary for submissions. Can either be a user object or
@@ -54,6 +90,12 @@ struct xe_exec_queue {
struct kref refcount;
/** @vm: VM (address space) for this exec queue */
struct xe_vm *vm;
+ /**
+ * @user_vm: User VM (address space) for this exec queue (bind queues
+ * only)
+ */
+ struct xe_vm *user_vm;
+
/** @class: class of this exec queue */
enum xe_engine_class class;
/**
@@ -111,6 +153,22 @@ struct xe_exec_queue {
struct xe_guc_exec_queue *guc;
};
+ /** @multi_queue: Multi queue information */
+ struct {
+ /** @multi_queue.group: Queue group information */
+ struct xe_exec_queue_group *group;
+ /** @multi_queue.link: Link into group's secondary queues list */
+ struct list_head link;
+ /** @multi_queue.priority: Queue priority within the multi-queue group */
+ enum xe_multi_queue_priority priority;
+ /** @multi_queue.pos: Position of queue within the multi-queue group */
+ u8 pos;
+ /** @multi_queue.valid: Queue belongs to a multi queue group */
+ u8 valid:1;
+ /** @multi_queue.is_primary: Is primary queue (Q0) of the group */
+ u8 is_primary:1;
+ } multi_queue;
+
/** @sched_props: scheduling properties */
struct {
/** @sched_props.timeslice_us: timeslice period in micro-seconds */
@@ -167,6 +225,9 @@ struct xe_exec_queue {
/** @ufence_timeline_value: User fence timeline value */
u64 ufence_timeline_value;
+ /** @replay_state: GPU hang replay state */
+ void *replay_state;
+
/** @ops: submission backend exec queue operations */
const struct xe_exec_queue_ops *ops;
@@ -213,6 +274,9 @@ struct xe_exec_queue_ops {
int (*set_timeslice)(struct xe_exec_queue *q, u32 timeslice_us);
/** @set_preempt_timeout: Set preemption timeout for exec queue */
int (*set_preempt_timeout)(struct xe_exec_queue *q, u32 preempt_timeout_us);
+ /** @set_multi_queue_priority: Set multi queue priority */
+ int (*set_multi_queue_priority)(struct xe_exec_queue *q,
+ enum xe_multi_queue_priority priority);
/**
* @suspend: Suspend exec queue from executing, allowed to be called
* multiple times in a row before resume with the caveat that
diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c
index 769d05517f93..8bf330aeaec0 100644
--- a/drivers/gpu/drm/xe/xe_execlist.c
+++ b/drivers/gpu/drm/xe/xe_execlist.c
@@ -15,8 +15,7 @@
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_exec_queue.h"
-#include "xe_gt.h"
-#include "xe_hw_fence.h"
+#include "xe_gt_types.h"
#include "xe_irq.h"
#include "xe_lrc.h"
#include "xe_macros.h"
@@ -269,7 +268,7 @@ struct xe_execlist_port *xe_execlist_port_create(struct xe_device *xe,
port->hwe = hwe;
- port->lrc = xe_lrc_create(hwe, NULL, SZ_16K, XE_IRQ_DEFAULT_MSIX, 0);
+ port->lrc = xe_lrc_create(hwe, NULL, NULL, SZ_16K, XE_IRQ_DEFAULT_MSIX, 0);
if (IS_ERR(port->lrc)) {
err = PTR_ERR(port->lrc);
goto err;
diff --git a/drivers/gpu/drm/xe/xe_force_wake.c b/drivers/gpu/drm/xe/xe_force_wake.c
index c59a9b330697..76e054f314ee 100644
--- a/drivers/gpu/drm/xe/xe_force_wake.c
+++ b/drivers/gpu/drm/xe/xe_force_wake.c
@@ -166,6 +166,13 @@ static int domain_sleep_wait(struct xe_gt *gt,
* xe_force_wake_ref_has_domain() function. Caller must call
* xe_force_wake_put() function to decrease incremented refcounts.
*
+ * When possible, scope-based forcewake (through CLASS(xe_force_wake, ...) or
+ * xe_with_force_wake()) should be used instead of direct calls to this
+ * function. Direct usage of get/put should only be used when the function
+ * has goto-based flows that can interfere with scope-based cleanup, or when
+ * the lifetime of the forcewake reference does not match a specific scope
+ * (e.g., forcewake obtained in one function and released in a different one).
+ *
* Return: opaque reference to woken domains or zero if none of requested
* domains were awake.
*/
diff --git a/drivers/gpu/drm/xe/xe_force_wake.h b/drivers/gpu/drm/xe/xe_force_wake.h
index 0e3e84bfa51c..1e2198f6a007 100644
--- a/drivers/gpu/drm/xe/xe_force_wake.h
+++ b/drivers/gpu/drm/xe/xe_force_wake.h
@@ -61,4 +61,44 @@ xe_force_wake_ref_has_domain(unsigned int fw_ref, enum xe_force_wake_domains dom
return fw_ref & domain;
}
+struct xe_force_wake_ref {
+ struct xe_force_wake *fw;
+ unsigned int domains;
+};
+
+static struct xe_force_wake_ref
+xe_force_wake_constructor(struct xe_force_wake *fw, unsigned int domains)
+{
+ struct xe_force_wake_ref fw_ref = { .fw = fw };
+
+ fw_ref.domains = xe_force_wake_get(fw, domains);
+
+ return fw_ref;
+}
+
+DEFINE_CLASS(xe_force_wake, struct xe_force_wake_ref,
+ xe_force_wake_put(_T.fw, _T.domains),
+ xe_force_wake_constructor(fw, domains),
+ struct xe_force_wake *fw, unsigned int domains);
+
+/*
+ * Scoped helper for the forcewake class, using the same trick as scoped_guard()
+ * to bind the lifetime to the next statement/block.
+ */
+#define __xe_with_force_wake(ref, fw, domains, done) \
+ for (CLASS(xe_force_wake, ref)(fw, domains), *(done) = NULL; \
+ !(done); (done) = (void *)1)
+
+#define xe_with_force_wake(ref, fw, domains) \
+ __xe_with_force_wake(ref, fw, domains, __UNIQUE_ID(done))
+
+/*
+ * Used when xe_force_wake_constructor() has already been called by another
+ * function and the current function is responsible for releasing the forcewake
+ * reference in all possible cases and error paths.
+ */
+DEFINE_CLASS(xe_force_wake_release_only, struct xe_force_wake_ref,
+ if (_T.fw) xe_force_wake_put(_T.fw, _T.domains), fw_ref,
+ struct xe_force_wake_ref fw_ref);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_gen_wa_oob.c b/drivers/gpu/drm/xe/xe_gen_wa_oob.c
index 247e41c1c48d..e7a50b1348b7 100644
--- a/drivers/gpu/drm/xe/xe_gen_wa_oob.c
+++ b/drivers/gpu/drm/xe/xe_gen_wa_oob.c
@@ -195,7 +195,8 @@ int main(int argc, const char *argv[])
}
}
- fprintf(args[ARGS_CHEADER].f, HEADER, args[ARGS_INPUT].fn, prefix, prefix);
+ fprintf(args[ARGS_CHEADER].f, HEADER, xbasename(args[ARGS_INPUT].fn),
+ prefix, prefix);
ret = parse(args[ARGS_INPUT].f, args[ARGS_CSOURCE].f,
args[ARGS_CHEADER].f, prefix);
diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c
index ef481b334af4..60665ad1415b 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.c
+++ b/drivers/gpu/drm/xe/xe_ggtt.c
@@ -20,9 +20,8 @@
#include "regs/xe_regs.h"
#include "xe_assert.h"
#include "xe_bo.h"
-#include "xe_device.h"
-#include "xe_gt.h"
#include "xe_gt_printk.h"
+#include "xe_gt_types.h"
#include "xe_map.h"
#include "xe_mmio.h"
#include "xe_pm.h"
@@ -67,6 +66,24 @@
* give us the correct placement for free.
*/
+/**
+ * struct xe_ggtt_node - A node in GGTT.
+ *
+ * This struct needs to be initialized (only-once) with xe_ggtt_node_init() before any node
+ * insertion, reservation, or 'ballooning'.
+ * It will, then, be finalized by either xe_ggtt_node_remove() or xe_ggtt_node_deballoon().
+ */
+struct xe_ggtt_node {
+ /** @ggtt: Back pointer to xe_ggtt where this region will be inserted at */
+ struct xe_ggtt *ggtt;
+ /** @base: A drm_mm_node */
+ struct drm_mm_node base;
+ /** @delayed_removal_work: The work struct for the delayed removal */
+ struct work_struct delayed_removal_work;
+ /** @invalidate_on_remove: If it needs invalidation upon removal */
+ bool invalidate_on_remove;
+};
+
static u64 xelp_ggtt_pte_flags(struct xe_bo *bo, u16 pat_index)
{
u64 pte = XE_PAGE_PRESENT;
@@ -137,10 +154,32 @@ static void ggtt_update_access_counter(struct xe_ggtt *ggtt)
}
}
+/**
+ * xe_ggtt_start - Get starting offset of GGTT.
+ * @ggtt: &xe_ggtt
+ *
+ * Returns: Starting offset for this &xe_ggtt.
+ */
+u64 xe_ggtt_start(struct xe_ggtt *ggtt)
+{
+ return ggtt->start;
+}
+
+/**
+ * xe_ggtt_size - Get size of GGTT.
+ * @ggtt: &xe_ggtt
+ *
+ * Returns: Total usable size of this &xe_ggtt.
+ */
+u64 xe_ggtt_size(struct xe_ggtt *ggtt)
+{
+ return ggtt->size;
+}
+
static void xe_ggtt_set_pte(struct xe_ggtt *ggtt, u64 addr, u64 pte)
{
xe_tile_assert(ggtt->tile, !(addr & XE_PTE_MASK));
- xe_tile_assert(ggtt->tile, addr < ggtt->size);
+ xe_tile_assert(ggtt->tile, addr < ggtt->start + ggtt->size);
writeq(pte, &ggtt->gsm[addr >> XE_PTE_SHIFT]);
}
@@ -256,16 +295,16 @@ static const struct xe_ggtt_pt_ops xelpg_pt_wa_ops = {
.ggtt_get_pte = xe_ggtt_get_pte,
};
-static void __xe_ggtt_init_early(struct xe_ggtt *ggtt, u32 reserved)
+static void __xe_ggtt_init_early(struct xe_ggtt *ggtt, u64 start, u64 size)
{
- drm_mm_init(&ggtt->mm, reserved,
- ggtt->size - reserved);
+ ggtt->start = start;
+ ggtt->size = size;
+ drm_mm_init(&ggtt->mm, start, size);
}
-int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 reserved, u32 size)
+int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 start, u32 size)
{
- ggtt->size = size;
- __xe_ggtt_init_early(ggtt, reserved);
+ __xe_ggtt_init_early(ggtt, start, size);
return 0;
}
EXPORT_SYMBOL_IF_KUNIT(xe_ggtt_init_kunit);
@@ -293,26 +332,32 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
struct xe_device *xe = tile_to_xe(ggtt->tile);
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
unsigned int gsm_size;
+ u64 ggtt_start, wopcm = xe_wopcm_size(xe), ggtt_size;
int err;
- if (IS_SRIOV_VF(xe) || GRAPHICS_VERx100(xe) >= 1250)
- gsm_size = SZ_8M; /* GGTT is expected to be 4GiB */
- else
- gsm_size = probe_gsm_size(pdev);
-
- if (gsm_size == 0) {
- xe_tile_err(ggtt->tile, "Hardware reported no preallocated GSM\n");
- return -ENOMEM;
+ if (!IS_SRIOV_VF(xe)) {
+ if (GRAPHICS_VERx100(xe) >= 1250)
+ gsm_size = SZ_8M; /* GGTT is expected to be 4GiB */
+ else
+ gsm_size = probe_gsm_size(pdev);
+ if (gsm_size == 0) {
+ xe_tile_err(ggtt->tile, "Hardware reported no preallocated GSM\n");
+ return -ENOMEM;
+ }
+ ggtt_start = wopcm;
+ ggtt_size = (gsm_size / 8) * (u64)XE_PAGE_SIZE - ggtt_start;
+ } else {
+ /* GGTT is expected to be 4GiB */
+ ggtt_start = wopcm;
+ ggtt_size = SZ_4G - ggtt_start;
}
ggtt->gsm = ggtt->tile->mmio.regs + SZ_8M;
- ggtt->size = (gsm_size / 8) * (u64) XE_PAGE_SIZE;
-
if (IS_DGFX(xe) && xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K)
ggtt->flags |= XE_GGTT_FLAGS_64K;
- if (ggtt->size > GUC_GGTT_TOP)
- ggtt->size = GUC_GGTT_TOP;
+ if (ggtt_size + ggtt_start > GUC_GGTT_TOP)
+ ggtt_size = GUC_GGTT_TOP - ggtt_start;
if (GRAPHICS_VERx100(xe) >= 1270)
ggtt->pt_ops =
@@ -322,11 +367,11 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
else
ggtt->pt_ops = &xelp_pt_ops;
- ggtt->wq = alloc_workqueue("xe-ggtt-wq", 0, WQ_MEM_RECLAIM);
+ ggtt->wq = alloc_workqueue("xe-ggtt-wq", WQ_MEM_RECLAIM, 0);
if (!ggtt->wq)
return -ENOMEM;
- __xe_ggtt_init_early(ggtt, xe_wopcm_size(xe));
+ __xe_ggtt_init_early(ggtt, ggtt_start, ggtt_size);
err = drmm_add_action_or_reset(&xe->drm, ggtt_fini_early, ggtt);
if (err)
@@ -396,9 +441,8 @@ static void ggtt_node_remove_work_func(struct work_struct *work)
delayed_removal_work);
struct xe_device *xe = tile_to_xe(node->ggtt->tile);
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
ggtt_node_remove(node);
- xe_pm_runtime_put(xe);
}
/**
@@ -564,11 +608,9 @@ void xe_ggtt_node_remove_balloon_locked(struct xe_ggtt_node *node)
static void xe_ggtt_assert_fit(struct xe_ggtt *ggtt, u64 start, u64 size)
{
struct xe_tile *tile = ggtt->tile;
- struct xe_device *xe = tile_to_xe(tile);
- u64 __maybe_unused wopcm = xe_wopcm_size(xe);
- xe_tile_assert(tile, start >= wopcm);
- xe_tile_assert(tile, start + size < ggtt->size - wopcm);
+ xe_tile_assert(tile, start >= ggtt->start);
+ xe_tile_assert(tile, start + size <= ggtt->start + ggtt->size);
}
/**
@@ -611,20 +653,8 @@ void xe_ggtt_shift_nodes_locked(struct xe_ggtt *ggtt, s64 shift)
}
}
-/**
- * xe_ggtt_node_insert_locked - Locked version to insert a &xe_ggtt_node into the GGTT
- * @node: the &xe_ggtt_node to be inserted
- * @size: size of the node
- * @align: alignment constrain of the node
- * @mm_flags: flags to control the node behavior
- *
- * It cannot be called without first having called xe_ggtt_init() once.
- * To be used in cases where ggtt->lock is already taken.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int xe_ggtt_node_insert_locked(struct xe_ggtt_node *node,
- u32 size, u32 align, u32 mm_flags)
+static int xe_ggtt_node_insert_locked(struct xe_ggtt_node *node,
+ u32 size, u32 align, u32 mm_flags)
{
return drm_mm_insert_node_generic(&node->ggtt->mm, &node->base, size, align, 0,
mm_flags);
@@ -662,9 +692,11 @@ int xe_ggtt_node_insert(struct xe_ggtt_node *node, u32 size, u32 align)
* This function will allocate the struct %xe_ggtt_node and return its pointer.
* This struct will then be freed after the node removal upon xe_ggtt_node_remove()
* or xe_ggtt_node_remove_balloon_locked().
- * Having %xe_ggtt_node struct allocated doesn't mean that the node is already allocated
- * in GGTT. Only the xe_ggtt_node_insert(), xe_ggtt_node_insert_locked(),
- * xe_ggtt_node_insert_balloon_locked() will ensure the node is inserted or reserved in GGTT.
+ *
+ * Having %xe_ggtt_node struct allocated doesn't mean that the node is already
+ * allocated in GGTT. Only xe_ggtt_node_insert(), allocation through
+ * xe_ggtt_node_insert_transform(), or xe_ggtt_node_insert_balloon_locked() will ensure the node is inserted or reserved
+ * in GGTT.
*
* Return: A pointer to %xe_ggtt_node struct on success. An ERR_PTR otherwise.
**/
@@ -727,13 +759,12 @@ size_t xe_ggtt_node_pt_size(const struct xe_ggtt_node *node)
* @ggtt: the &xe_ggtt where node will be mapped
* @node: the &xe_ggtt_node where this BO is mapped
* @bo: the &xe_bo to be mapped
- * @pat_index: Which pat_index to use.
+ * @pte: The pte flags to append.
*/
-void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
- struct xe_bo *bo, u16 pat_index)
+static void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
+ struct xe_bo *bo, u64 pte)
{
-
- u64 start, pte, end;
+ u64 start, end;
struct xe_res_cursor cur;
if (XE_WARN_ON(!node))
@@ -742,7 +773,6 @@ void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
start = node->base.start;
end = start + xe_bo_size(bo);
- pte = ggtt->pt_ops->pte_encode_flags(bo, pat_index);
if (!xe_bo_is_vram(bo) && !xe_bo_is_stolen(bo)) {
xe_assert(xe_bo_device(bo), bo->ttm.ttm);
@@ -772,12 +802,65 @@ void xe_ggtt_map_bo_unlocked(struct xe_ggtt *ggtt, struct xe_bo *bo)
{
u16 cache_mode = bo->flags & XE_BO_FLAG_NEEDS_UC ? XE_CACHE_NONE : XE_CACHE_WB;
u16 pat_index = tile_to_xe(ggtt->tile)->pat.idx[cache_mode];
+ u64 pte;
mutex_lock(&ggtt->lock);
- xe_ggtt_map_bo(ggtt, bo->ggtt_node[ggtt->tile->id], bo, pat_index);
+ pte = ggtt->pt_ops->pte_encode_flags(bo, pat_index);
+ xe_ggtt_map_bo(ggtt, bo->ggtt_node[ggtt->tile->id], bo, pte);
mutex_unlock(&ggtt->lock);
}
+/**
+ * xe_ggtt_node_insert_transform - Insert a newly allocated &xe_ggtt_node into the GGTT
+ * @ggtt: the &xe_ggtt where the node will inserted/reserved.
+ * @bo: The bo to be transformed
+ * @pte_flags: The extra GGTT flags to add to mapping.
+ * @size: size of the node
+ * @align: required alignment for node
+ * @transform: transformation function that will populate the GGTT node, or NULL for linear mapping.
+ * @arg: Extra argument to pass to the transformation function.
+ *
+ * This function allows inserting a GGTT node with a custom transformation function.
+ * This is useful for display to allow inserting rotated framebuffers to GGTT.
+ *
+ * Return: A pointer to %xe_ggtt_node struct on success. An ERR_PTR otherwise.
+ */
+struct xe_ggtt_node *xe_ggtt_node_insert_transform(struct xe_ggtt *ggtt,
+ struct xe_bo *bo, u64 pte_flags,
+ u64 size, u32 align,
+ xe_ggtt_transform_cb transform, void *arg)
+{
+ struct xe_ggtt_node *node;
+ int ret;
+
+ node = xe_ggtt_node_init(ggtt);
+ if (IS_ERR(node))
+ return ERR_CAST(node);
+
+ if (mutex_lock_interruptible(&ggtt->lock) < 0) {
+ ret = -ERESTARTSYS;
+ goto err;
+ }
+
+ ret = xe_ggtt_node_insert_locked(node, size, align, 0);
+ if (ret)
+ goto err_unlock;
+
+ if (transform)
+ transform(ggtt, node, pte_flags, ggtt->pt_ops->ggtt_set_pte, arg);
+ else
+ xe_ggtt_map_bo(ggtt, node, bo, pte_flags);
+
+ mutex_unlock(&ggtt->lock);
+ return node;
+
+err_unlock:
+ mutex_unlock(&ggtt->lock);
+err:
+ xe_ggtt_node_fini(node);
+ return ERR_PTR(ret);
+}
+
static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
u64 start, u64 end, struct drm_exec *exec)
{
@@ -816,8 +899,9 @@ static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
} else {
u16 cache_mode = bo->flags & XE_BO_FLAG_NEEDS_UC ? XE_CACHE_NONE : XE_CACHE_WB;
u16 pat_index = tile_to_xe(ggtt->tile)->pat.idx[cache_mode];
+ u64 pte = ggtt->pt_ops->pte_encode_flags(bo, pat_index);
- xe_ggtt_map_bo(ggtt, bo->ggtt_node[tile_id], bo, pat_index);
+ xe_ggtt_map_bo(ggtt, bo->ggtt_node[tile_id], bo, pte);
}
mutex_unlock(&ggtt->lock);
@@ -891,14 +975,12 @@ u64 xe_ggtt_largest_hole(struct xe_ggtt *ggtt, u64 alignment, u64 *spare)
{
const struct drm_mm *mm = &ggtt->mm;
const struct drm_mm_node *entry;
- u64 hole_min_start = xe_wopcm_size(tile_to_xe(ggtt->tile));
u64 hole_start, hole_end, hole_size;
u64 max_hole = 0;
mutex_lock(&ggtt->lock);
-
drm_mm_for_each_hole(entry, mm, hole_start, hole_end) {
- hole_start = max(hole_start, hole_min_start);
+ hole_start = max(hole_start, ggtt->start);
hole_start = ALIGN(hole_start, alignment);
hole_end = ALIGN_DOWN(hole_end, alignment);
if (hole_start >= hole_end)
@@ -1070,15 +1152,13 @@ u64 xe_ggtt_print_holes(struct xe_ggtt *ggtt, u64 alignment, struct drm_printer
{
const struct drm_mm *mm = &ggtt->mm;
const struct drm_mm_node *entry;
- u64 hole_min_start = xe_wopcm_size(tile_to_xe(ggtt->tile));
u64 hole_start, hole_end, hole_size;
u64 total = 0;
char buf[10];
mutex_lock(&ggtt->lock);
-
drm_mm_for_each_hole(entry, mm, hole_start, hole_end) {
- hole_start = max(hole_start, hole_min_start);
+ hole_start = max(hole_start, ggtt->start);
hole_start = ALIGN(hole_start, alignment);
hole_end = ALIGN_DOWN(hole_end, alignment);
if (hole_start >= hole_end)
@@ -1122,3 +1202,25 @@ u64 xe_ggtt_read_pte(struct xe_ggtt *ggtt, u64 offset)
{
return ioread64(ggtt->gsm + (offset / XE_PAGE_SIZE));
}
+
+/**
+ * xe_ggtt_node_addr - Get @node offset in GGTT.
+ * @node: &xe_ggtt_node
+ *
+ * Get the GGTT offset for allocated node.
+ */
+u64 xe_ggtt_node_addr(const struct xe_ggtt_node *node)
+{
+ return node->base.start;
+}
+
+/**
+ * xe_ggtt_node_size - Get @node allocation size.
+ * @node: &xe_ggtt_node
+ *
+ * Get the allocated node's size.
+ */
+u64 xe_ggtt_node_size(const struct xe_ggtt_node *node)
+{
+ return node->base.size;
+}
diff --git a/drivers/gpu/drm/xe/xe_ggtt.h b/drivers/gpu/drm/xe/xe_ggtt.h
index 93fea4b6079c..70d5e07ac4b6 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.h
+++ b/drivers/gpu/drm/xe/xe_ggtt.h
@@ -23,15 +23,18 @@ int xe_ggtt_node_insert_balloon_locked(struct xe_ggtt_node *node,
u64 start, u64 size);
void xe_ggtt_node_remove_balloon_locked(struct xe_ggtt_node *node);
void xe_ggtt_shift_nodes_locked(struct xe_ggtt *ggtt, s64 shift);
+u64 xe_ggtt_start(struct xe_ggtt *ggtt);
+u64 xe_ggtt_size(struct xe_ggtt *ggtt);
int xe_ggtt_node_insert(struct xe_ggtt_node *node, u32 size, u32 align);
-int xe_ggtt_node_insert_locked(struct xe_ggtt_node *node,
- u32 size, u32 align, u32 mm_flags);
+struct xe_ggtt_node *
+xe_ggtt_node_insert_transform(struct xe_ggtt *ggtt,
+ struct xe_bo *bo, u64 pte,
+ u64 size, u32 align,
+ xe_ggtt_transform_cb transform, void *arg);
void xe_ggtt_node_remove(struct xe_ggtt_node *node, bool invalidate);
bool xe_ggtt_node_allocated(const struct xe_ggtt_node *node);
size_t xe_ggtt_node_pt_size(const struct xe_ggtt_node *node);
-void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
- struct xe_bo *bo, u16 pat_index);
void xe_ggtt_map_bo_unlocked(struct xe_ggtt *ggtt, struct xe_bo *bo);
int xe_ggtt_insert_bo(struct xe_ggtt *ggtt, struct xe_bo *bo, struct drm_exec *exec);
int xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
@@ -58,4 +61,7 @@ void xe_ggtt_might_lock(struct xe_ggtt *ggtt);
u64 xe_ggtt_encode_pte_flags(struct xe_ggtt *ggtt, struct xe_bo *bo, u16 pat_index);
u64 xe_ggtt_read_pte(struct xe_ggtt *ggtt, u64 offset);
+u64 xe_ggtt_node_addr(const struct xe_ggtt_node *node);
+u64 xe_ggtt_node_size(const struct xe_ggtt_node *node);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_ggtt_types.h b/drivers/gpu/drm/xe/xe_ggtt_types.h
index dacd796f8184..d82b71a198bc 100644
--- a/drivers/gpu/drm/xe/xe_ggtt_types.h
+++ b/drivers/gpu/drm/xe/xe_ggtt_types.h
@@ -11,6 +11,7 @@
#include "xe_pt_types.h"
struct xe_bo;
+struct xe_ggtt_node;
struct xe_gt;
/**
@@ -22,7 +23,9 @@ struct xe_gt;
struct xe_ggtt {
/** @tile: Back pointer to tile where this GGTT belongs */
struct xe_tile *tile;
- /** @size: Total size of this GGTT */
+ /** @start: Start offset of GGTT */
+ u64 start;
+ /** @size: Total usable size of this GGTT */
u64 size;
#define XE_GGTT_FLAGS_64K BIT(0)
@@ -51,24 +54,11 @@ struct xe_ggtt {
struct workqueue_struct *wq;
};
-/**
- * struct xe_ggtt_node - A node in GGTT.
- *
- * This struct needs to be initialized (only-once) with xe_ggtt_node_init() before any node
- * insertion, reservation, or 'ballooning'.
- * It will, then, be finalized by either xe_ggtt_node_remove() or xe_ggtt_node_deballoon().
- */
-struct xe_ggtt_node {
- /** @ggtt: Back pointer to xe_ggtt where this region will be inserted at */
- struct xe_ggtt *ggtt;
- /** @base: A drm_mm_node */
- struct drm_mm_node base;
- /** @delayed_removal_work: The work struct for the delayed removal */
- struct work_struct delayed_removal_work;
- /** @invalidate_on_remove: If it needs invalidation upon removal */
- bool invalidate_on_remove;
-};
-
+typedef void (*xe_ggtt_set_pte_fn)(struct xe_ggtt *ggtt, u64 addr, u64 pte);
+typedef void (*xe_ggtt_transform_cb)(struct xe_ggtt *ggtt,
+ struct xe_ggtt_node *node,
+ u64 pte_flags,
+ xe_ggtt_set_pte_fn set_pte, void *arg);
/**
* struct xe_ggtt_pt_ops - GGTT Page table operations
* Which can vary from platform to platform.
@@ -76,8 +66,10 @@ struct xe_ggtt_node {
struct xe_ggtt_pt_ops {
/** @pte_encode_flags: Encode PTE flags for a given BO */
u64 (*pte_encode_flags)(struct xe_bo *bo, u16 pat_index);
+
/** @ggtt_set_pte: Directly write into GGTT's PTE */
- void (*ggtt_set_pte)(struct xe_ggtt *ggtt, u64 addr, u64 pte);
+ xe_ggtt_set_pte_fn ggtt_set_pte;
+
/** @ggtt_get_pte: Directly read from GGTT's PTE */
u64 (*ggtt_get_pte)(struct xe_ggtt *ggtt, u64 addr);
};
diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.c b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
index f91e06d03511..9c8004d5dd91 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler.c
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
@@ -7,7 +7,7 @@
static void xe_sched_process_msg_queue(struct xe_gpu_scheduler *sched)
{
- if (!READ_ONCE(sched->base.pause_submit))
+ if (!drm_sched_is_stopped(&sched->base))
queue_work(sched->base.submit_wq, &sched->work_process_msg);
}
@@ -43,7 +43,7 @@ static void xe_sched_process_msg_work(struct work_struct *w)
container_of(w, struct xe_gpu_scheduler, work_process_msg);
struct xe_sched_msg *msg;
- if (READ_ONCE(sched->base.pause_submit))
+ if (drm_sched_is_stopped(&sched->base))
return;
msg = xe_sched_get_msg(sched);
@@ -77,6 +77,7 @@ int xe_sched_init(struct xe_gpu_scheduler *sched,
};
sched->ops = xe_ops;
+ spin_lock_init(&sched->msg_lock);
INIT_LIST_HEAD(&sched->msgs);
INIT_WORK(&sched->work_process_msg, xe_sched_process_msg_work);
@@ -117,7 +118,7 @@ void xe_sched_add_msg(struct xe_gpu_scheduler *sched,
void xe_sched_add_msg_locked(struct xe_gpu_scheduler *sched,
struct xe_sched_msg *msg)
{
- lockdep_assert_held(&sched->base.job_list_lock);
+ lockdep_assert_held(&sched->msg_lock);
list_add_tail(&msg->link, &sched->msgs);
xe_sched_process_msg_queue(sched);
@@ -131,7 +132,7 @@ void xe_sched_add_msg_locked(struct xe_gpu_scheduler *sched,
void xe_sched_add_msg_head(struct xe_gpu_scheduler *sched,
struct xe_sched_msg *msg)
{
- lockdep_assert_held(&sched->base.job_list_lock);
+ lockdep_assert_held(&sched->msg_lock);
list_add(&msg->link, &sched->msgs);
xe_sched_process_msg_queue(sched);
diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.h b/drivers/gpu/drm/xe/xe_gpu_scheduler.h
index c7a77a3a9681..664c2db56af3 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler.h
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.h
@@ -33,12 +33,12 @@ void xe_sched_add_msg_head(struct xe_gpu_scheduler *sched,
static inline void xe_sched_msg_lock(struct xe_gpu_scheduler *sched)
{
- spin_lock(&sched->base.job_list_lock);
+ spin_lock(&sched->msg_lock);
}
static inline void xe_sched_msg_unlock(struct xe_gpu_scheduler *sched)
{
- spin_unlock(&sched->base.job_list_lock);
+ spin_unlock(&sched->msg_lock);
}
static inline void xe_sched_stop(struct xe_gpu_scheduler *sched)
@@ -56,12 +56,9 @@ static inline void xe_sched_resubmit_jobs(struct xe_gpu_scheduler *sched)
struct drm_sched_job *s_job;
bool restore_replay = false;
- list_for_each_entry(s_job, &sched->base.pending_list, list) {
- struct drm_sched_fence *s_fence = s_job->s_fence;
- struct dma_fence *hw_fence = s_fence->parent;
-
+ drm_sched_for_each_pending_job(s_job, &sched->base, NULL) {
restore_replay |= to_xe_sched_job(s_job)->restore_replay;
- if (restore_replay || (hw_fence && !dma_fence_is_signaled(hw_fence)))
+ if (restore_replay || !drm_sched_job_is_signaled(s_job))
sched->base.ops->run_job(s_job);
}
}
@@ -72,14 +69,6 @@ xe_sched_invalidate_job(struct xe_sched_job *job, int threshold)
return drm_sched_invalidate_job(&job->drm, threshold);
}
-static inline void xe_sched_add_pending_job(struct xe_gpu_scheduler *sched,
- struct xe_sched_job *job)
-{
- spin_lock(&sched->base.job_list_lock);
- list_add(&job->drm.list, &sched->base.pending_list);
- spin_unlock(&sched->base.job_list_lock);
-}
-
/**
* xe_sched_first_pending_job() - Find first pending job which is unsignaled
* @sched: Xe GPU scheduler
@@ -89,21 +78,13 @@ static inline void xe_sched_add_pending_job(struct xe_gpu_scheduler *sched,
static inline
struct xe_sched_job *xe_sched_first_pending_job(struct xe_gpu_scheduler *sched)
{
- struct xe_sched_job *job, *r_job = NULL;
-
- spin_lock(&sched->base.job_list_lock);
- list_for_each_entry(job, &sched->base.pending_list, drm.list) {
- struct drm_sched_fence *s_fence = job->drm.s_fence;
- struct dma_fence *hw_fence = s_fence->parent;
+ struct drm_sched_job *job;
- if (hw_fence && !dma_fence_is_signaled(hw_fence)) {
- r_job = job;
- break;
- }
- }
- spin_unlock(&sched->base.job_list_lock);
+ drm_sched_for_each_pending_job(job, &sched->base, NULL)
+ if (!drm_sched_job_is_signaled(job))
+ return to_xe_sched_job(job);
- return r_job;
+ return NULL;
}
static inline int
diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler_types.h b/drivers/gpu/drm/xe/xe_gpu_scheduler_types.h
index 6731b13da8bb..63d9bf92583c 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler_types.h
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler_types.h
@@ -47,6 +47,8 @@ struct xe_gpu_scheduler {
const struct xe_sched_backend_ops *ops;
/** @msgs: list of messages to be processed in @work_process_msg */
struct list_head msgs;
+ /** @msg_lock: Message lock */
+ spinlock_t msg_lock;
/** @work_process_msg: processes messages */
struct work_struct work_process_msg;
};
diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c
index dd69cb834f8e..e5c234f3d795 100644
--- a/drivers/gpu/drm/xe/xe_gsc.c
+++ b/drivers/gpu/drm/xe/xe_gsc.c
@@ -352,7 +352,6 @@ static void gsc_work(struct work_struct *work)
struct xe_gsc *gsc = container_of(work, typeof(*gsc), work);
struct xe_gt *gt = gsc_to_gt(gsc);
struct xe_device *xe = gt_to_xe(gt);
- unsigned int fw_ref;
u32 actions;
int ret;
@@ -361,13 +360,12 @@ static void gsc_work(struct work_struct *work)
gsc->work_actions = 0;
spin_unlock_irq(&gsc->lock);
- xe_pm_runtime_get(xe);
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
+ guard(xe_pm_runtime)(xe);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC);
if (actions & GSC_ACTION_ER_COMPLETE) {
- ret = gsc_er_complete(gt);
- if (ret)
- goto out;
+ if (gsc_er_complete(gt))
+ return;
}
if (actions & GSC_ACTION_FW_LOAD) {
@@ -380,10 +378,6 @@ static void gsc_work(struct work_struct *work)
if (actions & GSC_ACTION_SW_PROXY)
xe_gsc_proxy_request_handler(gsc);
-
-out:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
- xe_pm_runtime_put(xe);
}
void xe_gsc_hwe_irq_handler(struct xe_hw_engine *hwe, u16 intr_vec)
@@ -420,15 +414,16 @@ int xe_gsc_init(struct xe_gsc *gsc)
}
/*
- * Some platforms can have GuC but not GSC. That would cause
- * xe_uc_fw_init(gsc) to return a "not supported" failure code and abort
- * all firmware loading. So check for GSC being enabled before
- * propagating the failure back up. That way the higher level will keep
- * going and load GuC as appropriate.
+ * Starting from BMG the GSC is no longer needed for MC6 entry, so the
+ * only missing features if the FW is lacking would be the content
+ * protection ones. This is acceptable, so we allow the driver load to
+ * continue if the GSC FW is missing.
*/
ret = xe_uc_fw_init(&gsc->fw);
if (!xe_uc_fw_is_enabled(&gsc->fw))
return 0;
+ else if (gt_to_xe(gt)->info.platform >= XE_BATTLEMAGE && !xe_uc_fw_is_available(&gsc->fw))
+ return 0;
else if (ret)
goto out;
@@ -615,17 +610,16 @@ void xe_gsc_print_info(struct xe_gsc *gsc, struct drm_printer *p)
{
struct xe_gt *gt = gsc_to_gt(gsc);
struct xe_mmio *mmio = &gt->mmio;
- unsigned int fw_ref;
xe_uc_fw_print(&gsc->fw, p);
drm_printf(p, "\tfound security version %u\n", gsc->security_version);
- if (!xe_uc_fw_is_enabled(&gsc->fw))
+ if (!xe_uc_fw_is_available(&gsc->fw))
return;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC);
+ if (!fw_ref.domains)
return;
drm_printf(p, "\nHECI1 FWSTS: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
@@ -635,6 +629,4 @@ void xe_gsc_print_info(struct xe_gsc *gsc, struct drm_printer *p)
xe_mmio_read32(mmio, HECI_FWSTS4(MTL_GSC_HECI1_BASE)),
xe_mmio_read32(mmio, HECI_FWSTS5(MTL_GSC_HECI1_BASE)),
xe_mmio_read32(mmio, HECI_FWSTS6(MTL_GSC_HECI1_BASE)));
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
diff --git a/drivers/gpu/drm/xe/xe_gsc_debugfs.c b/drivers/gpu/drm/xe/xe_gsc_debugfs.c
index 461d7e99c2b3..d4977e666946 100644
--- a/drivers/gpu/drm/xe/xe_gsc_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_gsc_debugfs.c
@@ -7,11 +7,10 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
-#include "xe_device.h"
-#include "xe_gt.h"
+#include "xe_gt_types.h"
#include "xe_gsc.h"
-#include "xe_macros.h"
#include "xe_pm.h"
static struct xe_gt *
@@ -37,9 +36,8 @@ static int gsc_info(struct seq_file *m, void *data)
struct xe_device *xe = gsc_to_xe(gsc);
struct drm_printer p = drm_seq_file_printer(m);
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
xe_gsc_print_info(gsc, &p);
- xe_pm_runtime_put(xe);
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_gsc_proxy.c b/drivers/gpu/drm/xe/xe_gsc_proxy.c
index 464282a89eef..42438b21f235 100644
--- a/drivers/gpu/drm/xe/xe_gsc_proxy.c
+++ b/drivers/gpu/drm/xe/xe_gsc_proxy.c
@@ -18,8 +18,8 @@
#include "xe_force_wake.h"
#include "xe_gsc.h"
#include "xe_gsc_submit.h"
-#include "xe_gt.h"
#include "xe_gt_printk.h"
+#include "xe_gt_types.h"
#include "xe_map.h"
#include "xe_mmio.h"
#include "xe_pm.h"
@@ -440,22 +440,19 @@ static void xe_gsc_proxy_remove(void *arg)
struct xe_gsc *gsc = arg;
struct xe_gt *gt = gsc_to_gt(gsc);
struct xe_device *xe = gt_to_xe(gt);
- unsigned int fw_ref = 0;
if (!gsc->proxy.component_added)
return;
/* disable HECI2 IRQs */
- xe_pm_runtime_get(xe);
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
- if (!fw_ref)
- xe_gt_err(gt, "failed to get forcewake to disable GSC interrupts\n");
+ scoped_guard(xe_pm_runtime, xe) {
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC);
+ if (!fw_ref.domains)
+ xe_gt_err(gt, "failed to get forcewake to disable GSC interrupts\n");
- /* try do disable irq even if forcewake failed */
- gsc_proxy_irq_toggle(gsc, false);
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
- xe_pm_runtime_put(xe);
+ /* try do disable irq even if forcewake failed */
+ gsc_proxy_irq_toggle(gsc, false);
+ }
xe_gsc_wait_for_worker_completion(gsc);
diff --git a/drivers/gpu/drm/xe/xe_gsc_submit.c b/drivers/gpu/drm/xe/xe_gsc_submit.c
index 9ede483d37ef..08082b596501 100644
--- a/drivers/gpu/drm/xe/xe_gsc_submit.c
+++ b/drivers/gpu/drm/xe/xe_gsc_submit.c
@@ -11,12 +11,10 @@
#include "xe_assert.h"
#include "xe_bb.h"
#include "xe_exec_queue.h"
-#include "xe_gt_printk.h"
#include "xe_gt_types.h"
#include "xe_map.h"
#include "xe_sched_job.h"
#include "instructions/xe_gsc_commands.h"
-#include "regs/xe_gsc_regs.h"
#define GSC_HDR_SIZE (sizeof(struct intel_gsc_mtl_header)) /* shorthand define */
diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c
index dbb5e7a9bc6a..9d090d0f2438 100644
--- a/drivers/gpu/drm/xe/xe_gt.c
+++ b/drivers/gpu/drm/xe/xe_gt.c
@@ -13,13 +13,11 @@
#include <generated/xe_wa_oob.h>
#include "instructions/xe_alu_commands.h"
-#include "instructions/xe_gfxpipe_commands.h"
#include "instructions/xe_mi_commands.h"
#include "regs/xe_engine_regs.h"
#include "regs/xe_gt_regs.h"
#include "xe_assert.h"
#include "xe_bb.h"
-#include "xe_bo.h"
#include "xe_device.h"
#include "xe_eu_stall.h"
#include "xe_exec_queue.h"
@@ -103,14 +101,13 @@ void xe_gt_sanitize(struct xe_gt *gt)
static void xe_gt_enable_host_l2_vram(struct xe_gt *gt)
{
- unsigned int fw_ref;
u32 reg;
if (!XE_GT_WA(gt, 16023588340))
return;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return;
if (xe_gt_is_main_type(gt)) {
@@ -120,12 +117,10 @@ static void xe_gt_enable_host_l2_vram(struct xe_gt *gt)
}
xe_gt_mcr_multicast_write(gt, XEHPC_L3CLOS_MASK(3), 0xF);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
static void xe_gt_disable_host_l2_vram(struct xe_gt *gt)
{
- unsigned int fw_ref;
u32 reg;
if (!XE_GT_WA(gt, 16023588340))
@@ -134,15 +129,43 @@ static void xe_gt_disable_host_l2_vram(struct xe_gt *gt)
if (xe_gt_is_media_type(gt))
return;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return;
reg = xe_gt_mcr_unicast_read_any(gt, XE2_GAMREQSTRM_CTRL);
reg &= ~CG_DIS_CNTLBUS;
xe_gt_mcr_multicast_write(gt, XE2_GAMREQSTRM_CTRL, reg);
+}
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
+static void xe_gt_enable_comp_1wcoh(struct xe_gt *gt)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ unsigned int fw_ref;
+ u32 reg;
+
+ if (IS_SRIOV_VF(xe))
+ return;
+
+ if (GRAPHICS_VER(xe) >= 30 && xe->info.has_flat_ccs) {
+ fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref)
+ return;
+
+ reg = xe_gt_mcr_unicast_read_any(gt, XE2_GAMREQSTRM_CTRL);
+ reg |= EN_CMP_1WCOH;
+ xe_gt_mcr_multicast_write(gt, XE2_GAMREQSTRM_CTRL, reg);
+
+ if (xe_gt_is_media_type(gt)) {
+ xe_mmio_rmw32(&gt->mmio, XE2_GAMWALK_CTRL_MEDIA, 0, EN_CMP_1WCOH_GW);
+ } else {
+ reg = xe_gt_mcr_unicast_read_any(gt, XE2_GAMWALK_CTRL_3D);
+ reg |= EN_CMP_1WCOH_GW;
+ xe_gt_mcr_multicast_write(gt, XE2_GAMWALK_CTRL_3D, reg);
+ }
+
+ xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ }
}
static void gt_reset_worker(struct work_struct *w);
@@ -389,7 +412,6 @@ put_exec_queue:
int xe_gt_init_early(struct xe_gt *gt)
{
- unsigned int fw_ref;
int err;
if (IS_SRIOV_PF(gt_to_xe(gt))) {
@@ -436,13 +458,12 @@ int xe_gt_init_early(struct xe_gt *gt)
if (err)
return err;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
xe_gt_mcr_init_early(gt);
xe_pat_init(gt);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
return 0;
}
@@ -460,25 +481,25 @@ static void dump_pat_on_error(struct xe_gt *gt)
static int gt_init_with_gt_forcewake(struct xe_gt *gt)
{
- unsigned int fw_ref;
int err;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
err = xe_uc_init(&gt->uc);
if (err)
- goto err_force_wake;
+ return err;
xe_gt_topology_init(gt);
xe_gt_mcr_init(gt);
xe_gt_enable_host_l2_vram(gt);
+ xe_gt_enable_comp_1wcoh(gt);
if (xe_gt_is_main_type(gt)) {
err = xe_ggtt_init(gt_to_tile(gt)->mem.ggtt);
if (err)
- goto err_force_wake;
+ return err;
if (IS_SRIOV_PF(gt_to_xe(gt)))
xe_lmtt_init(&gt_to_tile(gt)->sriov.pf.lmtt);
}
@@ -492,17 +513,17 @@ static int gt_init_with_gt_forcewake(struct xe_gt *gt)
err = xe_hw_engines_init_early(gt);
if (err) {
dump_pat_on_error(gt);
- goto err_force_wake;
+ return err;
}
err = xe_hw_engine_class_sysfs_init(gt);
if (err)
- goto err_force_wake;
+ return err;
/* Initialize CCS mode sysfs after early initialization of HW engines */
err = xe_gt_ccs_mode_sysfs_init(gt);
if (err)
- goto err_force_wake;
+ return err;
/*
* Stash hardware-reported version. Since this register does not exist
@@ -510,25 +531,16 @@ static int gt_init_with_gt_forcewake(struct xe_gt *gt)
*/
gt->info.gmdid = xe_mmio_read32(&gt->mmio, GMD_ID);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
return 0;
-
-err_force_wake:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
-
- return err;
}
static int gt_init_with_all_forcewake(struct xe_gt *gt)
{
- unsigned int fw_ref;
int err;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) {
- err = -ETIMEDOUT;
- goto err_force_wake;
- }
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
+ return -ETIMEDOUT;
xe_gt_mcr_set_implicit_defaults(gt);
xe_wa_process_gt(gt);
@@ -537,20 +549,20 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt)
err = xe_gt_clock_init(gt);
if (err)
- goto err_force_wake;
+ return err;
xe_mocs_init(gt);
err = xe_execlist_init(gt);
if (err)
- goto err_force_wake;
+ return err;
err = xe_hw_engines_init(gt);
if (err)
- goto err_force_wake;
+ return err;
err = xe_uc_init_post_hwconfig(&gt->uc);
if (err)
- goto err_force_wake;
+ return err;
if (xe_gt_is_main_type(gt)) {
/*
@@ -561,10 +573,8 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt)
gt->usm.bb_pool = xe_sa_bo_manager_init(gt_to_tile(gt),
IS_DGFX(xe) ? SZ_1M : SZ_512K, 16);
- if (IS_ERR(gt->usm.bb_pool)) {
- err = PTR_ERR(gt->usm.bb_pool);
- goto err_force_wake;
- }
+ if (IS_ERR(gt->usm.bb_pool))
+ return PTR_ERR(gt->usm.bb_pool);
}
}
@@ -573,12 +583,12 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt)
err = xe_migrate_init(tile->migrate);
if (err)
- goto err_force_wake;
+ return err;
}
err = xe_uc_load_hw(&gt->uc);
if (err)
- goto err_force_wake;
+ return err;
/* Configure default CCS mode of 1 engine with all resources */
if (xe_gt_ccs_mode_enabled(gt)) {
@@ -592,14 +602,7 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt)
if (IS_SRIOV_PF(gt_to_xe(gt)))
xe_gt_sriov_pf_init_hw(gt);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
-
return 0;
-
-err_force_wake:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
-
- return err;
}
static void xe_gt_fini(void *arg)
@@ -771,6 +774,7 @@ static int do_gt_restart(struct xe_gt *gt)
xe_pat_init(gt);
xe_gt_enable_host_l2_vram(gt);
+ xe_gt_enable_comp_1wcoh(gt);
xe_gt_mcr_set_implicit_defaults(gt);
xe_reg_sr_apply_mmio(&gt->reg_sr, gt);
@@ -797,9 +801,6 @@ static int do_gt_restart(struct xe_gt *gt)
xe_gt_sriov_pf_init_hw(gt);
xe_mocs_init(gt);
- err = xe_uc_start(&gt->uc);
- if (err)
- return err;
for_each_hw_engine(hwe, gt, id)
xe_reg_sr_apply_mmio(&hwe->reg_sr, gt);
@@ -807,6 +808,10 @@ static int do_gt_restart(struct xe_gt *gt)
/* Get CCS mode in sync between sw/hw */
xe_gt_apply_ccs_mode(gt);
+ err = xe_uc_start(&gt->uc);
+ if (err)
+ return err;
+
/* Restore GT freq to expected values */
xe_gt_sanitize_freq(gt);
@@ -901,56 +906,42 @@ void xe_gt_reset_async(struct xe_gt *gt)
void xe_gt_suspend_prepare(struct xe_gt *gt)
{
- unsigned int fw_ref;
-
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
-
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
xe_uc_suspend_prepare(&gt->uc);
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
int xe_gt_suspend(struct xe_gt *gt)
{
- unsigned int fw_ref;
int err;
xe_gt_dbg(gt, "suspending\n");
xe_gt_sanitize(gt);
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
- goto err_msg;
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) {
+ xe_gt_err(gt, "suspend failed (%pe)\n", ERR_PTR(-ETIMEDOUT));
+ return -ETIMEDOUT;
+ }
err = xe_uc_suspend(&gt->uc);
- if (err)
- goto err_force_wake;
+ if (err) {
+ xe_gt_err(gt, "suspend failed (%pe)\n", ERR_PTR(err));
+ return err;
+ }
xe_gt_idle_disable_pg(gt);
xe_gt_disable_host_l2_vram(gt);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
xe_gt_dbg(gt, "suspended\n");
return 0;
-
-err_msg:
- err = -ETIMEDOUT;
-err_force_wake:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
- xe_gt_err(gt, "suspend failed (%pe)\n", ERR_PTR(err));
-
- return err;
}
void xe_gt_shutdown(struct xe_gt *gt)
{
- unsigned int fw_ref;
-
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
do_gt_reset(gt);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
/**
@@ -975,32 +966,72 @@ int xe_gt_sanitize_freq(struct xe_gt *gt)
int xe_gt_resume(struct xe_gt *gt)
{
- unsigned int fw_ref;
int err;
xe_gt_dbg(gt, "resuming\n");
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
- goto err_msg;
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) {
+ xe_gt_err(gt, "resume failed (%pe)\n", ERR_PTR(-ETIMEDOUT));
+ return -ETIMEDOUT;
+ }
err = do_gt_restart(gt);
if (err)
- goto err_force_wake;
+ return err;
xe_gt_idle_enable_pg(gt);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
xe_gt_dbg(gt, "resumed\n");
return 0;
+}
+
+/**
+ * xe_gt_runtime_suspend() - GT runtime suspend
+ * @gt: the GT object
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int xe_gt_runtime_suspend(struct xe_gt *gt)
+{
+ xe_gt_dbg(gt, "runtime suspending\n");
+
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) {
+ xe_gt_err(gt, "runtime suspend failed (%pe)\n", ERR_PTR(-ETIMEDOUT));
+ return -ETIMEDOUT;
+ }
+
+ xe_uc_runtime_suspend(&gt->uc);
+ xe_gt_disable_host_l2_vram(gt);
+
+ xe_gt_dbg(gt, "runtime suspended\n");
+
+ return 0;
+}
-err_msg:
- err = -ETIMEDOUT;
-err_force_wake:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
- xe_gt_err(gt, "resume failed (%pe)\n", ERR_PTR(err));
+/**
+ * xe_gt_runtime_resume() - GT runtime resume
+ * @gt: the GT object
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int xe_gt_runtime_resume(struct xe_gt *gt)
+{
+ xe_gt_dbg(gt, "runtime resuming\n");
- return err;
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) {
+ xe_gt_err(gt, "runtime resume failed (%pe)\n", ERR_PTR(-ETIMEDOUT));
+ return -ETIMEDOUT;
+ }
+
+ xe_gt_enable_host_l2_vram(gt);
+ xe_uc_runtime_resume(&gt->uc);
+
+ xe_gt_dbg(gt, "runtime resumed\n");
+
+ return 0;
}
struct xe_hw_engine *xe_gt_hw_engine(struct xe_gt *gt,
diff --git a/drivers/gpu/drm/xe/xe_gt.h b/drivers/gpu/drm/xe/xe_gt.h
index 9d710049da45..de7e47763411 100644
--- a/drivers/gpu/drm/xe/xe_gt.h
+++ b/drivers/gpu/drm/xe/xe_gt.h
@@ -20,7 +20,17 @@
for_each_if(((hwe__) = (gt__)->hw_engines + (id__)) && \
xe_hw_engine_is_valid((hwe__)))
-#define CCS_MASK(gt) (((gt)->info.engine_mask & XE_HW_ENGINE_CCS_MASK) >> XE_HW_ENGINE_CCS0)
+#define XE_ENGINE_INSTANCES_FROM_MASK(gt, NAME) \
+ (((gt)->info.engine_mask & XE_HW_ENGINE_##NAME##_MASK) >> XE_HW_ENGINE_##NAME##0)
+
+#define RCS_INSTANCES(gt) XE_ENGINE_INSTANCES_FROM_MASK(gt, RCS)
+#define VCS_INSTANCES(gt) XE_ENGINE_INSTANCES_FROM_MASK(gt, VCS)
+#define VECS_INSTANCES(gt) XE_ENGINE_INSTANCES_FROM_MASK(gt, VECS)
+#define CCS_INSTANCES(gt) XE_ENGINE_INSTANCES_FROM_MASK(gt, CCS)
+#define GSCCS_INSTANCES(gt) XE_ENGINE_INSTANCES_FROM_MASK(gt, GSCCS)
+
+/* Our devices have up to 4 media slices */
+#define MAX_MEDIA_SLICES 4
#define GT_VER(gt) ({ \
typeof(gt) gt_ = (gt); \
@@ -58,6 +68,8 @@ int xe_gt_suspend(struct xe_gt *gt);
void xe_gt_shutdown(struct xe_gt *gt);
int xe_gt_resume(struct xe_gt *gt);
void xe_gt_reset_async(struct xe_gt *gt);
+int xe_gt_runtime_resume(struct xe_gt *gt);
+int xe_gt_runtime_suspend(struct xe_gt *gt);
void xe_gt_sanitize(struct xe_gt *gt);
int xe_gt_sanitize_freq(struct xe_gt *gt);
diff --git a/drivers/gpu/drm/xe/xe_gt_ccs_mode.c b/drivers/gpu/drm/xe/xe_gt_ccs_mode.c
index 50fffc9ebf62..91ac22ef5703 100644
--- a/drivers/gpu/drm/xe/xe_gt_ccs_mode.c
+++ b/drivers/gpu/drm/xe/xe_gt_ccs_mode.c
@@ -17,7 +17,7 @@
static void __xe_gt_apply_ccs_mode(struct xe_gt *gt, u32 num_engines)
{
u32 mode = CCS_MODE_CSLICE_0_3_MASK; /* disable all by default */
- int num_slices = hweight32(CCS_MASK(gt));
+ int num_slices = hweight32(CCS_INSTANCES(gt));
struct xe_device *xe = gt_to_xe(gt);
int width, cslice = 0;
u32 config = 0;
@@ -59,7 +59,7 @@ static void __xe_gt_apply_ccs_mode(struct xe_gt *gt, u32 num_engines)
config |= BIT(hwe->instance) << XE_HW_ENGINE_CCS0;
/* If a slice is fused off, leave disabled */
- while ((CCS_MASK(gt) & BIT(cslice)) == 0)
+ while ((CCS_INSTANCES(gt) & BIT(cslice)) == 0)
cslice++;
mode &= ~CCS_MODE_CSLICE(cslice, CCS_MODE_CSLICE_MASK);
@@ -94,7 +94,7 @@ num_cslices_show(struct device *kdev,
{
struct xe_gt *gt = kobj_to_gt(&kdev->kobj);
- return sysfs_emit(buf, "%u\n", hweight32(CCS_MASK(gt)));
+ return sysfs_emit(buf, "%u\n", hweight32(CCS_INSTANCES(gt)));
}
static DEVICE_ATTR_RO(num_cslices);
@@ -131,7 +131,7 @@ ccs_mode_store(struct device *kdev, struct device_attribute *attr,
* Ensure number of engines specified is valid and there is an
* exact multiple of engines for slices.
*/
- num_slices = hweight32(CCS_MASK(gt));
+ num_slices = hweight32(CCS_INSTANCES(gt));
if (!num_engines || num_engines > num_slices || num_slices % num_engines) {
xe_gt_dbg(gt, "Invalid compute config, %d engines %d slices\n",
num_engines, num_slices);
diff --git a/drivers/gpu/drm/xe/xe_gt_ccs_mode.h b/drivers/gpu/drm/xe/xe_gt_ccs_mode.h
index f8779852cf0d..ef3b853f5c8c 100644
--- a/drivers/gpu/drm/xe/xe_gt_ccs_mode.h
+++ b/drivers/gpu/drm/xe/xe_gt_ccs_mode.h
@@ -17,7 +17,7 @@ int xe_gt_ccs_mode_sysfs_init(struct xe_gt *gt);
static inline bool xe_gt_ccs_mode_enabled(const struct xe_gt *gt)
{
/* Check if there are more than one compute engines available */
- return hweight32(CCS_MASK(gt)) > 1;
+ return hweight32(CCS_INSTANCES(gt)) > 1;
}
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_clock.c b/drivers/gpu/drm/xe/xe_gt_clock.c
index bfc25c46f798..53b3835192da 100644
--- a/drivers/gpu/drm/xe/xe_gt_clock.c
+++ b/drivers/gpu/drm/xe/xe_gt_clock.c
@@ -8,12 +8,8 @@
#include "xe_gt_clock.h"
#include "regs/xe_gt_regs.h"
-#include "regs/xe_regs.h"
-#include "xe_assert.h"
-#include "xe_device.h"
-#include "xe_gt.h"
+#include "xe_gt_types.h"
#include "xe_gt_printk.h"
-#include "xe_macros.h"
#include "xe_mmio.h"
#define f19_2_mhz 19200000
diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.c b/drivers/gpu/drm/xe/xe_gt_debugfs.c
index e4fd632f43cf..4363bc9c3606 100644
--- a/drivers/gpu/drm/xe/xe_gt_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_debugfs.c
@@ -22,7 +22,6 @@
#include "xe_guc_hwconfig.h"
#include "xe_hw_engine.h"
#include "xe_lrc.h"
-#include "xe_macros.h"
#include "xe_mocs.h"
#include "xe_pat.h"
#include "xe_pm.h"
@@ -105,35 +104,24 @@ int xe_gt_debugfs_show_with_rpm(struct seq_file *m, void *data)
struct drm_info_node *node = m->private;
struct xe_gt *gt = node_to_gt(node);
struct xe_device *xe = gt_to_xe(gt);
- int ret;
-
- xe_pm_runtime_get(xe);
- ret = xe_gt_debugfs_simple_show(m, data);
- xe_pm_runtime_put(xe);
- return ret;
+ guard(xe_pm_runtime)(xe);
+ return xe_gt_debugfs_simple_show(m, data);
}
static int hw_engines(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_hw_engine *hwe;
enum xe_hw_engine_id id;
- unsigned int fw_ref;
- int ret = 0;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) {
- ret = -ETIMEDOUT;
- goto fw_put;
- }
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
+ return -ETIMEDOUT;
for_each_hw_engine(hwe, gt, id)
xe_hw_engine_print(hwe, p);
-fw_put:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
-
- return ret;
+ return 0;
}
static int steering(struct xe_gt *gt, struct drm_printer *p)
@@ -220,6 +208,7 @@ static const struct drm_info_list vf_safe_debugfs_list[] = {
{ "default_lrc_vcs", .show = xe_gt_debugfs_show_with_rpm, .data = vcs_default_lrc },
{ "default_lrc_vecs", .show = xe_gt_debugfs_show_with_rpm, .data = vecs_default_lrc },
{ "hwconfig", .show = xe_gt_debugfs_show_with_rpm, .data = hwconfig },
+ { "pat_sw_config", .show = xe_gt_debugfs_simple_show, .data = xe_pat_dump_sw_config },
};
/* everything else should be added here */
@@ -269,9 +258,8 @@ static void force_reset(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
xe_gt_reset_async(gt);
- xe_pm_runtime_put(xe);
}
static ssize_t force_reset_write(struct file *file,
@@ -297,9 +285,8 @@ static void force_reset_sync(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
xe_gt_reset(gt);
- xe_pm_runtime_put(xe);
}
static ssize_t force_reset_sync_write(struct file *file,
diff --git a/drivers/gpu/drm/xe/xe_gt_freq.c b/drivers/gpu/drm/xe/xe_gt_freq.c
index 849ea6c86e8e..a40dd074106f 100644
--- a/drivers/gpu/drm/xe/xe_gt_freq.c
+++ b/drivers/gpu/drm/xe/xe_gt_freq.c
@@ -70,9 +70,8 @@ static ssize_t act_freq_show(struct kobject *kobj,
struct xe_guc_pc *pc = dev_to_pc(dev);
u32 freq;
- xe_pm_runtime_get(dev_to_xe(dev));
+ guard(xe_pm_runtime)(dev_to_xe(dev));
freq = xe_guc_pc_get_act_freq(pc);
- xe_pm_runtime_put(dev_to_xe(dev));
return sysfs_emit(buf, "%d\n", freq);
}
@@ -86,9 +85,8 @@ static ssize_t cur_freq_show(struct kobject *kobj,
u32 freq;
ssize_t ret;
- xe_pm_runtime_get(dev_to_xe(dev));
+ guard(xe_pm_runtime)(dev_to_xe(dev));
ret = xe_guc_pc_get_cur_freq(pc, &freq);
- xe_pm_runtime_put(dev_to_xe(dev));
if (ret)
return ret;
@@ -113,9 +111,8 @@ static ssize_t rpe_freq_show(struct kobject *kobj,
struct xe_guc_pc *pc = dev_to_pc(dev);
u32 freq;
- xe_pm_runtime_get(dev_to_xe(dev));
+ guard(xe_pm_runtime)(dev_to_xe(dev));
freq = xe_guc_pc_get_rpe_freq(pc);
- xe_pm_runtime_put(dev_to_xe(dev));
return sysfs_emit(buf, "%d\n", freq);
}
@@ -128,9 +125,8 @@ static ssize_t rpa_freq_show(struct kobject *kobj,
struct xe_guc_pc *pc = dev_to_pc(dev);
u32 freq;
- xe_pm_runtime_get(dev_to_xe(dev));
+ guard(xe_pm_runtime)(dev_to_xe(dev));
freq = xe_guc_pc_get_rpa_freq(pc);
- xe_pm_runtime_put(dev_to_xe(dev));
return sysfs_emit(buf, "%d\n", freq);
}
@@ -154,9 +150,8 @@ static ssize_t min_freq_show(struct kobject *kobj,
u32 freq;
ssize_t ret;
- xe_pm_runtime_get(dev_to_xe(dev));
+ guard(xe_pm_runtime)(dev_to_xe(dev));
ret = xe_guc_pc_get_min_freq(pc, &freq);
- xe_pm_runtime_put(dev_to_xe(dev));
if (ret)
return ret;
@@ -175,9 +170,8 @@ static ssize_t min_freq_store(struct kobject *kobj,
if (ret)
return ret;
- xe_pm_runtime_get(dev_to_xe(dev));
+ guard(xe_pm_runtime)(dev_to_xe(dev));
ret = xe_guc_pc_set_min_freq(pc, freq);
- xe_pm_runtime_put(dev_to_xe(dev));
if (ret)
return ret;
@@ -193,9 +187,8 @@ static ssize_t max_freq_show(struct kobject *kobj,
u32 freq;
ssize_t ret;
- xe_pm_runtime_get(dev_to_xe(dev));
+ guard(xe_pm_runtime)(dev_to_xe(dev));
ret = xe_guc_pc_get_max_freq(pc, &freq);
- xe_pm_runtime_put(dev_to_xe(dev));
if (ret)
return ret;
@@ -214,9 +207,8 @@ static ssize_t max_freq_store(struct kobject *kobj,
if (ret)
return ret;
- xe_pm_runtime_get(dev_to_xe(dev));
+ guard(xe_pm_runtime)(dev_to_xe(dev));
ret = xe_guc_pc_set_max_freq(pc, freq);
- xe_pm_runtime_put(dev_to_xe(dev));
if (ret)
return ret;
@@ -243,9 +235,8 @@ static ssize_t power_profile_store(struct kobject *kobj,
struct xe_guc_pc *pc = dev_to_pc(dev);
int err;
- xe_pm_runtime_get(dev_to_xe(dev));
+ guard(xe_pm_runtime)(dev_to_xe(dev));
err = xe_guc_pc_set_power_profile(pc, buff);
- xe_pm_runtime_put(dev_to_xe(dev));
return err ?: count;
}
@@ -293,8 +284,10 @@ int xe_gt_freq_init(struct xe_gt *gt)
return -ENOMEM;
err = sysfs_create_files(gt->freq, freq_attrs);
- if (err)
+ if (err) {
+ kobject_put(gt->freq);
return err;
+ }
err = devm_add_action_or_reset(xe->drm.dev, freq_fini, gt->freq);
if (err)
diff --git a/drivers/gpu/drm/xe/xe_gt_idle.c b/drivers/gpu/drm/xe/xe_gt_idle.c
index bdc9d9877ec4..7a569e1730a4 100644
--- a/drivers/gpu/drm/xe/xe_gt_idle.c
+++ b/drivers/gpu/drm/xe/xe_gt_idle.c
@@ -5,6 +5,7 @@
#include <drm/drm_managed.h>
+#include <generated/xe_wa_oob.h>
#include "xe_force_wake.h"
#include "xe_device.h"
#include "xe_gt.h"
@@ -12,10 +13,10 @@
#include "xe_gt_sysfs.h"
#include "xe_guc_pc.h"
#include "regs/xe_gt_regs.h"
-#include "xe_macros.h"
#include "xe_mmio.h"
#include "xe_pm.h"
#include "xe_sriov.h"
+#include "xe_wa.h"
/**
* DOC: Xe GT Idle
@@ -103,7 +104,6 @@ void xe_gt_idle_enable_pg(struct xe_gt *gt)
struct xe_gt_idle *gtidle = &gt->gtidle;
struct xe_mmio *mmio = &gt->mmio;
u32 vcs_mask, vecs_mask;
- unsigned int fw_ref;
int i, j;
if (IS_SRIOV_VF(xe))
@@ -135,7 +135,7 @@ void xe_gt_idle_enable_pg(struct xe_gt *gt)
}
}
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
if (xe->info.skip_guc_pc) {
/*
* GuC sets the hysteresis value when GuC PC is enabled
@@ -145,14 +145,18 @@ void xe_gt_idle_enable_pg(struct xe_gt *gt)
xe_mmio_write32(mmio, RENDER_POWERGATE_IDLE_HYSTERESIS, 25);
}
+ if (XE_GT_WA(gt, 14020316580))
+ gtidle->powergate_enable &= ~(VDN_HCP_POWERGATE_ENABLE(0) |
+ VDN_MFXVDENC_POWERGATE_ENABLE(0) |
+ VDN_HCP_POWERGATE_ENABLE(2) |
+ VDN_MFXVDENC_POWERGATE_ENABLE(2));
+
xe_mmio_write32(mmio, POWERGATE_ENABLE, gtidle->powergate_enable);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
void xe_gt_idle_disable_pg(struct xe_gt *gt)
{
struct xe_gt_idle *gtidle = &gt->gtidle;
- unsigned int fw_ref;
if (IS_SRIOV_VF(gt_to_xe(gt)))
return;
@@ -160,9 +164,8 @@ void xe_gt_idle_disable_pg(struct xe_gt *gt)
xe_device_assert_mem_access(gt_to_xe(gt));
gtidle->powergate_enable = 0;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
xe_mmio_write32(&gt->mmio, POWERGATE_ENABLE, gtidle->powergate_enable);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
/**
@@ -181,7 +184,6 @@ int xe_gt_idle_pg_print(struct xe_gt *gt, struct drm_printer *p)
enum xe_gt_idle_state state;
u32 pg_enabled, pg_status = 0;
u32 vcs_mask, vecs_mask;
- unsigned int fw_ref;
int n;
/*
* Media Slices
@@ -218,14 +220,12 @@ int xe_gt_idle_pg_print(struct xe_gt *gt, struct drm_printer *p)
/* Do not wake the GT to read powergating status */
if (state != GT_IDLE_C6) {
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
pg_enabled = xe_mmio_read32(&gt->mmio, POWERGATE_ENABLE);
pg_status = xe_mmio_read32(&gt->mmio, POWERGATE_DOMAIN_STATUS);
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
if (gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK) {
@@ -263,13 +263,9 @@ static ssize_t name_show(struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct xe_gt_idle *gtidle = dev_to_gtidle(dev);
struct xe_guc_pc *pc = gtidle_to_pc(gtidle);
- ssize_t ret;
-
- xe_pm_runtime_get(pc_to_xe(pc));
- ret = sysfs_emit(buff, "%s\n", gtidle->name);
- xe_pm_runtime_put(pc_to_xe(pc));
- return ret;
+ guard(xe_pm_runtime)(pc_to_xe(pc));
+ return sysfs_emit(buff, "%s\n", gtidle->name);
}
static struct kobj_attribute name_attr = __ATTR_RO(name);
@@ -281,9 +277,8 @@ static ssize_t idle_status_show(struct kobject *kobj,
struct xe_guc_pc *pc = gtidle_to_pc(gtidle);
enum xe_gt_idle_state state;
- xe_pm_runtime_get(pc_to_xe(pc));
- state = gtidle->idle_status(pc);
- xe_pm_runtime_put(pc_to_xe(pc));
+ scoped_guard(xe_pm_runtime, pc_to_xe(pc))
+ state = gtidle->idle_status(pc);
return sysfs_emit(buff, "%s\n", gt_idle_state_to_string(state));
}
@@ -311,9 +306,8 @@ static ssize_t idle_residency_ms_show(struct kobject *kobj,
struct xe_guc_pc *pc = gtidle_to_pc(gtidle);
u64 residency;
- xe_pm_runtime_get(pc_to_xe(pc));
- residency = xe_gt_idle_residency_msec(gtidle);
- xe_pm_runtime_put(pc_to_xe(pc));
+ scoped_guard(xe_pm_runtime, pc_to_xe(pc))
+ residency = xe_gt_idle_residency_msec(gtidle);
return sysfs_emit(buff, "%llu\n", residency);
}
@@ -396,21 +390,17 @@ void xe_gt_idle_enable_c6(struct xe_gt *gt)
int xe_gt_idle_disable_c6(struct xe_gt *gt)
{
- unsigned int fw_ref;
-
xe_device_assert_mem_access(gt_to_xe(gt));
if (IS_SRIOV_VF(gt_to_xe(gt)))
return 0;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
xe_mmio_write32(&gt->mmio, RC_CONTROL, 0);
xe_mmio_write32(&gt->mmio, RC_STATE, 0);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
-
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_gt_mcr.c b/drivers/gpu/drm/xe/xe_gt_mcr.c
index 164010860664..7c1fe9ac120d 100644
--- a/drivers/gpu/drm/xe/xe_gt_mcr.c
+++ b/drivers/gpu/drm/xe/xe_gt_mcr.c
@@ -7,7 +7,6 @@
#include "regs/xe_gt_regs.h"
#include "xe_assert.h"
-#include "xe_gt.h"
#include "xe_gt_printk.h"
#include "xe_gt_topology.h"
#include "xe_gt_types.h"
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf.c
index 0714c758b9c1..fb5c9101e275 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf.c
@@ -14,6 +14,7 @@
#include "xe_gt_sriov_pf_control.h"
#include "xe_gt_sriov_pf_helpers.h"
#include "xe_gt_sriov_pf_migration.h"
+#include "xe_gt_sriov_pf_policy.h"
#include "xe_gt_sriov_pf_service.h"
#include "xe_gt_sriov_printk.h"
#include "xe_guc_submit.h"
@@ -123,6 +124,8 @@ int xe_gt_sriov_pf_init(struct xe_gt *gt)
if (err)
return err;
+ xe_gt_sriov_pf_policy_init(gt);
+
err = xe_gt_sriov_pf_migration_init(gt);
if (err)
return err;
@@ -281,3 +284,20 @@ int xe_gt_sriov_pf_wait_ready(struct xe_gt *gt)
pf_flush_restart(gt);
return 0;
}
+
+/**
+ * xe_gt_sriov_pf_sched_groups_enabled - Check if multiple scheduler groups are
+ * enabled
+ * @gt: the &xe_gt
+ *
+ * This function is for PF use only.
+ *
+ * Return: true if shed groups were enabled, false otherwise.
+ */
+bool xe_gt_sriov_pf_sched_groups_enabled(struct xe_gt *gt)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+
+ return xe_gt_sriov_pf_policy_sched_groups_enabled(gt);
+}
+
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf.h
index e7fde3f9937a..1ccfc7137b98 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf.h
@@ -6,6 +6,8 @@
#ifndef _XE_GT_SRIOV_PF_H_
#define _XE_GT_SRIOV_PF_H_
+#include <linux/types.h>
+
struct xe_gt;
#ifdef CONFIG_PCI_IOV
@@ -16,6 +18,7 @@ void xe_gt_sriov_pf_init_hw(struct xe_gt *gt);
void xe_gt_sriov_pf_sanitize_hw(struct xe_gt *gt, unsigned int vfid);
void xe_gt_sriov_pf_stop_prepare(struct xe_gt *gt);
void xe_gt_sriov_pf_restart(struct xe_gt *gt);
+bool xe_gt_sriov_pf_sched_groups_enabled(struct xe_gt *gt);
#else
static inline int xe_gt_sriov_pf_init_early(struct xe_gt *gt)
{
@@ -38,6 +41,11 @@ static inline void xe_gt_sriov_pf_stop_prepare(struct xe_gt *gt)
static inline void xe_gt_sriov_pf_restart(struct xe_gt *gt)
{
}
+
+static inline bool xe_gt_sriov_pf_sched_groups_enabled(struct xe_gt *gt)
+{
+ return false;
+}
#endif
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
index 59c5c6b4d994..23601ce79348 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
@@ -24,13 +24,11 @@
#include "xe_guc_buf.h"
#include "xe_guc_ct.h"
#include "xe_guc_db_mgr.h"
-#include "xe_guc_fwif.h"
#include "xe_guc_id_mgr.h"
#include "xe_guc_klv_helpers.h"
#include "xe_guc_klv_thresholds_set.h"
#include "xe_guc_submit.h"
#include "xe_lmtt.h"
-#include "xe_map.h"
#include "xe_migrate.h"
#include "xe_sriov.h"
#include "xe_ttm_vram_mgr.h"
@@ -195,6 +193,25 @@ static int pf_push_vf_cfg_dbs(struct xe_gt *gt, unsigned int vfid, u32 begin, u3
return pf_push_vf_cfg_klvs(gt, vfid, 2, klvs, ARRAY_SIZE(klvs));
}
+static int pf_push_vf_grp_cfg_u32(struct xe_gt *gt, unsigned int vfid,
+ u16 key, const u32 *values, u32 count)
+{
+ CLASS(xe_guc_buf, buf)(&gt->uc.guc.buf, GUC_KLV_LEN_MIN + GUC_MAX_SCHED_GROUPS);
+ u32 *klv;
+
+ xe_gt_assert(gt, count && count <= GUC_MAX_SCHED_GROUPS);
+
+ if (!xe_guc_buf_is_valid(buf))
+ return -ENOBUFS;
+
+ klv = xe_guc_buf_cpu_ptr(buf);
+
+ klv[0] = FIELD_PREP(GUC_KLV_0_KEY, key) | FIELD_PREP(GUC_KLV_0_LEN, count);
+ memcpy(&klv[1], values, count * sizeof(u32));
+
+ return pf_push_vf_buf_klvs(gt, vfid, 1, buf, GUC_KLV_LEN_MIN + count);
+}
+
static int pf_push_vf_cfg_exec_quantum(struct xe_gt *gt, unsigned int vfid, u32 *exec_quantum)
{
/* GuC will silently clamp values exceeding max */
@@ -265,11 +282,43 @@ static u32 encode_config_ggtt(u32 *cfg, const struct xe_gt_sriov_config *config,
if (!xe_ggtt_node_allocated(node))
return 0;
- return encode_ggtt(cfg, node->base.start, node->base.size, details);
+ return encode_ggtt(cfg, xe_ggtt_node_addr(node), xe_ggtt_node_size(node), details);
+}
+
+static u32 encode_config_sched(struct xe_gt *gt, u32 *cfg, u32 n,
+ const struct xe_gt_sriov_config *config)
+{
+ int i;
+
+ if (xe_sriov_gt_pf_policy_has_multi_group_modes(gt)) {
+ BUILD_BUG_ON(ARRAY_SIZE(config->exec_quantum) >
+ GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_MAX_LEN);
+ BUILD_BUG_ON(ARRAY_SIZE(config->preempt_timeout) >
+ GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_MAX_LEN);
+
+ cfg[n++] = PREP_GUC_KLV_CONST(GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY,
+ ARRAY_SIZE(config->exec_quantum));
+ for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++)
+ cfg[n++] = config->exec_quantum[i];
+
+ cfg[n++] = PREP_GUC_KLV_CONST(GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY,
+ ARRAY_SIZE(config->preempt_timeout));
+ for (i = 0; i < ARRAY_SIZE(config->preempt_timeout); i++)
+ cfg[n++] = config->preempt_timeout[i];
+ } else {
+ cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_EXEC_QUANTUM);
+ cfg[n++] = config->exec_quantum[0];
+
+ cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_PREEMPT_TIMEOUT);
+ cfg[n++] = config->preempt_timeout[0];
+ }
+
+ return n;
}
/* Return: number of configuration dwords written */
-static u32 encode_config(u32 *cfg, const struct xe_gt_sriov_config *config, bool details)
+static u32 encode_config(struct xe_gt *gt, u32 *cfg, const struct xe_gt_sriov_config *config,
+ bool details)
{
u32 n = 0;
@@ -297,15 +346,13 @@ static u32 encode_config(u32 *cfg, const struct xe_gt_sriov_config *config, bool
cfg[n++] = upper_32_bits(xe_bo_size(config->lmem_obj));
}
- cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_EXEC_QUANTUM);
- cfg[n++] = config->exec_quantum;
+ n = encode_config_sched(gt, cfg, n, config);
- cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_PREEMPT_TIMEOUT);
- cfg[n++] = config->preempt_timeout;
-
-#define encode_threshold_config(TAG, ...) ({ \
- cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_THRESHOLD_##TAG); \
- cfg[n++] = config->thresholds[MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)]; \
+#define encode_threshold_config(TAG, NAME, VER...) ({ \
+ if (IF_ARGS(GUC_FIRMWARE_VER_AT_LEAST(&gt->uc.guc, VER), true, VER)) { \
+ cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_THRESHOLD_##TAG); \
+ cfg[n++] = config->thresholds[MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)]; \
+ } \
});
MAKE_XE_GUC_KLV_THRESHOLDS_SET(encode_threshold_config);
@@ -328,7 +375,7 @@ static int pf_push_full_vf_config(struct xe_gt *gt, unsigned int vfid)
return -ENOBUFS;
cfg = xe_guc_buf_cpu_ptr(buf);
- num_dwords = encode_config(cfg, config, true);
+ num_dwords = encode_config(gt, cfg, config, true);
xe_gt_assert(gt, num_dwords <= max_cfg_dwords);
if (xe_gt_is_media_type(gt)) {
@@ -344,8 +391,8 @@ static int pf_push_full_vf_config(struct xe_gt *gt, unsigned int vfid)
xe_gt_assert(gt, num_dwords <= max_cfg_dwords);
if (vfid == PFID) {
- u64 ggtt_start = xe_wopcm_size(gt_to_xe(gt));
- u64 ggtt_size = gt_to_tile(gt)->mem.ggtt->size - ggtt_start;
+ u64 ggtt_start = xe_ggtt_start(gt_to_tile(gt)->mem.ggtt);
+ u64 ggtt_size = xe_ggtt_size(gt_to_tile(gt)->mem.ggtt);
/* plain PF config data will never include a real GGTT region */
xe_gt_assert(gt, !encode_config_ggtt(cfg + num_dwords, config, true));
@@ -496,9 +543,9 @@ static int pf_provision_vf_ggtt(struct xe_gt *gt, unsigned int vfid, u64 size)
xe_ggtt_assign(node, vfid);
xe_gt_sriov_dbg_verbose(gt, "VF%u assigned GGTT %llx-%llx\n",
- vfid, node->base.start, node->base.start + node->base.size - 1);
+ vfid, xe_ggtt_node_addr(node), xe_ggtt_node_addr(node) + size - 1);
- err = pf_distribute_config_ggtt(gt->tile, vfid, node->base.start, node->base.size);
+ err = pf_distribute_config_ggtt(gt->tile, vfid, xe_ggtt_node_addr(node), size);
if (unlikely(err))
goto err;
@@ -515,7 +562,7 @@ static u64 pf_get_vf_config_ggtt(struct xe_gt *gt, unsigned int vfid)
struct xe_ggtt_node *node = config->ggtt_region;
xe_gt_assert(gt, xe_gt_is_main_type(gt));
- return xe_ggtt_node_allocated(node) ? node->base.size : 0;
+ return xe_ggtt_node_allocated(node) ? xe_ggtt_node_size(node) : 0;
}
/**
@@ -973,6 +1020,33 @@ static int pf_config_set_u32_done(struct xe_gt *gt, unsigned int vfid, u32 value
return 0;
}
+static char *to_group_name(const char *what, u8 group, char *buf, size_t size)
+{
+ snprintf(buf, size, "group%u%s%s", group, what ? " " : "", what ?: "");
+ return buf;
+}
+
+static int
+pf_groups_cfg_set_u32_done(struct xe_gt *gt, unsigned int vfid, u32 *values, u32 count,
+ void (*get_actual)(struct xe_gt *, unsigned int, u32 *, u32),
+ const char *what, const char *(*unit)(u32), int err)
+{
+ u32 actual[GUC_MAX_SCHED_GROUPS];
+ char group_name[32];
+ u8 g;
+
+ xe_gt_assert(gt, count <= ARRAY_SIZE(actual));
+
+ get_actual(gt, vfid, actual, count);
+
+ for (g = 0; g < count; g++)
+ pf_config_set_u32_done(gt, vfid, values[g], actual[g],
+ to_group_name(what, g, group_name, sizeof(group_name)),
+ unit, err);
+
+ return err;
+}
+
/**
* xe_gt_sriov_pf_config_set_ctxs - Configure GuC contexts IDs quota for the VF.
* @gt: the &xe_gt
@@ -1857,12 +1931,15 @@ static int pf_provision_exec_quantum(struct xe_gt *gt, unsigned int vfid,
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int err;
+ int i;
err = pf_push_vf_cfg_exec_quantum(gt, vfid, &exec_quantum);
if (unlikely(err))
return err;
- config->exec_quantum = exec_quantum;
+ for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++)
+ config->exec_quantum[i] = exec_quantum;
+
return 0;
}
@@ -1870,7 +1947,7 @@ static u32 pf_get_exec_quantum(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
- return config->exec_quantum;
+ return config->exec_quantum[0];
}
/**
@@ -1977,6 +2054,88 @@ int xe_gt_sriov_pf_config_bulk_set_exec_quantum_locked(struct xe_gt *gt, u32 exe
exec_quantum_unit, n, err);
}
+static int pf_provision_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid,
+ const u32 *exec_quantums, u32 count)
+{
+ struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
+ int err;
+ int i;
+
+ err = pf_push_vf_grp_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY,
+ exec_quantums, count);
+ if (unlikely(err))
+ return err;
+
+ /*
+ * GuC silently clamps values exceeding the max and zeroes out the
+ * quantum for groups not in the klv payload
+ */
+ for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++) {
+ if (i < count)
+ config->exec_quantum[i] = min_t(u32, exec_quantums[i],
+ GUC_KLV_VF_CFG_EXEC_QUANTUM_MAX_VALUE);
+ else
+ config->exec_quantum[i] = 0;
+ }
+
+ return 0;
+}
+
+static void pf_get_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid,
+ u32 *exec_quantums, u32 max_count)
+{
+ struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
+ u32 count = min_t(u32, max_count, ARRAY_SIZE(config->exec_quantum));
+
+ memcpy(exec_quantums, config->exec_quantum, sizeof(u32) * count);
+}
+
+/**
+ * xe_gt_sriov_pf_config_set_groups_exec_quantums() - Configure PF/VF EQs for sched groups.
+ * @gt: the &xe_gt
+ * @vfid: the PF or VF identifier
+ * @exec_quantums: array of requested EQs in milliseconds (0 is infinity)
+ * @count: number of entries in the array
+ *
+ * This function can only be called on PF.
+ * It will log the provisioned value or an error in case of the failure.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_config_set_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid,
+ u32 *exec_quantums, u32 count)
+{
+ int err;
+
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
+
+ err = pf_provision_groups_exec_quantums(gt, vfid, exec_quantums, count);
+
+ return pf_groups_cfg_set_u32_done(gt, vfid, exec_quantums, count,
+ pf_get_groups_exec_quantums,
+ "execution quantum",
+ exec_quantum_unit, err);
+}
+
+/**
+ * xe_gt_sriov_pf_config_get_groups_exec_quantums() - Get PF/VF sched groups EQs
+ * @gt: the &xe_gt
+ * @vfid: the PF or VF identifier
+ * @exec_quantums: array in which to store the execution quantums values
+ * @count: maximum number of entries to store
+ *
+ * This function can only be called on PF.
+ */
+void xe_gt_sriov_pf_config_get_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid,
+ u32 *exec_quantums, u32 count)
+{
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
+
+ xe_gt_assert(gt, count <= GUC_MAX_SCHED_GROUPS);
+
+ pf_get_groups_exec_quantums(gt, vfid, exec_quantums, count);
+}
+
static const char *preempt_timeout_unit(u32 preempt_timeout)
{
return preempt_timeout ? "us" : "(infinity)";
@@ -1987,12 +2146,14 @@ static int pf_provision_preempt_timeout(struct xe_gt *gt, unsigned int vfid,
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int err;
+ int i;
err = pf_push_vf_cfg_preempt_timeout(gt, vfid, &preempt_timeout);
if (unlikely(err))
return err;
- config->preempt_timeout = preempt_timeout;
+ for (i = 0; i < ARRAY_SIZE(config->preempt_timeout); i++)
+ config->preempt_timeout[i] = preempt_timeout;
return 0;
}
@@ -2001,7 +2162,7 @@ static u32 pf_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
- return config->preempt_timeout;
+ return config->preempt_timeout[0];
}
/**
@@ -2107,6 +2268,89 @@ int xe_gt_sriov_pf_config_bulk_set_preempt_timeout_locked(struct xe_gt *gt, u32
preempt_timeout_unit, n, err);
}
+static int pf_provision_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid,
+ const u32 *preempt_timeouts, u32 count)
+{
+ struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
+ int err;
+ int i;
+
+ err = pf_push_vf_grp_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY,
+ preempt_timeouts, count);
+ if (unlikely(err))
+ return err;
+
+ /*
+ * GuC silently clamps values exceeding the max and zeroes out the
+ * quantum for groups not in the klv payload
+ */
+ for (i = 0; i < ARRAY_SIZE(config->preempt_timeout); i++) {
+ if (i < count)
+ config->preempt_timeout[i] =
+ min_t(u32, preempt_timeouts[i],
+ GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_MAX_VALUE);
+ else
+ config->preempt_timeout[i] = 0;
+ }
+
+ return 0;
+}
+
+static void pf_get_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid,
+ u32 *preempt_timeouts, u32 max_count)
+{
+ struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
+ u32 count = min_t(u32, max_count, ARRAY_SIZE(config->preempt_timeout));
+
+ memcpy(preempt_timeouts, config->preempt_timeout, sizeof(u32) * count);
+}
+
+/**
+ * xe_gt_sriov_pf_config_set_groups_preempt_timeouts() - Configure PF/VF PTs for sched groups.
+ * @gt: the &xe_gt
+ * @vfid: the PF or VF identifier
+ * @preempt_timeouts: array of requested PTs in microseconds (0 is infinity)
+ * @count: number of entries in the array
+ *
+ * This function can only be called on PF.
+ * It will log the provisioned value or an error in case of the failure.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_config_set_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid,
+ u32 *preempt_timeouts, u32 count)
+{
+ int err;
+
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
+
+ err = pf_provision_groups_preempt_timeouts(gt, vfid, preempt_timeouts, count);
+
+ return pf_groups_cfg_set_u32_done(gt, vfid, preempt_timeouts, count,
+ pf_get_groups_preempt_timeouts,
+ "preempt_timeout",
+ preempt_timeout_unit, err);
+}
+
+/**
+ * xe_gt_sriov_pf_config_get_groups_preempt_timeouts() - Get PF/VF sched groups PTs
+ * @gt: the &xe_gt
+ * @vfid: the PF or VF identifier
+ * @preempt_timeouts: array in which to store the preemption timeouts values
+ * @count: maximum number of entries to store
+ *
+ * This function can only be called on PF.
+ */
+void xe_gt_sriov_pf_config_get_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid,
+ u32 *preempt_timeouts, u32 count)
+{
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
+
+ xe_gt_assert(gt, count <= GUC_MAX_SCHED_GROUPS);
+
+ pf_get_groups_preempt_timeouts(gt, vfid, preempt_timeouts, count);
+}
+
static const char *sched_priority_unit(u32 priority)
{
return priority == GUC_SCHED_PRIORITY_LOW ? "(low)" :
@@ -2180,10 +2424,14 @@ u32 xe_gt_sriov_pf_config_get_sched_priority(struct xe_gt *gt, unsigned int vfid
static void pf_reset_config_sched(struct xe_gt *gt, struct xe_gt_sriov_config *config)
{
+ int i;
+
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
- config->exec_quantum = 0;
- config->preempt_timeout = 0;
+ for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++) {
+ config->exec_quantum[i] = 0;
+ config->preempt_timeout[i] = 0;
+ }
}
static int pf_provision_threshold(struct xe_gt *gt, unsigned int vfid,
@@ -2518,7 +2766,7 @@ ssize_t xe_gt_sriov_pf_config_save(struct xe_gt *gt, unsigned int vfid, void *bu
ret = -ENOBUFS;
} else {
config = pf_pick_vf_config(gt, vfid);
- ret = encode_config(buf, config, false) * sizeof(u32);
+ ret = encode_config(gt, buf, config, false) * sizeof(u32);
}
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
@@ -2545,17 +2793,29 @@ static int pf_restore_vf_config_klv(struct xe_gt *gt, unsigned int vfid,
return -EBADMSG;
return pf_provision_exec_quantum(gt, vfid, value[0]);
+ case GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY:
+ if (len > GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_MAX_LEN)
+ return -EBADMSG;
+ return pf_provision_groups_exec_quantums(gt, vfid, value, len);
+
+ case GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY:
+ if (len > GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_MAX_LEN)
+ return -EBADMSG;
+ return pf_provision_groups_preempt_timeouts(gt, vfid, value, len);
+
case GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_KEY:
if (len != GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_LEN)
return -EBADMSG;
return pf_provision_preempt_timeout(gt, vfid, value[0]);
/* auto-generate case statements */
-#define define_threshold_key_to_provision_case(TAG, ...) \
+#define define_threshold_key_to_provision_case(TAG, NAME, VER...) \
case MAKE_GUC_KLV_VF_CFG_THRESHOLD_KEY(TAG): \
BUILD_BUG_ON(MAKE_GUC_KLV_VF_CFG_THRESHOLD_LEN(TAG) != 1u); \
if (len != MAKE_GUC_KLV_VF_CFG_THRESHOLD_LEN(TAG)) \
return -EBADMSG; \
+ if (IF_ARGS(!GUC_FIRMWARE_VER_AT_LEAST(&gt->uc.guc, VER), false, VER)) \
+ return -EKEYREJECTED; \
return pf_provision_threshold(gt, vfid, \
MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG), \
value[0]);
@@ -2778,11 +3038,12 @@ int xe_gt_sriov_pf_config_print_ggtt(struct xe_gt *gt, struct drm_printer *p)
if (!xe_ggtt_node_allocated(config->ggtt_region))
continue;
- string_get_size(config->ggtt_region->base.size, 1, STRING_UNITS_2,
+ string_get_size(xe_ggtt_node_size(config->ggtt_region), 1, STRING_UNITS_2,
buf, sizeof(buf));
drm_printf(p, "VF%u:\t%#0llx-%#llx\t(%s)\n",
- n, config->ggtt_region->base.start,
- config->ggtt_region->base.start + config->ggtt_region->base.size - 1,
+ n, xe_ggtt_node_addr(config->ggtt_region),
+ xe_ggtt_node_addr(config->ggtt_region) +
+ xe_ggtt_node_size(config->ggtt_region) - 1,
buf);
}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h
index 4975730423d7..3c6c8b6655af 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h
@@ -46,6 +46,11 @@ int xe_gt_sriov_pf_config_set_exec_quantum_locked(struct xe_gt *gt, unsigned int
u32 exec_quantum);
int xe_gt_sriov_pf_config_bulk_set_exec_quantum_locked(struct xe_gt *gt, u32 exec_quantum);
+void xe_gt_sriov_pf_config_get_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid,
+ u32 *exec_quantum, u32 max_count);
+int xe_gt_sriov_pf_config_set_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid,
+ u32 *exec_quantum, u32 count);
+
u32 xe_gt_sriov_pf_config_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_config_set_preempt_timeout(struct xe_gt *gt, unsigned int vfid,
u32 preempt_timeout);
@@ -55,6 +60,11 @@ int xe_gt_sriov_pf_config_set_preempt_timeout_locked(struct xe_gt *gt, unsigned
u32 preempt_timeout);
int xe_gt_sriov_pf_config_bulk_set_preempt_timeout_locked(struct xe_gt *gt, u32 preempt_timeout);
+void xe_gt_sriov_pf_config_get_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid,
+ u32 *preempt_timeout, u32 max_count);
+int xe_gt_sriov_pf_config_set_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid,
+ u32 *preempt_timeout, u32 count);
+
u32 xe_gt_sriov_pf_config_get_sched_priority(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_config_set_sched_priority(struct xe_gt *gt, unsigned int vfid, u32 priority);
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h
index 686c7b3b6d7a..75a48d0fa859 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h
@@ -6,6 +6,7 @@
#ifndef _XE_GT_SRIOV_PF_CONFIG_TYPES_H_
#define _XE_GT_SRIOV_PF_CONFIG_TYPES_H_
+#include "abi/guc_scheduler_abi.h"
#include "xe_ggtt_types.h"
#include "xe_guc_klv_thresholds_set_types.h"
@@ -30,9 +31,9 @@ struct xe_gt_sriov_config {
/** @begin_db: start index of GuC doorbell ID range. */
u16 begin_db;
/** @exec_quantum: execution-quantum in milliseconds. */
- u32 exec_quantum;
+ u32 exec_quantum[GUC_MAX_SCHED_GROUPS];
/** @preempt_timeout: preemption timeout in microseconds. */
- u32 preempt_timeout;
+ u32 preempt_timeout[GUC_MAX_SCHED_GROUPS];
/** @sched_priority: scheduling priority. */
u32 sched_priority;
/** @thresholds: GuC thresholds for adverse events notifications. */
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c
index 5278ea4fd655..f97abb02aebd 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c
@@ -8,7 +8,6 @@
#include <drm/drm_print.h>
#include <drm/drm_debugfs.h>
-#include "xe_bo.h"
#include "xe_debugfs.h"
#include "xe_device.h"
#include "xe_gt.h"
@@ -21,6 +20,7 @@
#include "xe_gt_sriov_pf_monitor.h"
#include "xe_gt_sriov_pf_policy.h"
#include "xe_gt_sriov_pf_service.h"
+#include "xe_guc.h"
#include "xe_pm.h"
#include "xe_sriov_pf.h"
#include "xe_sriov_pf_provision.h"
@@ -123,11 +123,10 @@ static int POLICY##_set(void *data, u64 val) \
if (val > (TYPE)~0ull) \
return -EOVERFLOW; \
\
- xe_pm_runtime_get(xe); \
+ guard(xe_pm_runtime)(xe); \
err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val); \
if (!err) \
xe_sriov_pf_provision_set_custom_mode(xe); \
- xe_pm_runtime_put(xe); \
\
return err; \
} \
@@ -160,6 +159,299 @@ static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent)
* /sys/kernel/debug/dri/BDF/
* ├── sriov
* : ├── pf
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── sched_groups_mode
+ * ├── sched_groups_exec_quantums_ms
+ * ├── sched_groups_preempt_timeout_us
+ * ├── sched_groups
+ * : ├── group0
+ * :
+ * : └── groupN
+ * ├── vf1
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── sched_groups_exec_quantums_ms
+ * ├── sched_groups_preempt_timeout_us
+ * :
+ */
+
+static const char *sched_group_mode_to_string(enum xe_sriov_sched_group_modes mode)
+{
+ switch (mode) {
+ case XE_SRIOV_SCHED_GROUPS_DISABLED:
+ return "disabled";
+ case XE_SRIOV_SCHED_GROUPS_MEDIA_SLICES:
+ return "media_slices";
+ case XE_SRIOV_SCHED_GROUPS_MODES_COUNT:
+ /* dummy mode to make the compiler happy */
+ break;
+ }
+
+ return "unknown";
+}
+
+static int sched_groups_info(struct seq_file *m, void *data)
+{
+ struct drm_printer p = drm_seq_file_printer(m);
+ struct xe_gt *gt = extract_gt(m->private);
+ enum xe_sriov_sched_group_modes current_mode =
+ gt->sriov.pf.policy.guc.sched_groups.current_mode;
+ enum xe_sriov_sched_group_modes mode;
+
+ for (mode = XE_SRIOV_SCHED_GROUPS_DISABLED;
+ mode < XE_SRIOV_SCHED_GROUPS_MODES_COUNT;
+ mode++) {
+ if (!xe_sriov_gt_pf_policy_has_sched_group_mode(gt, mode))
+ continue;
+
+ drm_printf(&p, "%s%s%s%s",
+ mode == XE_SRIOV_SCHED_GROUPS_DISABLED ? "" : " ",
+ mode == current_mode ? "[" : "",
+ sched_group_mode_to_string(mode),
+ mode == current_mode ? "]" : "");
+ }
+
+ drm_puts(&p, "\n");
+
+ return 0;
+}
+
+static int sched_groups_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, sched_groups_info, inode->i_private);
+}
+
+static ssize_t sched_groups_write(struct file *file, const char __user *ubuf,
+ size_t size, loff_t *pos)
+{
+ struct xe_gt *gt = extract_gt(file_inode(file)->i_private);
+ enum xe_sriov_sched_group_modes mode;
+ char name[32];
+ int ret;
+
+ if (*pos)
+ return -ESPIPE;
+
+ if (!size)
+ return -ENODATA;
+
+ if (size > sizeof(name) - 1)
+ return -EINVAL;
+
+ ret = simple_write_to_buffer(name, sizeof(name) - 1, pos, ubuf, size);
+ if (ret < 0)
+ return ret;
+ name[ret] = '\0';
+
+ for (mode = XE_SRIOV_SCHED_GROUPS_DISABLED;
+ mode < XE_SRIOV_SCHED_GROUPS_MODES_COUNT;
+ mode++)
+ if (sysfs_streq(name, sched_group_mode_to_string(mode)))
+ break;
+
+ if (mode == XE_SRIOV_SCHED_GROUPS_MODES_COUNT)
+ return -EINVAL;
+
+ guard(xe_pm_runtime)(gt_to_xe(gt));
+ ret = xe_gt_sriov_pf_policy_set_sched_groups_mode(gt, mode);
+
+ return ret < 0 ? ret : size;
+}
+
+static const struct file_operations sched_groups_fops = {
+ .owner = THIS_MODULE,
+ .open = sched_groups_open,
+ .read = seq_read,
+ .write = sched_groups_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int sched_groups_config_show(struct seq_file *m, void *data,
+ void (*get)(struct xe_gt *, unsigned int, u32 *, u32))
+{
+ struct drm_printer p = drm_seq_file_printer(m);
+ unsigned int vfid = extract_vfid(m->private);
+ struct xe_gt *gt = extract_gt(m->private);
+ u32 values[GUC_MAX_SCHED_GROUPS];
+ bool first = true;
+ u8 group;
+
+ get(gt, vfid, values, ARRAY_SIZE(values));
+
+ for (group = 0; group < ARRAY_SIZE(values); group++) {
+ drm_printf(&p, "%s%u", first ? "" : ",", values[group]);
+
+ first = false;
+ }
+
+ drm_puts(&p, "\n");
+
+ return 0;
+}
+
+static ssize_t sched_groups_config_write(struct file *file, const char __user *ubuf,
+ size_t size, loff_t *pos,
+ int (*set)(struct xe_gt *, unsigned int, u32 *, u32))
+{
+ struct dentry *parent = file_inode(file)->i_private;
+ unsigned int vfid = extract_vfid(parent);
+ struct xe_gt *gt = extract_gt(parent);
+ u32 values[GUC_MAX_SCHED_GROUPS];
+ int *input __free(kfree) = NULL;
+ u32 count;
+ int ret;
+ int i;
+
+ if (*pos)
+ return -ESPIPE;
+
+ if (!size)
+ return -ENODATA;
+
+ ret = parse_int_array_user(ubuf, min(size, GUC_MAX_SCHED_GROUPS * sizeof(u32)), &input);
+ if (ret)
+ return ret;
+
+ count = input[0];
+ if (count > GUC_MAX_SCHED_GROUPS)
+ return -E2BIG;
+
+ for (i = 0; i < count; i++) {
+ if (input[i + 1] < 0 || input[i + 1] > S32_MAX)
+ return -EINVAL;
+
+ values[i] = input[i + 1];
+ }
+
+ guard(xe_pm_runtime)(gt_to_xe(gt));
+ ret = set(gt, vfid, values, count);
+
+ return ret < 0 ? ret : size;
+}
+
+#define DEFINE_SRIOV_GT_GRP_CFG_DEBUGFS_ATTRIBUTE(CONFIG) \
+static int sched_groups_##CONFIG##_show(struct seq_file *m, void *data) \
+{ \
+ return sched_groups_config_show(m, data, \
+ xe_gt_sriov_pf_config_get_groups_##CONFIG); \
+} \
+ \
+static int sched_groups_##CONFIG##_open(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, sched_groups_##CONFIG##_show, \
+ inode->i_private); \
+} \
+ \
+static ssize_t sched_groups_##CONFIG##_write(struct file *file, \
+ const char __user *ubuf, \
+ size_t size, loff_t *pos) \
+{ \
+ return sched_groups_config_write(file, ubuf, size, pos, \
+ xe_gt_sriov_pf_config_set_groups_##CONFIG); \
+} \
+ \
+static const struct file_operations sched_groups_##CONFIG##_fops = { \
+ .owner = THIS_MODULE, \
+ .open = sched_groups_##CONFIG##_open, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .write = sched_groups_##CONFIG##_write, \
+ .release = single_release, \
+}
+
+DEFINE_SRIOV_GT_GRP_CFG_DEBUGFS_ATTRIBUTE(exec_quantums);
+DEFINE_SRIOV_GT_GRP_CFG_DEBUGFS_ATTRIBUTE(preempt_timeouts);
+
+static ssize_t sched_group_engines_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *dent = file_dentry(file);
+ struct xe_gt *gt = extract_gt(dent->d_parent->d_parent);
+ struct xe_gt_sriov_scheduler_groups *info = &gt->sriov.pf.policy.guc.sched_groups;
+ struct guc_sched_group *groups = info->modes[info->current_mode].groups;
+ u32 num_groups = info->modes[info->current_mode].num_groups;
+ unsigned int group = (uintptr_t)extract_priv(dent);
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+ char engines[128];
+
+ engines[0] = '\0';
+
+ if (group < num_groups) {
+ for_each_hw_engine(hwe, gt, id) {
+ u8 guc_class = xe_engine_class_to_guc_class(hwe->class);
+ u32 mask = groups[group].engines[guc_class];
+
+ if (mask & BIT(hwe->logical_instance)) {
+ strlcat(engines, hwe->name, sizeof(engines));
+ strlcat(engines, " ", sizeof(engines));
+ }
+ }
+ strlcat(engines, "\n", sizeof(engines));
+ }
+
+ return simple_read_from_buffer(buf, count, ppos, engines, strlen(engines));
+}
+
+static const struct file_operations sched_group_engines_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = sched_group_engines_read,
+ .llseek = default_llseek,
+};
+
+static void pf_add_sched_groups(struct xe_gt *gt, struct dentry *parent, unsigned int vfid)
+{
+ struct dentry *groups;
+ u8 group;
+
+ xe_gt_assert(gt, gt == extract_gt(parent));
+ xe_gt_assert(gt, vfid == extract_vfid(parent));
+
+ /*
+ * TODO: we currently call this function before we initialize scheduler
+ * groups, so at this point in time we don't know if there are any
+ * valid groups on the GT and we can't selectively register the debugfs
+ * only if there are any. Therefore, we always register the debugfs
+ * files if we're on a platform that has support for groups.
+ * We should rework the flow so that debugfs is registered after the
+ * policy init, so that we check if there are valid groups before
+ * adding the debugfs files.
+ * Similarly, instead of using GUC_MAX_SCHED_GROUPS we could use
+ * gt->sriov.pf.policy.guc.sched_groups.max_number_of_groups.
+ */
+ if (!xe_sriov_gt_pf_policy_has_sched_groups_support(gt))
+ return;
+
+ debugfs_create_file("sched_groups_exec_quantums_ms", 0644, parent, parent,
+ &sched_groups_exec_quantums_fops);
+ debugfs_create_file("sched_groups_preempt_timeouts_us", 0644, parent, parent,
+ &sched_groups_preempt_timeouts_fops);
+
+ if (vfid != PFID)
+ return;
+
+ debugfs_create_file("sched_groups_mode", 0644, parent, parent, &sched_groups_fops);
+
+ groups = debugfs_create_dir("sched_groups", parent);
+ if (IS_ERR(groups))
+ return;
+
+ for (group = 0; group < GUC_MAX_SCHED_GROUPS; group++) {
+ char name[10];
+
+ snprintf(name, sizeof(name), "group%u", group);
+ debugfs_create_file(name, 0644, groups, (void *)(uintptr_t)group,
+ &sched_group_engines_fops);
+ }
+}
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
* │ ├── tile0
* │ : ├── gt0
* │ : ├── doorbells_spare
@@ -189,12 +481,11 @@ static int CONFIG##_set(void *data, u64 val) \
if (val > (TYPE)~0ull) \
return -EOVERFLOW; \
\
- xe_pm_runtime_get(xe); \
+ guard(xe_pm_runtime)(xe); \
err = xe_sriov_pf_wait_ready(xe) ?: \
xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \
if (!err) \
xe_sriov_pf_provision_set_custom_mode(xe); \
- xe_pm_runtime_put(xe); \
\
return err; \
} \
@@ -249,11 +540,10 @@ static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index in
if (val > (u32)~0ull)
return -EOVERFLOW;
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val);
if (!err)
xe_sriov_pf_provision_set_custom_mode(xe);
- xe_pm_runtime_put(xe);
return err;
}
@@ -304,9 +594,11 @@ static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigne
&sched_priority_fops);
/* register all threshold attributes */
-#define register_threshold_attribute(TAG, NAME, ...) \
- debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \
- &NAME##_fops);
+#define register_threshold_attribute(TAG, NAME, VER...) ({ \
+ if (IF_ARGS(GUC_FIRMWARE_VER_AT_LEAST(&gt->uc.guc, VER), true, VER)) \
+ debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \
+ &NAME##_fops); \
+});
MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute)
#undef register_threshold_attribute
}
@@ -358,9 +650,8 @@ static ssize_t control_write(struct file *file, const char __user *buf, size_t c
xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd));
if (sysfs_streq(cmd, control_cmds[n].cmd)) {
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0;
- xe_pm_runtime_put(xe);
break;
}
}
@@ -519,6 +810,7 @@ static void pf_populate_gt(struct xe_gt *gt, struct dentry *dent, unsigned int v
if (vfid) {
pf_add_config_attrs(gt, dent, vfid);
+ pf_add_sched_groups(gt, dent, vfid);
debugfs_create_file("control", 0600, dent, NULL, &control_ops);
@@ -532,6 +824,7 @@ static void pf_populate_gt(struct xe_gt *gt, struct dentry *dent, unsigned int v
} else {
pf_add_config_attrs(gt, dent, PFID);
pf_add_policy_attrs(gt, dent);
+ pf_add_sched_groups(gt, dent, PFID);
drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), dent, minor);
}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c
index 3174a8dee779..87a164efcc33 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c
@@ -14,7 +14,6 @@
#include "xe_gt_sriov_pf.h"
#include "xe_gt_sriov_pf_config.h"
#include "xe_gt_sriov_pf_control.h"
-#include "xe_gt_sriov_pf_helpers.h"
#include "xe_gt_sriov_pf_migration.h"
#include "xe_gt_sriov_printk.h"
#include "xe_guc.h"
@@ -25,6 +24,7 @@
#include "xe_sriov.h"
#include "xe_sriov_packet.h"
#include "xe_sriov_packet_types.h"
+#include "xe_sriov_pf_helpers.h"
#include "xe_sriov_pf_migration.h"
#define XE_GT_SRIOV_PF_MIGRATION_RING_SIZE 5
@@ -1026,7 +1026,7 @@ static void action_ring_cleanup(void *arg)
static void pf_gt_migration_check_support(struct xe_gt *gt)
{
- if (GUC_FIRMWARE_VER(&gt->uc.guc) < MAKE_GUC_VER(70, 54, 0))
+ if (!GUC_FIRMWARE_VER_AT_LEAST(&gt->uc.guc, 70, 54))
xe_sriov_pf_migration_disable(gt_to_xe(gt), "requires GuC version >= 70.54.0");
}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c
index 4445f660e6d1..848e24926ecd 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c
@@ -3,16 +3,19 @@
* Copyright © 2023-2024 Intel Corporation
*/
+#include <drm/drm_managed.h>
+
#include "abi/guc_actions_sriov_abi.h"
-#include "xe_bo.h"
#include "xe_gt.h"
#include "xe_gt_sriov_pf_helpers.h"
#include "xe_gt_sriov_pf_policy.h"
#include "xe_gt_sriov_printk.h"
+#include "xe_guc.h"
#include "xe_guc_buf.h"
#include "xe_guc_ct.h"
#include "xe_guc_klv_helpers.h"
+#include "xe_guc_submit.h"
#include "xe_pm.h"
/*
@@ -94,6 +97,23 @@ static int pf_push_policy_u32(struct xe_gt *gt, u16 key, u32 value)
return pf_push_policy_klvs(gt, 1, klv, ARRAY_SIZE(klv));
}
+static int pf_push_policy_payload(struct xe_gt *gt, u16 key, void *payload, u32 num_dwords)
+{
+ CLASS(xe_guc_buf, buf)(&gt->uc.guc.buf, GUC_KLV_LEN_MIN + num_dwords);
+ u32 *klv;
+
+ if (!xe_guc_buf_is_valid(buf))
+ return -ENOBUFS;
+
+ klv = xe_guc_buf_cpu_ptr(buf);
+
+ klv[0] = PREP_GUC_KLV(key, num_dwords);
+ if (num_dwords)
+ memcpy(&klv[1], payload, num_dwords * sizeof(u32));
+
+ return pf_push_policy_buf_klvs(gt, 1, buf, GUC_KLV_LEN_MIN + num_dwords);
+}
+
static int pf_update_policy_bool(struct xe_gt *gt, u16 key, bool *policy, bool value)
{
int err;
@@ -351,11 +371,306 @@ u32 xe_gt_sriov_pf_policy_get_sample_period(struct xe_gt *gt)
return value;
}
+static void pf_sched_group_media_slices(struct xe_gt *gt, struct guc_sched_group **groups,
+ u32 *num_groups)
+{
+ u8 slice_to_group[MAX_MEDIA_SLICES];
+ u32 vecs_mask = VECS_INSTANCES(gt);
+ u32 gsc_mask = GSCCS_INSTANCES(gt);
+ u32 vcs_mask = VCS_INSTANCES(gt);
+ struct guc_sched_group *values;
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+ int group = 0;
+ int slice;
+
+ xe_gt_assert(gt, xe_gt_is_media_type(gt));
+
+ /*
+ * Post-BMG the matching of video engines to slices changes, so for now
+ * we don't allow this mode on those platforms.
+ */
+ if (gt_to_xe(gt)->info.platform > XE_BATTLEMAGE)
+ return;
+
+ /*
+ * On BMG and older platforms a media slice has 2 VCS and a VECS. We
+ * bundle the GSC with the first slice.
+ */
+ for (slice = 0; slice < MAX_MEDIA_SLICES; slice++) {
+ if ((vcs_mask & 0x3) || (vecs_mask & 0x1) || (gsc_mask & 0x1))
+ slice_to_group[slice] = group++;
+
+ vcs_mask >>= 2;
+ vecs_mask >>= 1;
+ gsc_mask >>= 1;
+ }
+
+ xe_gt_assert(gt, !vcs_mask);
+ xe_gt_assert(gt, !vecs_mask);
+ xe_gt_assert(gt, !gsc_mask);
+
+ /* We need at least 2 slices to split them up */
+ if (group < 2)
+ return;
+
+ /*
+ * If we have more groups than the GuC can support then we don't want to
+ * expose this specific mode, because the GuC will return an error if we
+ * try to enable it.
+ */
+ if (group > gt->sriov.pf.policy.guc.sched_groups.max_groups) {
+ xe_gt_sriov_notice(gt, "media_slice mode has too many groups: %u vs %u\n",
+ group, gt->sriov.pf.policy.guc.sched_groups.max_groups);
+ return;
+ }
+
+ /* The GuC expects an array with a guc_sched_group entry for each group */
+ values = drmm_kcalloc(&gt_to_xe(gt)->drm, group, sizeof(struct guc_sched_group),
+ GFP_KERNEL);
+ if (!values)
+ return;
+
+ for_each_hw_engine(hwe, gt, id) {
+ u8 guc_class = xe_engine_class_to_guc_class(hwe->class);
+
+ switch (hwe->class) {
+ case XE_ENGINE_CLASS_VIDEO_DECODE:
+ slice = hwe->instance / 2;
+ break;
+ case XE_ENGINE_CLASS_VIDEO_ENHANCE:
+ slice = hwe->instance;
+ break;
+ case XE_ENGINE_CLASS_OTHER:
+ slice = 0;
+ break;
+ default:
+ xe_gt_assert_msg(gt, false,
+ "unknown media gt class %u (%s) during EGS setup\n",
+ hwe->class, hwe->name);
+ slice = 0;
+ }
+
+ values[slice_to_group[slice]].engines[guc_class] |= BIT(hwe->logical_instance);
+ }
+
+ *groups = values;
+ *num_groups = group;
+}
+
+/**
+ * xe_sriov_gt_pf_policy_has_sched_groups_support() - Checks whether scheduler
+ * groups are supported.
+ * @gt: the &xe_gt
+ *
+ * This function can only be called on PF.
+ *
+ * Return: true if scheduler groups are supported, false otherwise.
+ */
+bool xe_sriov_gt_pf_policy_has_sched_groups_support(struct xe_gt *gt)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+
+ /*
+ * The GuC supports scheduler groups from v70.53.0, but a fix for it has
+ * been merged in v70.55.1, so we require the latter. The feature is
+ * also only enabled on BMG and newer FW.
+ */
+ return GUC_FIRMWARE_VER_AT_LEAST(&gt->uc.guc, 70, 55, 1) &&
+ gt_to_xe(gt)->info.platform >= XE_BATTLEMAGE;
+}
+
+static void pf_init_sched_groups(struct xe_gt *gt)
+{
+ enum xe_sriov_sched_group_modes m;
+
+ if (!xe_sriov_gt_pf_policy_has_sched_groups_support(gt))
+ return;
+
+ /*
+ * The GuC interface supports up to 8 groups. However, the GuC only
+ * fully allocates resources for a subset of groups, based on the number
+ * of engines and expected usage. The plan is for this to become
+ * queryable via H2G, but for now GuC FW for all devices supports a
+ * maximum of 2 groups so we can just hardcode that.
+ */
+ gt->sriov.pf.policy.guc.sched_groups.max_groups = 2;
+
+ for (m = XE_SRIOV_SCHED_GROUPS_DISABLED + 1; m < XE_SRIOV_SCHED_GROUPS_MODES_COUNT; m++) {
+ u32 *num_groups = &gt->sriov.pf.policy.guc.sched_groups.modes[m].num_groups;
+ struct guc_sched_group **groups =
+ &gt->sriov.pf.policy.guc.sched_groups.modes[m].groups;
+
+ switch (m) {
+ case XE_SRIOV_SCHED_GROUPS_MEDIA_SLICES:
+ /* this mode only has groups on the media GT */
+ if (xe_gt_is_media_type(gt))
+ pf_sched_group_media_slices(gt, groups, num_groups);
+ break;
+ case XE_SRIOV_SCHED_GROUPS_DISABLED:
+ case XE_SRIOV_SCHED_GROUPS_MODES_COUNT:
+ /*
+ * By defining m of type enum xe_sriov_sched_group_modes
+ * we can get the compiler to automatically flag
+ * missing cases if new enum entries are added. However,
+ * to keep the compiler happy we also need to add the
+ * cases that are excluded from the loop.
+ */
+ xe_gt_assert(gt, false);
+ break;
+ }
+
+ xe_gt_assert(gt, *num_groups < GUC_MAX_SCHED_GROUPS);
+
+ if (*num_groups)
+ gt->sriov.pf.policy.guc.sched_groups.supported_modes |= BIT(m);
+ }
+}
+
+/**
+ * xe_sriov_gt_pf_policy_has_multi_group_modes() - check whether the GT supports
+ * any scheduler modes that have multiple groups
+ * @gt: the &xe_gt to check
+ *
+ * This function can only be called on PF.
+ *
+ * Return: true if the GT supports modes with multiple groups, false otherwise.
+ */
+bool xe_sriov_gt_pf_policy_has_multi_group_modes(struct xe_gt *gt)
+{
+ return gt->sriov.pf.policy.guc.sched_groups.supported_modes;
+}
+
+/**
+ * xe_sriov_gt_pf_policy_has_sched_group_mode() - check whether the GT supports
+ * a specific scheduler group mode
+ * @gt: the &xe_gt to check
+ * @mode: the mode to check
+ *
+ * This function can only be called on PF.
+ *
+ * Return: true if the GT supports the specified mode, false otherwise.
+ */
+bool xe_sriov_gt_pf_policy_has_sched_group_mode(struct xe_gt *gt,
+ enum xe_sriov_sched_group_modes mode)
+{
+ if (mode == XE_SRIOV_SCHED_GROUPS_DISABLED)
+ return true;
+
+ return gt->sriov.pf.policy.guc.sched_groups.supported_modes & BIT(mode);
+}
+
+static int __pf_provision_sched_groups(struct xe_gt *gt, enum xe_sriov_sched_group_modes mode)
+{
+ struct guc_sched_group *groups = gt->sriov.pf.policy.guc.sched_groups.modes[mode].groups;
+ u32 num_groups = gt->sriov.pf.policy.guc.sched_groups.modes[mode].num_groups;
+
+ return pf_push_policy_payload(gt, GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_KEY,
+ groups, num_groups * GUC_MAX_ENGINE_CLASSES);
+}
+
+static int pf_provision_sched_groups(struct xe_gt *gt, enum xe_sriov_sched_group_modes mode)
+{
+ int err;
+
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
+
+ if (!xe_sriov_gt_pf_policy_has_sched_group_mode(gt, mode))
+ return -EINVAL;
+
+ /* already in the desired mode */
+ if (gt->sriov.pf.policy.guc.sched_groups.current_mode == mode)
+ return 0;
+
+ /*
+ * We don't allow changing this with VFs active since it is hard for
+ * VFs to check.
+ */
+ if (xe_sriov_pf_num_vfs(gt_to_xe(gt)))
+ return -EBUSY;
+
+ /*
+ * The GuC silently ignores the setting if any MLRC contexts are
+ * registered. We expect the admin to make sure that all apps that use
+ * MLRC are terminated before scheduler groups are enabled, so this
+ * check is just to make sure that the exec_queue destruction has been
+ * completed.
+ */
+ if (mode != XE_SRIOV_SCHED_GROUPS_DISABLED &&
+ xe_guc_has_registered_mlrc_queues(&gt->uc.guc)) {
+ xe_gt_sriov_notice(gt, "can't enable sched groups with active MLRC queues\n");
+ return -EPERM;
+ }
+
+ err = __pf_provision_sched_groups(gt, mode);
+ if (err)
+ return err;
+
+ gt->sriov.pf.policy.guc.sched_groups.current_mode = mode;
+
+ return 0;
+}
+
+static int pf_reprovision_sched_groups(struct xe_gt *gt)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
+
+ /* We only have something to provision if we have possible groups */
+ if (!xe_sriov_gt_pf_policy_has_multi_group_modes(gt))
+ return 0;
+
+ return __pf_provision_sched_groups(gt, gt->sriov.pf.policy.guc.sched_groups.current_mode);
+}
+
+static void pf_sanitize_sched_groups(struct xe_gt *gt)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
+
+ gt->sriov.pf.policy.guc.sched_groups.current_mode = XE_SRIOV_SCHED_GROUPS_DISABLED;
+}
+
+/**
+ * xe_gt_sriov_pf_policy_set_sched_groups_mode() - Control the 'sched_groups' policy.
+ * @gt: the &xe_gt where to apply the policy
+ * @mode: the sched_group mode to be activated
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_policy_set_sched_groups_mode(struct xe_gt *gt,
+ enum xe_sriov_sched_group_modes mode)
+{
+ if (!xe_sriov_gt_pf_policy_has_multi_group_modes(gt))
+ return -ENODEV;
+
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
+ return pf_provision_sched_groups(gt, mode);
+}
+
+/**
+ * xe_gt_sriov_pf_policy_sched_groups_enabled() - check whether the GT has
+ * multiple scheduler groups enabled
+ * @gt: the &xe_gt to check
+ *
+ * This function can only be called on PF.
+ *
+ * Return: true if the GT has multiple groups enabled, false otherwise.
+ */
+bool xe_gt_sriov_pf_policy_sched_groups_enabled(struct xe_gt *gt)
+{
+ return gt->sriov.pf.policy.guc.sched_groups.current_mode != XE_SRIOV_SCHED_GROUPS_DISABLED;
+}
+
static void pf_sanitize_guc_policies(struct xe_gt *gt)
{
pf_sanitize_sched_if_idle(gt);
pf_sanitize_reset_engine(gt);
pf_sanitize_sample_period(gt);
+ pf_sanitize_sched_groups(gt);
}
/**
@@ -394,6 +709,7 @@ int xe_gt_sriov_pf_policy_reprovision(struct xe_gt *gt, bool reset)
err |= pf_reprovision_sched_if_idle(gt);
err |= pf_reprovision_reset_engine(gt);
err |= pf_reprovision_sample_period(gt);
+ err |= pf_reprovision_sched_groups(gt);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
xe_pm_runtime_put(gt_to_xe(gt));
@@ -401,6 +717,18 @@ int xe_gt_sriov_pf_policy_reprovision(struct xe_gt *gt, bool reset)
return err ? -ENXIO : 0;
}
+/**
+ * xe_gt_sriov_pf_policy_init() - Initializes the SW state of the PF policies.
+ * @gt: the &xe_gt
+ *
+ * This function can only be called on PF. This function does not touch the HW,
+ * but must be called after the engines have been initialized.
+ */
+void xe_gt_sriov_pf_policy_init(struct xe_gt *gt)
+{
+ pf_init_sched_groups(gt);
+}
+
static void print_guc_policies(struct drm_printer *p, struct xe_gt_sriov_guc_policies *policy)
{
drm_printf(p, "%s:\t%s\n",
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h
index 2a5dc33dc6d7..bd73aa58f9ca 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h
@@ -8,6 +8,8 @@
#include <linux/types.h>
+#include "xe_gt_sriov_pf_policy_types.h"
+
struct drm_printer;
struct xe_gt;
@@ -17,7 +19,15 @@ int xe_gt_sriov_pf_policy_set_reset_engine(struct xe_gt *gt, bool enable);
bool xe_gt_sriov_pf_policy_get_reset_engine(struct xe_gt *gt);
int xe_gt_sriov_pf_policy_set_sample_period(struct xe_gt *gt, u32 value);
u32 xe_gt_sriov_pf_policy_get_sample_period(struct xe_gt *gt);
+bool xe_sriov_gt_pf_policy_has_sched_groups_support(struct xe_gt *gt);
+bool xe_sriov_gt_pf_policy_has_multi_group_modes(struct xe_gt *gt);
+bool xe_sriov_gt_pf_policy_has_sched_group_mode(struct xe_gt *gt,
+ enum xe_sriov_sched_group_modes mode);
+int xe_gt_sriov_pf_policy_set_sched_groups_mode(struct xe_gt *gt,
+ enum xe_sriov_sched_group_modes mode);
+bool xe_gt_sriov_pf_policy_sched_groups_enabled(struct xe_gt *gt);
+void xe_gt_sriov_pf_policy_init(struct xe_gt *gt);
void xe_gt_sriov_pf_policy_sanitize(struct xe_gt *gt);
int xe_gt_sriov_pf_policy_reprovision(struct xe_gt *gt, bool reset);
int xe_gt_sriov_pf_policy_print(struct xe_gt *gt, struct drm_printer *p);
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h
index 4de532af135e..97d278190521 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h
@@ -8,16 +8,55 @@
#include <linux/types.h>
+#include "abi/guc_scheduler_abi.h"
+
+/**
+ * enum xe_sriov_sched_group_modes - list of possible scheduler group modes
+ * @XE_SRIOV_SCHED_GROUPS_DISABLED: no separate groups (i.e., all engines in group 0)
+ * @XE_SRIOV_SCHED_GROUPS_MEDIA_SLICES: separate groups for each media slice
+ * @XE_SRIOV_SCHED_GROUPS_MODES_COUNT: number of valid modes
+ */
+enum xe_sriov_sched_group_modes {
+ XE_SRIOV_SCHED_GROUPS_DISABLED = 0,
+ XE_SRIOV_SCHED_GROUPS_MEDIA_SLICES,
+ XE_SRIOV_SCHED_GROUPS_MODES_COUNT /* must be last */
+};
+
+/**
+ * struct xe_gt_sriov_scheduler_groups - Scheduler groups policy info
+ * @max_groups: max number of groups supported by the GuC for the platform
+ * @supported_modes: mask of supported modes
+ * @current_mode: active scheduler groups mode
+ * @modes: array of masks and their number for each mode
+ * @modes.groups: array of engine instance groups in given mode, with each group
+ * consisting of GUC_MAX_ENGINE_CLASSES engine instances masks. A
+ * A NULL value indicates that all the engines are in the same
+ * group for this mode on this GT.
+ * @modes.num_groups: number of groups in given mode, zero if all the engines
+ * are in the same group.
+ */
+struct xe_gt_sriov_scheduler_groups {
+ u8 max_groups;
+ u32 supported_modes;
+ enum xe_sriov_sched_group_modes current_mode;
+ struct {
+ struct guc_sched_group *groups;
+ u32 num_groups;
+ } modes[XE_SRIOV_SCHED_GROUPS_MODES_COUNT];
+};
+
/**
* struct xe_gt_sriov_guc_policies - GuC SR-IOV policies.
* @sched_if_idle: controls strict scheduling policy.
* @reset_engine: controls engines reset on VF switch policy.
* @sample_period: adverse events sampling period (in milliseconds).
+ * @sched_groups: available scheduling group configurations.
*/
struct xe_gt_sriov_guc_policies {
bool sched_if_idle;
bool reset_engine;
u32 sample_period;
+ struct xe_gt_sriov_scheduler_groups sched_groups;
};
/**
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c
index 2eb21610e5a0..b5e0a5b7723e 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c
@@ -5,20 +5,19 @@
#include <drm/drm_managed.h>
-#include "abi/guc_actions_sriov_abi.h"
#include "abi/guc_relay_actions_abi.h"
#include "regs/xe_gt_regs.h"
#include "regs/xe_guc_regs.h"
-#include "regs/xe_regs.h"
+#include "xe_assert.h"
#include "xe_mmio.h"
#include "xe_gt_sriov_printk.h"
-#include "xe_gt_sriov_pf_helpers.h"
#include "xe_gt_sriov_pf_service.h"
#include "xe_gt_sriov_pf_service_types.h"
#include "xe_guc_ct.h"
#include "xe_guc_hxg_helpers.h"
+#include "xe_sriov.h"
#include "xe_sriov_pf_service.h"
static const struct xe_reg tgl_runtime_regs[] = {
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
index 4c73a077d314..30e8c2cf5f09 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
@@ -5,6 +5,7 @@
#include <linux/bitfield.h>
#include <linux/bsearch.h>
+#include <linux/delay.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
@@ -14,7 +15,6 @@
#include "abi/guc_klvs_abi.h"
#include "abi/guc_relay_actions_abi.h"
#include "regs/xe_gt_regs.h"
-#include "regs/xe_gtt_defs.h"
#include "xe_assert.h"
#include "xe_device.h"
@@ -41,6 +41,37 @@
#define make_u64_from_u32(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo)))
+#ifdef CONFIG_DRM_XE_DEBUG
+enum VF_MIGRATION_WAIT_POINTS {
+ VF_MIGRATION_WAIT_RESFIX_START = BIT(0),
+ VF_MIGRATION_WAIT_FIXUPS = BIT(1),
+ VF_MIGRATION_WAIT_RESTART_JOBS = BIT(2),
+ VF_MIGRATION_WAIT_RESFIX_DONE = BIT(3),
+};
+
+#define VF_MIGRATION_WAIT_DELAY_IN_MS 1000
+static void vf_post_migration_inject_wait(struct xe_gt *gt,
+ enum VF_MIGRATION_WAIT_POINTS wait)
+{
+ while (gt->sriov.vf.migration.debug.resfix_stoppers & wait) {
+ xe_gt_dbg(gt,
+ "*TESTING* injecting %u ms delay due to resfix_stoppers=%#x, to continue clear %#x\n",
+ VF_MIGRATION_WAIT_DELAY_IN_MS,
+ gt->sriov.vf.migration.debug.resfix_stoppers, wait);
+
+ msleep(VF_MIGRATION_WAIT_DELAY_IN_MS);
+ }
+}
+
+#define VF_MIGRATION_INJECT_WAIT(gt, _POS) ({ \
+ struct xe_gt *__gt = (gt); \
+ vf_post_migration_inject_wait(__gt, VF_MIGRATION_WAIT_##_POS); \
+ })
+
+#else
+#define VF_MIGRATION_INJECT_WAIT(_gt, ...) typecheck(struct xe_gt *, (_gt))
+#endif
+
static int guc_action_vf_reset(struct xe_guc *guc)
{
u32 request[GUC_HXG_REQUEST_MSG_MIN_LEN] = {
@@ -299,12 +330,13 @@ void xe_gt_sriov_vf_guc_versions(struct xe_gt *gt,
*found = gt->sriov.vf.guc_version;
}
-static int guc_action_vf_notify_resfix_done(struct xe_guc *guc)
+static int guc_action_vf_resfix_start(struct xe_guc *guc, u16 marker)
{
u32 request[GUC_HXG_REQUEST_MSG_MIN_LEN] = {
FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
- FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE),
+ FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_ACTION_VF2GUC_RESFIX_START) |
+ FIELD_PREP(VF2GUC_RESFIX_START_REQUEST_MSG_0_MARKER, marker),
};
int ret;
@@ -313,28 +345,43 @@ static int guc_action_vf_notify_resfix_done(struct xe_guc *guc)
return ret > 0 ? -EPROTO : ret;
}
-/**
- * vf_notify_resfix_done - Notify GuC about resource fixups apply completed.
- * @gt: the &xe_gt struct instance linked to target GuC
- *
- * Returns: 0 if the operation completed successfully, or a negative error
- * code otherwise.
- */
-static int vf_notify_resfix_done(struct xe_gt *gt)
+static int vf_resfix_start(struct xe_gt *gt, u16 marker)
{
struct xe_guc *guc = &gt->uc.guc;
- int err;
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- err = guc_action_vf_notify_resfix_done(guc);
- if (unlikely(err))
- xe_gt_sriov_err(gt, "Failed to notify GuC about resource fixup done (%pe)\n",
- ERR_PTR(err));
- else
- xe_gt_sriov_dbg_verbose(gt, "sent GuC resource fixup done\n");
+ VF_MIGRATION_INJECT_WAIT(gt, RESFIX_START);
- return err;
+ xe_gt_sriov_dbg_verbose(gt, "Sending resfix start marker %u\n", marker);
+
+ return guc_action_vf_resfix_start(guc, marker);
+}
+
+static int guc_action_vf_resfix_done(struct xe_guc *guc, u16 marker)
+{
+ u32 request[GUC_HXG_REQUEST_MSG_MIN_LEN] = {
+ FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+ FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
+ FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_ACTION_VF2GUC_RESFIX_DONE) |
+ FIELD_PREP(VF2GUC_RESFIX_DONE_REQUEST_MSG_0_MARKER, marker),
+ };
+ int ret;
+
+ ret = xe_guc_mmio_send(guc, request, ARRAY_SIZE(request));
+
+ return ret > 0 ? -EPROTO : ret;
+}
+
+static int vf_resfix_done(struct xe_gt *gt, u16 marker)
+{
+ struct xe_guc *guc = &gt->uc.guc;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ xe_gt_sriov_dbg_verbose(gt, "Sending resfix done marker %u\n", marker);
+
+ return guc_action_vf_resfix_done(guc, marker);
}
static int guc_action_query_single_klv(struct xe_guc *guc, u32 key,
@@ -564,6 +611,52 @@ static void vf_cache_gmdid(struct xe_gt *gt)
gt->sriov.vf.runtime.gmdid = xe_gt_sriov_vf_gmdid(gt);
}
+static int vf_query_sched_groups(struct xe_gt *gt)
+{
+ struct xe_guc *guc = &gt->uc.guc;
+ struct xe_uc_fw_version guc_version;
+ u32 value = 0;
+ int err;
+
+ xe_gt_sriov_vf_guc_versions(gt, NULL, &guc_version);
+
+ if (MAKE_GUC_VER_STRUCT(guc_version) < MAKE_GUC_VER(1, 26, 0))
+ return 0;
+
+ err = guc_action_query_single_klv32(guc,
+ GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE_KEY,
+ &value);
+ if (unlikely(err)) {
+ xe_gt_sriov_err(gt, "Failed to obtain sched groups status (%pe)\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ /* valid values are 0 (disabled) and 1 (enabled) */
+ if (value > 1) {
+ xe_gt_sriov_err(gt, "Invalid sched groups status %u\n", value);
+ return -EPROTO;
+ }
+
+ xe_gt_sriov_dbg(gt, "sched groups %s\n", str_enabled_disabled(value));
+ return value;
+}
+
+static int vf_cache_sched_groups_status(struct xe_gt *gt)
+{
+ int ret;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ ret = vf_query_sched_groups(gt);
+ if (ret < 0)
+ return ret;
+
+ gt->sriov.vf.runtime.uses_sched_groups = ret;
+
+ return 0;
+}
+
/**
* xe_gt_sriov_vf_query_config - Query SR-IOV config data over MMIO.
* @gt: the &xe_gt
@@ -593,6 +686,10 @@ int xe_gt_sriov_vf_query_config(struct xe_gt *gt)
if (unlikely(err))
return err;
+ err = vf_cache_sched_groups_status(gt);
+ if (unlikely(err))
+ return err;
+
if (has_gmdid(xe))
vf_cache_gmdid(gt);
@@ -600,6 +697,23 @@ int xe_gt_sriov_vf_query_config(struct xe_gt *gt)
}
/**
+ * xe_gt_sriov_vf_sched_groups_enabled() - Check if PF has enabled multiple
+ * scheduler groups
+ * @gt: the &xe_gt
+ *
+ * This function is for VF use only.
+ *
+ * Return: true if shed groups were enabled, false otherwise.
+ */
+bool xe_gt_sriov_vf_sched_groups_enabled(struct xe_gt *gt)
+{
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+ xe_gt_assert(gt, gt->sriov.vf.guc_version.major);
+
+ return gt->sriov.vf.runtime.uses_sched_groups;
+}
+
+/**
* xe_gt_sriov_vf_guc_ids - VF GuC context IDs configuration.
* @gt: the &xe_gt
*
@@ -733,7 +847,7 @@ static void vf_start_migration_recovery(struct xe_gt *gt)
spin_lock(&gt->sriov.vf.migration.lock);
- if (!gt->sriov.vf.migration.recovery_queued ||
+ if (!gt->sriov.vf.migration.recovery_queued &&
!gt->sriov.vf.migration.recovery_teardown) {
gt->sriov.vf.migration.recovery_queued = true;
WRITE_ONCE(gt->sriov.vf.migration.recovery_inprogress, true);
@@ -1123,12 +1237,8 @@ static bool vf_post_migration_shutdown(struct xe_gt *gt)
return true;
}
- spin_lock_irq(&gt->sriov.vf.migration.lock);
- gt->sriov.vf.migration.recovery_queued = false;
- spin_unlock_irq(&gt->sriov.vf.migration.lock);
-
xe_guc_ct_flush_and_stop(&gt->uc.guc.ct);
- xe_guc_submit_pause(&gt->uc.guc);
+ xe_guc_submit_pause_vf(&gt->uc.guc);
xe_tlb_inval_reset(&gt->tlb_inval);
return false;
@@ -1144,6 +1254,8 @@ static int vf_post_migration_fixups(struct xe_gt *gt)
void *buf = gt->sriov.vf.migration.scratch;
int err;
+ VF_MIGRATION_INJECT_WAIT(gt, FIXUPS);
+
/* xe_gt_sriov_vf_query_config will fixup the GGTT addresses */
err = xe_gt_sriov_vf_query_config(gt);
if (err)
@@ -1162,13 +1274,22 @@ static int vf_post_migration_fixups(struct xe_gt *gt)
static void vf_post_migration_rearm(struct xe_gt *gt)
{
+ VF_MIGRATION_INJECT_WAIT(gt, RESTART_JOBS);
+
+ /*
+ * Make sure interrupts on the new HW are properly set. The GuC IRQ
+ * must be working at this point, since the recovery did started,
+ * but the rest was not enabled using the procedure from spec.
+ */
+ xe_irq_resume(gt_to_xe(gt));
+
xe_guc_ct_restart(&gt->uc.guc.ct);
- xe_guc_submit_unpause_prepare(&gt->uc.guc);
+ xe_guc_submit_unpause_prepare_vf(&gt->uc.guc);
}
static void vf_post_migration_kickstart(struct xe_gt *gt)
{
- xe_guc_submit_unpause(&gt->uc.guc);
+ xe_guc_submit_unpause_vf(&gt->uc.guc);
}
static void vf_post_migration_abort(struct xe_gt *gt)
@@ -1183,37 +1304,49 @@ static void vf_post_migration_abort(struct xe_gt *gt)
xe_guc_submit_pause_abort(&gt->uc.guc);
}
-static int vf_post_migration_notify_resfix_done(struct xe_gt *gt)
+static int vf_post_migration_resfix_done(struct xe_gt *gt, u16 marker)
{
- bool skip_resfix = false;
+ VF_MIGRATION_INJECT_WAIT(gt, RESFIX_DONE);
spin_lock_irq(&gt->sriov.vf.migration.lock);
- if (gt->sriov.vf.migration.recovery_queued) {
- skip_resfix = true;
- xe_gt_sriov_dbg(gt, "another recovery imminent, resfix skipped\n");
- } else {
+ if (gt->sriov.vf.migration.recovery_queued)
+ xe_gt_sriov_dbg(gt, "another recovery imminent\n");
+ else
WRITE_ONCE(gt->sriov.vf.migration.recovery_inprogress, false);
- }
spin_unlock_irq(&gt->sriov.vf.migration.lock);
- if (skip_resfix)
- return -EAGAIN;
+ return vf_resfix_done(gt, marker);
+}
- /*
- * Make sure interrupts on the new HW are properly set. The GuC IRQ
- * must be working at this point, since the recovery did started,
- * but the rest was not enabled using the procedure from spec.
- */
- xe_irq_resume(gt_to_xe(gt));
+static int vf_post_migration_resfix_start(struct xe_gt *gt, u16 marker)
+{
+ int err;
+
+ err = vf_resfix_start(gt, marker);
+
+ guard(spinlock_irq) (&gt->sriov.vf.migration.lock);
+ gt->sriov.vf.migration.recovery_queued = false;
- return vf_notify_resfix_done(gt);
+ return err;
+}
+
+static u16 vf_post_migration_next_resfix_marker(struct xe_gt *gt)
+{
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ BUILD_BUG_ON(1 + ((typeof(gt->sriov.vf.migration.resfix_marker))~0) >
+ FIELD_MAX(VF2GUC_RESFIX_START_REQUEST_MSG_0_MARKER));
+
+ /* add 1 to avoid zero-marker */
+ return 1 + gt->sriov.vf.migration.resfix_marker++;
}
static void vf_post_migration_recovery(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
- int err;
+ u16 marker;
bool retry;
+ int err;
xe_gt_sriov_dbg(gt, "migration recovery in progress\n");
@@ -1227,15 +1360,30 @@ static void vf_post_migration_recovery(struct xe_gt *gt)
goto fail;
}
+ marker = vf_post_migration_next_resfix_marker(gt);
+
+ err = vf_post_migration_resfix_start(gt, marker);
+ if (unlikely(err)) {
+ xe_gt_sriov_err(gt, "Recovery failed at GuC RESFIX_START step (%pe)\n",
+ ERR_PTR(err));
+ goto fail;
+ }
+
err = vf_post_migration_fixups(gt);
if (err)
goto fail;
vf_post_migration_rearm(gt);
- err = vf_post_migration_notify_resfix_done(gt);
- if (err && err != -EAGAIN)
+ err = vf_post_migration_resfix_done(gt, marker);
+ if (err) {
+ if (err == -EREMCHG)
+ goto queue;
+
+ xe_gt_sriov_err(gt, "Recovery failed at GuC RESFIX_DONE step (%pe)\n",
+ ERR_PTR(err));
goto fail;
+ }
vf_post_migration_kickstart(gt);
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
index af40276790fa..7d97189c2d3d 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
@@ -30,6 +30,7 @@ bool xe_gt_sriov_vf_recovery_pending(struct xe_gt *gt);
u32 xe_gt_sriov_vf_gmdid(struct xe_gt *gt);
u16 xe_gt_sriov_vf_guc_ids(struct xe_gt *gt);
u64 xe_gt_sriov_vf_lmem(struct xe_gt *gt);
+bool xe_gt_sriov_vf_sched_groups_enabled(struct xe_gt *gt);
u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg);
void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val);
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c
index 2ed5b6780d30..507718326e1f 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c
@@ -69,4 +69,16 @@ void xe_gt_sriov_vf_debugfs_register(struct xe_gt *gt, struct dentry *root)
vfdentry->d_inode->i_private = gt;
drm_debugfs_create_files(vf_info, ARRAY_SIZE(vf_info), vfdentry, minor);
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── tile0
+ * ├── gt0
+ * ├── vf
+ * ├── resfix_stoppers
+ */
+ if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
+ debugfs_create_x8("resfix_stoppers", 0600, vfdentry,
+ &gt->sriov.vf.migration.debug.resfix_stoppers);
+ }
}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
index 420b0e6089de..4ef881b9b662 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
@@ -27,6 +27,8 @@ struct xe_gt_sriov_vf_selfconfig {
struct xe_gt_sriov_vf_runtime {
/** @gmdid: cached value of the GDMID register. */
u32 gmdid;
+ /** @uses_sched_groups: whether PF enabled sched groups or not. */
+ bool uses_sched_groups;
/** @regs_size: size of runtime register array. */
u32 regs_size;
/** @num_regs: number of runtime registers in the array. */
@@ -41,10 +43,10 @@ struct xe_gt_sriov_vf_runtime {
};
/**
- * xe_gt_sriov_vf_migration - VF migration data.
+ * struct xe_gt_sriov_vf_migration - VF migration data.
*/
struct xe_gt_sriov_vf_migration {
- /** @migration: VF migration recovery worker */
+ /** @worker: VF migration recovery worker */
struct work_struct worker;
/** @lock: Protects recovery_queued, teardown */
spinlock_t lock;
@@ -52,6 +54,19 @@ struct xe_gt_sriov_vf_migration {
wait_queue_head_t wq;
/** @scratch: Scratch memory for VF recovery */
void *scratch;
+ /** @debug: Debug hooks for delaying migration */
+ struct {
+ /**
+ * @debug.resfix_stoppers: Stop and wait at different stages
+ * during post migration recovery
+ */
+ u8 resfix_stoppers;
+ } debug;
+ /**
+ * @resfix_marker: Marker sent on start and on end of post-migration
+ * steps.
+ */
+ u8 resfix_marker;
/** @recovery_teardown: VF post migration recovery is being torn down */
bool recovery_teardown;
/** @recovery_queued: VF post migration recovery in queued */
diff --git a/drivers/gpu/drm/xe/xe_gt_stats.c b/drivers/gpu/drm/xe/xe_gt_stats.c
index 5f74706bab81..37506434d7a3 100644
--- a/drivers/gpu/drm/xe/xe_gt_stats.c
+++ b/drivers/gpu/drm/xe/xe_gt_stats.c
@@ -7,8 +7,8 @@
#include <drm/drm_print.h>
-#include "xe_gt.h"
#include "xe_gt_stats.h"
+#include "xe_gt_types.h"
/**
* xe_gt_stats_incr - Increments the specified stats counter
@@ -66,6 +66,21 @@ static const char *const stat_description[__XE_GT_STATS_NUM_IDS] = {
DEF_STAT_STR(SVM_4K_BIND_US, "svm_4K_bind_us"),
DEF_STAT_STR(SVM_64K_BIND_US, "svm_64K_bind_us"),
DEF_STAT_STR(SVM_2M_BIND_US, "svm_2M_bind_us"),
+ DEF_STAT_STR(HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT,
+ "hw_engine_group_suspend_lr_queue_count"),
+ DEF_STAT_STR(HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT,
+ "hw_engine_group_skip_lr_queue_count"),
+ DEF_STAT_STR(HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT,
+ "hw_engine_group_wait_dma_queue_count"),
+ DEF_STAT_STR(HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US,
+ "hw_engine_group_suspend_lr_queue_us"),
+ DEF_STAT_STR(HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US,
+ "hw_engine_group_wait_dma_queue_us"),
+ DEF_STAT_STR(PRL_4K_ENTRY_COUNT, "prl_4k_entry_count"),
+ DEF_STAT_STR(PRL_64K_ENTRY_COUNT, "prl_64k_entry_count"),
+ DEF_STAT_STR(PRL_2M_ENTRY_COUNT, "prl_2m_entry_count"),
+ DEF_STAT_STR(PRL_ISSUED_COUNT, "prl_issued_count"),
+ DEF_STAT_STR(PRL_ABORTED_COUNT, "prl_aborted_count"),
};
/**
diff --git a/drivers/gpu/drm/xe/xe_gt_stats.h b/drivers/gpu/drm/xe/xe_gt_stats.h
index e8aea32bc971..59a7bf60e242 100644
--- a/drivers/gpu/drm/xe/xe_gt_stats.h
+++ b/drivers/gpu/drm/xe/xe_gt_stats.h
@@ -6,6 +6,8 @@
#ifndef _XE_GT_STATS_H_
#define _XE_GT_STATS_H_
+#include <linux/ktime.h>
+
#include "xe_gt_stats_types.h"
struct xe_gt;
@@ -23,4 +25,34 @@ xe_gt_stats_incr(struct xe_gt *gt, const enum xe_gt_stats_id id,
}
#endif
+
+/**
+ * xe_gt_stats_ktime_us_delta() - Get delta in microseconds between now and a
+ * start time
+ * @start: Start time
+ *
+ * Helper for GT stats to get delta in microseconds between now and a start
+ * time, compiles out if GT stats are disabled.
+ *
+ * Return: Delta in microseconds between now and a start time
+ */
+static inline s64 xe_gt_stats_ktime_us_delta(ktime_t start)
+{
+ return IS_ENABLED(CONFIG_DEBUG_FS) ?
+ ktime_us_delta(ktime_get(), start) : 0;
+}
+
+/**
+ * xe_gt_stats_ktime_get() - Get current ktime
+ *
+ * Helper for GT stats to get current ktime, compiles out if GT stats are
+ * disabled.
+ *
+ * Return: Get current ktime
+ */
+static inline ktime_t xe_gt_stats_ktime_get(void)
+{
+ return IS_ENABLED(CONFIG_DEBUG_FS) ? ktime_get() : 0;
+}
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_stats_types.h b/drivers/gpu/drm/xe/xe_gt_stats_types.h
index d8348a8de2e1..b8accdbc54eb 100644
--- a/drivers/gpu/drm/xe/xe_gt_stats_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_stats_types.h
@@ -44,6 +44,16 @@ enum xe_gt_stats_id {
XE_GT_STATS_ID_SVM_4K_BIND_US,
XE_GT_STATS_ID_SVM_64K_BIND_US,
XE_GT_STATS_ID_SVM_2M_BIND_US,
+ XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT,
+ XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT,
+ XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT,
+ XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US,
+ XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US,
+ XE_GT_STATS_ID_PRL_4K_ENTRY_COUNT,
+ XE_GT_STATS_ID_PRL_64K_ENTRY_COUNT,
+ XE_GT_STATS_ID_PRL_2M_ENTRY_COUNT,
+ XE_GT_STATS_ID_PRL_ISSUED_COUNT,
+ XE_GT_STATS_ID_PRL_ABORTED_COUNT,
/* must be the last entry */
__XE_GT_STATS_NUM_IDS,
};
diff --git a/drivers/gpu/drm/xe/xe_gt_sysfs.c b/drivers/gpu/drm/xe/xe_gt_sysfs.c
index ec2b8246204b..1448be047b4a 100644
--- a/drivers/gpu/drm/xe/xe_gt_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_sysfs.c
@@ -10,7 +10,7 @@
#include <drm/drm_managed.h>
-#include "xe_gt.h"
+#include "xe_gt_types.h"
static void xe_gt_sysfs_kobj_release(struct kobject *kobj)
{
diff --git a/drivers/gpu/drm/xe/xe_gt_throttle.c b/drivers/gpu/drm/xe/xe_gt_throttle.c
index 82c5fbcdfbe3..1e7e3a31aa69 100644
--- a/drivers/gpu/drm/xe/xe_gt_throttle.c
+++ b/drivers/gpu/drm/xe/xe_gt_throttle.c
@@ -6,7 +6,7 @@
#include <drm/drm_managed.h>
#include <regs/xe_gt_regs.h>
-#include "xe_device.h"
+#include "xe_device_types.h"
#include "xe_gt.h"
#include "xe_gt_sysfs.h"
#include "xe_gt_throttle.h"
@@ -85,7 +85,7 @@ u32 xe_gt_throttle_get_limit_reasons(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
struct xe_reg reg;
- u32 val, mask;
+ u32 mask;
if (xe_gt_is_media_type(gt))
reg = MTL_MEDIA_PERF_LIMIT_REASONS;
@@ -97,11 +97,8 @@ u32 xe_gt_throttle_get_limit_reasons(struct xe_gt *gt)
else
mask = GT0_PERF_LIMIT_REASONS_MASK;
- xe_pm_runtime_get(xe);
- val = xe_mmio_read32(&gt->mmio, reg) & mask;
- xe_pm_runtime_put(xe);
-
- return val;
+ guard(xe_pm_runtime)(xe);
+ return xe_mmio_read32(&gt->mmio, reg) & mask;
}
static bool is_throttled_by(struct xe_gt *gt, u32 mask)
@@ -140,7 +137,7 @@ static ssize_t reasons_show(struct kobject *kobj,
struct throttle_attribute *other_ta = kobj_attribute_to_throttle(kattr);
if (other_ta->mask != U32_MAX && reasons & other_ta->mask)
- ret += sysfs_emit_at(buff, ret, "%s ", (*pother)->name);
+ ret += sysfs_emit_at(buff, ret, "%s ", (*pother)->name + strlen("reason_"));
}
if (drm_WARN_ONCE(&xe->drm, !ret, "Unknown reason: %#x\n", reasons))
diff --git a/drivers/gpu/drm/xe/xe_gt_types.h b/drivers/gpu/drm/xe/xe_gt_types.h
index 0a728180b6fe..5318d92fd473 100644
--- a/drivers/gpu/drm/xe/xe_gt_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_types.h
@@ -140,6 +140,11 @@ struct xe_gt {
u64 engine_mask;
/** @info.gmdid: raw GMD_ID value from hardware */
u32 gmdid;
+ /**
+ * @multi_queue_engine_class_mask: Bitmask of engine classes with
+ * multi queue support enabled.
+ */
+ u16 multi_queue_engine_class_mask;
/** @info.id: Unique ID of this GT within the PCI Device */
u8 id;
/** @info.has_indirect_ring_state: GT has indirect ring state support */
diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c
index a686b04879d6..44360437beeb 100644
--- a/drivers/gpu/drm/xe/xe_guc.c
+++ b/drivers/gpu/drm/xe/xe_guc.c
@@ -104,7 +104,7 @@ static u32 guc_ctl_log_params_flags(struct xe_guc *guc)
u32 offset = guc_bo_ggtt_addr(guc, guc->log.bo) >> PAGE_SHIFT;
u32 flags;
- #if (((CRASH_BUFFER_SIZE) % SZ_1M) == 0)
+ #if (((XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE) % SZ_1M) == 0)
#define LOG_UNIT SZ_1M
#define LOG_FLAG GUC_LOG_LOG_ALLOC_UNITS
#else
@@ -112,7 +112,7 @@ static u32 guc_ctl_log_params_flags(struct xe_guc *guc)
#define LOG_FLAG 0
#endif
- #if (((CAPTURE_BUFFER_SIZE) % SZ_1M) == 0)
+ #if (((XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE) % SZ_1M) == 0)
#define CAPTURE_UNIT SZ_1M
#define CAPTURE_FLAG GUC_LOG_CAPTURE_ALLOC_UNITS
#else
@@ -120,20 +120,21 @@ static u32 guc_ctl_log_params_flags(struct xe_guc *guc)
#define CAPTURE_FLAG 0
#endif
- BUILD_BUG_ON(!CRASH_BUFFER_SIZE);
- BUILD_BUG_ON(!IS_ALIGNED(CRASH_BUFFER_SIZE, LOG_UNIT));
- BUILD_BUG_ON(!DEBUG_BUFFER_SIZE);
- BUILD_BUG_ON(!IS_ALIGNED(DEBUG_BUFFER_SIZE, LOG_UNIT));
- BUILD_BUG_ON(!CAPTURE_BUFFER_SIZE);
- BUILD_BUG_ON(!IS_ALIGNED(CAPTURE_BUFFER_SIZE, CAPTURE_UNIT));
+ BUILD_BUG_ON(!XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE);
+ BUILD_BUG_ON(!IS_ALIGNED(XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE, LOG_UNIT));
+ BUILD_BUG_ON(!XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE);
+ BUILD_BUG_ON(!IS_ALIGNED(XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE, LOG_UNIT));
+ BUILD_BUG_ON(!XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE);
+ BUILD_BUG_ON(!IS_ALIGNED(XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE, CAPTURE_UNIT));
flags = GUC_LOG_VALID |
GUC_LOG_NOTIFY_ON_HALF_FULL |
CAPTURE_FLAG |
LOG_FLAG |
- FIELD_PREP(GUC_LOG_CRASH, CRASH_BUFFER_SIZE / LOG_UNIT - 1) |
- FIELD_PREP(GUC_LOG_DEBUG, DEBUG_BUFFER_SIZE / LOG_UNIT - 1) |
- FIELD_PREP(GUC_LOG_CAPTURE, CAPTURE_BUFFER_SIZE / CAPTURE_UNIT - 1) |
+ FIELD_PREP(GUC_LOG_CRASH_DUMP, XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE / LOG_UNIT - 1) |
+ FIELD_PREP(GUC_LOG_EVENT_DATA, XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE / LOG_UNIT - 1) |
+ FIELD_PREP(GUC_LOG_STATE_CAPTURE, XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE /
+ CAPTURE_UNIT - 1) |
FIELD_PREP(GUC_LOG_BUF_ADDR, offset);
#undef LOG_UNIT
@@ -174,7 +175,7 @@ static bool needs_wa_dual_queue(struct xe_gt *gt)
* the DUAL_QUEUE_WA on all newer platforms on GTs that have CCS engines
* to move management back to the GuC.
*/
- if (CCS_MASK(gt) && GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270)
+ if (CCS_INSTANCES(gt) && GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270)
return true;
return false;
@@ -660,11 +661,9 @@ static void guc_fini_hw(void *arg)
{
struct xe_guc *guc = arg;
struct xe_gt *gt = guc_to_gt(guc);
- unsigned int fw_ref;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
- xe_uc_sanitize_reset(&guc_to_gt(guc)->uc);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ xe_with_force_wake(fw_ref, gt_to_fw(gt), XE_FORCEWAKE_ALL)
+ xe_uc_sanitize_reset(&guc_to_gt(guc)->uc);
guc_g2g_fini(guc);
}
@@ -768,6 +767,10 @@ int xe_guc_init(struct xe_guc *guc)
if (!xe_uc_fw_is_enabled(&guc->fw))
return 0;
+ /* Disable page reclaim if GuC FW does not support */
+ if (GUC_SUBMIT_VER(guc) < MAKE_GUC_VER(1, 14, 0))
+ xe->info.has_page_reclaim_hw_assist = false;
+
if (IS_SRIOV_VF(xe)) {
ret = xe_guc_ct_init(&guc->ct);
if (ret)
@@ -1485,6 +1488,12 @@ timeout:
u32 hint = FIELD_GET(GUC_HXG_FAILURE_MSG_0_HINT, header);
u32 error = FIELD_GET(GUC_HXG_FAILURE_MSG_0_ERROR, header);
+ if (unlikely(error == XE_GUC_RESPONSE_VF_MIGRATED)) {
+ xe_gt_dbg(gt, "GuC mmio request %#x rejected due to MIGRATION (hint %#x)\n",
+ request[0], hint);
+ return -EREMCHG;
+ }
+
xe_gt_err(gt, "GuC mmio request %#x: failure %#x hint %#x\n",
request[0], error, hint);
return -ENXIO;
@@ -1618,18 +1627,51 @@ int xe_guc_start(struct xe_guc *guc)
return xe_guc_submit_start(guc);
}
+/**
+ * xe_guc_runtime_suspend() - GuC runtime suspend
+ * @guc: The GuC object
+ *
+ * Stop further runs of submission tasks on given GuC and runtime suspend
+ * GuC CT.
+ */
+void xe_guc_runtime_suspend(struct xe_guc *guc)
+{
+ xe_guc_submit_pause(guc);
+ xe_guc_submit_disable(guc);
+ xe_guc_ct_runtime_suspend(&guc->ct);
+}
+
+/**
+ * xe_guc_runtime_resume() - GuC runtime resume
+ * @guc: The GuC object
+ *
+ * Runtime resume GuC CT and allow further runs of submission tasks on
+ * given GuC.
+ */
+void xe_guc_runtime_resume(struct xe_guc *guc)
+{
+ /*
+ * Runtime PM flows are not applicable for VFs, so it's safe to
+ * directly enable IRQ.
+ */
+ guc_enable_irq(guc);
+
+ xe_guc_ct_runtime_resume(&guc->ct);
+ xe_guc_submit_enable(guc);
+ xe_guc_submit_unpause(guc);
+}
+
void xe_guc_print_info(struct xe_guc *guc, struct drm_printer *p)
{
struct xe_gt *gt = guc_to_gt(guc);
- unsigned int fw_ref;
u32 status;
int i;
xe_uc_fw_print(&guc->fw, p);
if (!IS_SRIOV_VF(gt_to_xe(gt))) {
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return;
status = xe_mmio_read32(&gt->mmio, GUC_STATUS);
@@ -1649,8 +1691,6 @@ void xe_guc_print_info(struct xe_guc *guc, struct drm_printer *p)
drm_printf(p, "\t%2d: \t0x%x\n",
i, xe_mmio_read32(&gt->mmio, SOFT_SCRATCH(i)));
}
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
drm_puts(p, "\n");
diff --git a/drivers/gpu/drm/xe/xe_guc.h b/drivers/gpu/drm/xe/xe_guc.h
index e2d4c5f44ae3..a169f231cbd8 100644
--- a/drivers/gpu/drm/xe/xe_guc.h
+++ b/drivers/gpu/drm/xe/xe_guc.h
@@ -18,10 +18,16 @@
*/
#define MAKE_GUC_VER(maj, min, pat) (((maj) << 16) | ((min) << 8) | (pat))
#define MAKE_GUC_VER_STRUCT(ver) MAKE_GUC_VER((ver).major, (ver).minor, (ver).patch)
+#define MAKE_GUC_VER_ARGS(ver...) \
+ (BUILD_BUG_ON_ZERO(COUNT_ARGS(ver) < 2 || COUNT_ARGS(ver) > 3) + \
+ MAKE_GUC_VER(PICK_ARG1(ver), PICK_ARG2(ver), IF_ARGS(PICK_ARG3(ver), 0, PICK_ARG3(ver))))
+
#define GUC_SUBMIT_VER(guc) \
MAKE_GUC_VER_STRUCT((guc)->fw.versions.found[XE_UC_FW_VER_COMPATIBILITY])
#define GUC_FIRMWARE_VER(guc) \
MAKE_GUC_VER_STRUCT((guc)->fw.versions.found[XE_UC_FW_VER_RELEASE])
+#define GUC_FIRMWARE_VER_AT_LEAST(guc, ver...) \
+ xe_guc_fw_version_at_least((guc), MAKE_GUC_VER_ARGS(ver))
struct drm_printer;
@@ -35,6 +41,8 @@ int xe_guc_upload(struct xe_guc *guc);
int xe_guc_min_load_for_hwconfig(struct xe_guc *guc);
int xe_guc_enable_communication(struct xe_guc *guc);
int xe_guc_opt_in_features_enable(struct xe_guc *guc);
+void xe_guc_runtime_suspend(struct xe_guc *guc);
+void xe_guc_runtime_resume(struct xe_guc *guc);
int xe_guc_suspend(struct xe_guc *guc);
void xe_guc_notify(struct xe_guc *guc);
int xe_guc_auth_huc(struct xe_guc *guc, u32 rsa_addr);
@@ -94,4 +102,19 @@ static inline struct drm_device *guc_to_drm(struct xe_guc *guc)
return &guc_to_xe(guc)->drm;
}
+/**
+ * xe_guc_fw_version_at_least() - Check if GuC is at least of given version.
+ * @guc: the &xe_guc
+ * @ver: the version to check
+ *
+ * The @ver should be prepared using MAKE_GUC_VER(major, minor, patch).
+ *
+ * Return: true if loaded GuC firmware is at least of given version,
+ * false otherwise.
+ */
+static inline bool xe_guc_fw_version_at_least(const struct xe_guc *guc, u32 ver)
+{
+ return GUC_FIRMWARE_VER(guc) >= ver;
+}
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c
index bcb85a1bf26d..f4cbc030f4c8 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.c
+++ b/drivers/gpu/drm/xe/xe_guc_ads.c
@@ -28,8 +28,6 @@
#include "xe_lrc.h"
#include "xe_map.h"
#include "xe_mmio.h"
-#include "xe_platform_types.h"
-#include "xe_uc_fw.h"
#include "xe_wa.h"
/* Slack of a few additional entries per engine */
@@ -317,7 +315,7 @@ static void guc_waklv_init(struct xe_guc_ads *ads)
offset = guc_ads_waklv_offset(ads);
remain = guc_ads_waklv_size(ads);
- if (XE_GT_WA(gt, 14019882105) || XE_GT_WA(gt, 16021333562))
+ if (XE_GT_WA(gt, 16021333562))
guc_waklv_enable(ads, NULL, 0, &offset, &remain,
GUC_WORKAROUND_KLV_BLOCK_INTERRUPTS_WHEN_MGSR_BLOCKED);
if (XE_GT_WA(gt, 18024947630))
@@ -347,10 +345,10 @@ static void guc_waklv_init(struct xe_guc_ads *ads)
guc_waklv_enable(ads, NULL, 0, &offset, &remain,
GUC_WORKAROUND_KLV_ID_BACK_TO_BACK_RCS_ENGINE_RESET);
- if (GUC_FIRMWARE_VER(&gt->uc.guc) >= MAKE_GUC_VER(70, 44, 0) && XE_GT_WA(gt, 16026508708))
+ if (GUC_FIRMWARE_VER_AT_LEAST(&gt->uc.guc, 70, 44) && XE_GT_WA(gt, 16026508708))
guc_waklv_enable(ads, NULL, 0, &offset, &remain,
GUC_WA_KLV_RESET_BB_STACK_PTR_ON_VF_SWITCH);
- if (GUC_FIRMWARE_VER(&gt->uc.guc) >= MAKE_GUC_VER(70, 47, 0) && XE_GT_WA(gt, 16026007364)) {
+ if (GUC_FIRMWARE_VER_AT_LEAST(&gt->uc.guc, 70, 47) && XE_GT_WA(gt, 16026007364)) {
u32 data[] = {
0x0,
0xF,
@@ -451,7 +449,7 @@ static void guc_policies_init(struct xe_guc_ads *ads)
ads_blob_write(ads, policies.max_num_work_items,
GLOBAL_POLICY_MAX_NUM_WI);
- if (xe->wedged.mode == 2)
+ if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)
global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET;
ads_blob_write(ads, policies.global_flags, global_flags);
@@ -983,16 +981,17 @@ static int guc_ads_action_update_policies(struct xe_guc_ads *ads, u32 policy_off
/**
* xe_guc_ads_scheduler_policy_toggle_reset - Toggle reset policy
* @ads: Additional data structures object
+ * @enable_engine_reset: true to enable engine resets, false otherwise
*
- * This function update the GuC's engine reset policy based on wedged.mode.
+ * This function update the GuC's engine reset policy.
*
* Return: 0 on success, and negative error code otherwise.
*/
-int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
+int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads,
+ bool enable_engine_reset)
{
struct guc_policies *policies;
struct xe_guc *guc = ads_to_guc(ads);
- struct xe_device *xe = ads_to_xe(ads);
CLASS(xe_guc_buf, buf)(&guc->buf, sizeof(*policies));
if (!xe_guc_buf_is_valid(buf))
@@ -1004,10 +1003,11 @@ int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
policies->dpc_promote_time = ads_blob_read(ads, policies.dpc_promote_time);
policies->max_num_work_items = ads_blob_read(ads, policies.max_num_work_items);
policies->is_valid = 1;
- if (xe->wedged.mode == 2)
- policies->global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET;
- else
+
+ if (enable_engine_reset)
policies->global_flags &= ~GLOBAL_POLICY_DISABLE_ENGINE_RESET;
+ else
+ policies->global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET;
return guc_ads_action_update_policies(ads, xe_guc_buf_flush(buf));
}
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.h b/drivers/gpu/drm/xe/xe_guc_ads.h
index 2e6674c760ff..7a39f361cb17 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.h
+++ b/drivers/gpu/drm/xe/xe_guc_ads.h
@@ -6,6 +6,8 @@
#ifndef _XE_GUC_ADS_H_
#define _XE_GUC_ADS_H_
+#include <linux/types.h>
+
struct xe_guc_ads;
int xe_guc_ads_init(struct xe_guc_ads *ads);
@@ -13,6 +15,7 @@ int xe_guc_ads_init_post_hwconfig(struct xe_guc_ads *ads);
void xe_guc_ads_populate(struct xe_guc_ads *ads);
void xe_guc_ads_populate_minimal(struct xe_guc_ads *ads);
void xe_guc_ads_populate_post_load(struct xe_guc_ads *ads);
-int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads);
+int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads,
+ bool enable_engine_reset);
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_buf.c b/drivers/gpu/drm/xe/xe_guc_buf.c
index 3ce442500130..11f77decd8d1 100644
--- a/drivers/gpu/drm/xe/xe_guc_buf.c
+++ b/drivers/gpu/drm/xe/xe_guc_buf.c
@@ -6,7 +6,6 @@
#include <linux/cleanup.h>
#include <drm/drm_managed.h>
-#include "xe_assert.h"
#include "xe_bo.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
@@ -30,7 +29,7 @@ static int guc_buf_cache_init(struct xe_guc_buf_cache *cache, u32 size)
struct xe_gt *gt = cache_to_gt(cache);
struct xe_sa_manager *sam;
- sam = __xe_sa_bo_manager_init(gt_to_tile(gt), size, 0, sizeof(u32));
+ sam = __xe_sa_bo_manager_init(gt_to_tile(gt), size, 0, sizeof(u32), 0);
if (IS_ERR(sam))
return PTR_ERR(sam);
cache->sam = sam;
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c
index 0c1fbe97b8bf..2f5816c78fba 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.c
+++ b/drivers/gpu/drm/xe/xe_guc_capture.c
@@ -13,17 +13,14 @@
#include "abi/guc_log_abi.h"
#include "regs/xe_engine_regs.h"
#include "regs/xe_gt_regs.h"
-#include "regs/xe_guc_regs.h"
-#include "regs/xe_regs.h"
-#include "xe_bo.h"
+#include "xe_bo_types.h"
#include "xe_device.h"
#include "xe_exec_queue_types.h"
#include "xe_gt.h"
#include "xe_gt_mcr.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
-#include "xe_guc_ads.h"
#include "xe_guc_capture.h"
#include "xe_guc_capture_types.h"
#include "xe_guc_ct.h"
@@ -843,7 +840,7 @@ static void check_guc_capture_size(struct xe_guc *guc)
{
int capture_size = guc_capture_output_size_est(guc);
int spare_size = capture_size * GUC_CAPTURE_OVERBUFFER_MULTIPLIER;
- u32 buffer_size = xe_guc_log_section_size_capture(&guc->log);
+ u32 buffer_size = XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE;
/*
* NOTE: capture_size is much smaller than the capture region
@@ -949,7 +946,7 @@ guc_capture_init_node(struct xe_guc *guc, struct __guc_capture_parsed_output *no
* ADS module also calls separately for PF vs VF.
*
* --> alloc B: GuC output capture buf (registered via guc_init_params(log_param))
- * Size = #define CAPTURE_BUFFER_SIZE (warns if on too-small)
+ * Size = XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE (warns if on too-small)
* Note2: 'x 3' to hold multiple capture groups
*
* GUC Runtime notify capture:
@@ -1367,7 +1364,7 @@ static int __guc_capture_flushlog_complete(struct xe_guc *guc)
{
u32 action[] = {
XE_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE,
- GUC_LOG_BUFFER_CAPTURE
+ GUC_LOG_TYPE_STATE_CAPTURE
};
return xe_guc_ct_send_g2h_handler(&guc->ct, action, ARRAY_SIZE(action));
@@ -1384,8 +1381,8 @@ static void __guc_capture_process_output(struct xe_guc *guc)
u32 log_buf_state_offset;
u32 src_data_offset;
- log_buf_state_offset = sizeof(struct guc_log_buffer_state) * GUC_LOG_BUFFER_CAPTURE;
- src_data_offset = xe_guc_get_log_buffer_offset(&guc->log, GUC_LOG_BUFFER_CAPTURE);
+ log_buf_state_offset = sizeof(struct guc_log_buffer_state) * GUC_LOG_TYPE_STATE_CAPTURE;
+ src_data_offset = XE_GUC_LOG_STATE_CAPTURE_OFFSET;
/*
* Make a copy of the state structure, inside GuC log buffer
@@ -1395,15 +1392,15 @@ static void __guc_capture_process_output(struct xe_guc *guc)
xe_map_memcpy_from(guc_to_xe(guc), &log_buf_state_local, &guc->log.bo->vmap,
log_buf_state_offset, sizeof(struct guc_log_buffer_state));
- buffer_size = xe_guc_get_log_buffer_size(&guc->log, GUC_LOG_BUFFER_CAPTURE);
+ buffer_size = XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE;
read_offset = log_buf_state_local.read_ptr;
write_offset = log_buf_state_local.sampled_write_ptr;
full_count = FIELD_GET(GUC_LOG_BUFFER_STATE_BUFFER_FULL_CNT, log_buf_state_local.flags);
/* Bookkeeping stuff */
tmp = FIELD_GET(GUC_LOG_BUFFER_STATE_FLUSH_TO_FILE, log_buf_state_local.flags);
- guc->log.stats[GUC_LOG_BUFFER_CAPTURE].flush += tmp;
- new_overflow = xe_guc_check_log_buf_overflow(&guc->log, GUC_LOG_BUFFER_CAPTURE,
+ guc->log.stats[GUC_LOG_TYPE_STATE_CAPTURE].flush += tmp;
+ new_overflow = xe_guc_check_log_buf_overflow(&guc->log, GUC_LOG_TYPE_STATE_CAPTURE,
full_count);
/* Now copy the actual logs. */
@@ -1889,7 +1886,14 @@ xe_guc_capture_get_matching_and_lock(struct xe_exec_queue *q)
return NULL;
xe = gt_to_xe(q->gt);
- if (xe->wedged.mode >= 2 || !xe_device_uc_enabled(xe) || IS_SRIOV_VF(xe))
+
+ if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)
+ return NULL;
+
+ if (!xe_device_uc_enabled(xe))
+ return NULL;
+
+ if (IS_SRIOV_VF(xe))
return NULL;
ss = &xe->devcoredump.snapshot;
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.h b/drivers/gpu/drm/xe/xe_guc_capture.h
index 20a078dc4b85..34d6fdc64f56 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.h
+++ b/drivers/gpu/drm/xe/xe_guc_capture.h
@@ -8,8 +8,8 @@
#include <linux/types.h>
#include "abi/guc_capture_abi.h"
+#include "abi/guc_scheduler_abi.h"
#include "xe_guc.h"
-#include "xe_guc_fwif.h"
struct xe_exec_queue;
struct xe_guc;
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c
index 4ac434ad216f..dfbf76037b04 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.c
+++ b/drivers/gpu/drm/xe/xe_guc_ct.c
@@ -42,6 +42,21 @@ static void ct_exit_safe_mode(struct xe_guc_ct *ct);
static void guc_ct_change_state(struct xe_guc_ct *ct,
enum xe_guc_ct_state state);
+static struct xe_guc *ct_to_guc(struct xe_guc_ct *ct)
+{
+ return container_of(ct, struct xe_guc, ct);
+}
+
+static struct xe_gt *ct_to_gt(struct xe_guc_ct *ct)
+{
+ return container_of(ct, struct xe_gt, uc.guc.ct);
+}
+
+static struct xe_device *ct_to_xe(struct xe_guc_ct *ct)
+{
+ return gt_to_xe(ct_to_gt(ct));
+}
+
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
enum {
/* Internal states, not error conditions */
@@ -68,14 +83,101 @@ enum {
static void ct_dead_worker_func(struct work_struct *w);
static void ct_dead_capture(struct xe_guc_ct *ct, struct guc_ctb *ctb, u32 reason_code);
-#define CT_DEAD(ct, ctb, reason_code) ct_dead_capture((ct), (ctb), CT_DEAD_##reason_code)
+static void ct_dead_fini(struct xe_guc_ct *ct)
+{
+ cancel_work_sync(&ct->dead.worker);
+}
+
+static void ct_dead_init(struct xe_guc_ct *ct)
+{
+ spin_lock_init(&ct->dead.lock);
+ INIT_WORK(&ct->dead.worker, ct_dead_worker_func);
+
+#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
+ stack_depot_init();
+#endif
+}
+
+static void fast_req_stack_save(struct xe_guc_ct *ct, unsigned int slot)
+{
+#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
+ unsigned long entries[SZ_32];
+ unsigned int n;
+
+ n = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
+ /* May be called under spinlock, so avoid sleeping */
+ ct->fast_req[slot].stack = stack_depot_save(entries, n, GFP_NOWAIT);
+#endif
+}
+
+static void fast_req_dump(struct xe_guc_ct *ct, u16 fence, unsigned int slot)
+{
+ struct xe_gt *gt = ct_to_gt(ct);
+#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
+ char *buf __cleanup(kfree) = kmalloc(SZ_4K, GFP_NOWAIT);
+
+ if (buf && stack_depot_snprint(ct->fast_req[slot].stack, buf, SZ_4K, 0))
+ xe_gt_err(gt, "Fence 0x%x was used by action %#04x sent at:\n%s\n",
+ fence, ct->fast_req[slot].action, buf);
+ else
+ xe_gt_err(gt, "Fence 0x%x was used by action %#04x [failed to retrieve stack]\n",
+ fence, ct->fast_req[slot].action);
+#else
+ xe_gt_err(gt, "Fence 0x%x was used by action %#04x\n",
+ fence, ct->fast_req[slot].action);
+#endif
+}
+
+static void fast_req_report(struct xe_guc_ct *ct, u16 fence)
+{
+ u16 fence_min = U16_MAX, fence_max = 0;
+ struct xe_gt *gt = ct_to_gt(ct);
+ unsigned int n;
+
+ lockdep_assert_held(&ct->lock);
+
+ for (n = 0; n < ARRAY_SIZE(ct->fast_req); n++) {
+ if (ct->fast_req[n].fence < fence_min)
+ fence_min = ct->fast_req[n].fence;
+ if (ct->fast_req[n].fence > fence_max)
+ fence_max = ct->fast_req[n].fence;
+
+ if (ct->fast_req[n].fence != fence)
+ continue;
+
+ return fast_req_dump(ct, fence, n);
+ }
+
+ xe_gt_warn(gt, "Fence 0x%x not found - tracking buffer wrapped? [range = 0x%x -> 0x%x, next = 0x%X]\n",
+ fence, fence_min, fence_max, ct->fence_seqno);
+}
+
+static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action)
+{
+ unsigned int slot = fence % ARRAY_SIZE(ct->fast_req);
+
+ fast_req_stack_save(ct, slot);
+ ct->fast_req[slot].fence = fence;
+ ct->fast_req[slot].action = action;
+}
+
+#define CT_DEAD(ct, ctb, reason_code) ct_dead_capture((ct), (ctb), CT_DEAD_##reason_code)
+
#else
+
+static void ct_dead_fini(struct xe_guc_ct *ct) { }
+static void ct_dead_init(struct xe_guc_ct *ct) { }
+
+static void fast_req_report(struct xe_guc_ct *ct, u16 fence) { }
+static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action) { }
+
#define CT_DEAD(ct, ctb, reason) \
do { \
struct guc_ctb *_ctb = (ctb); \
if (_ctb) \
_ctb->info.broken = true; \
} while (0)
+
#endif
/* Used when a CT send wants to block and / or receive data */
@@ -104,7 +206,9 @@ static void g2h_fence_cancel(struct g2h_fence *g2h_fence)
{
g2h_fence->cancel = true;
g2h_fence->fail = true;
- g2h_fence->done = true;
+
+ /* WRITE_ONCE pairs with READ_ONCEs in guc_ct_send_recv. */
+ WRITE_ONCE(g2h_fence->done, true);
}
static bool g2h_fence_needs_alloc(struct g2h_fence *g2h_fence)
@@ -112,24 +216,6 @@ static bool g2h_fence_needs_alloc(struct g2h_fence *g2h_fence)
return g2h_fence->seqno == ~0x0;
}
-static struct xe_guc *
-ct_to_guc(struct xe_guc_ct *ct)
-{
- return container_of(ct, struct xe_guc, ct);
-}
-
-static struct xe_gt *
-ct_to_gt(struct xe_guc_ct *ct)
-{
- return container_of(ct, struct xe_gt, uc.guc.ct);
-}
-
-static struct xe_device *
-ct_to_xe(struct xe_guc_ct *ct)
-{
- return gt_to_xe(ct_to_gt(ct));
-}
-
/**
* DOC: GuC CTB Blob
*
@@ -169,8 +255,11 @@ ct_to_xe(struct xe_guc_ct *ct)
#define CTB_DESC_SIZE ALIGN(sizeof(struct guc_ct_buffer_desc), SZ_2K)
#define CTB_H2G_BUFFER_OFFSET (CTB_DESC_SIZE * 2)
#define CTB_H2G_BUFFER_SIZE (SZ_4K)
+#define CTB_H2G_BUFFER_DWORDS (CTB_H2G_BUFFER_SIZE / sizeof(u32))
#define CTB_G2H_BUFFER_SIZE (SZ_128K)
+#define CTB_G2H_BUFFER_DWORDS (CTB_G2H_BUFFER_SIZE / sizeof(u32))
#define G2H_ROOM_BUFFER_SIZE (CTB_G2H_BUFFER_SIZE / 2)
+#define G2H_ROOM_BUFFER_DWORDS (CTB_G2H_BUFFER_DWORDS / 2)
/**
* xe_guc_ct_queue_proc_time_jiffies - Return maximum time to process a full
@@ -199,9 +288,7 @@ static void guc_ct_fini(struct drm_device *drm, void *arg)
{
struct xe_guc_ct *ct = arg;
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
- cancel_work_sync(&ct->dead.worker);
-#endif
+ ct_dead_fini(ct);
ct_exit_safe_mode(ct);
destroy_workqueue(ct->g2h_wq);
xa_destroy(&ct->fence_lookup);
@@ -239,13 +326,8 @@ int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct)
xa_init(&ct->fence_lookup);
INIT_WORK(&ct->g2h_worker, g2h_worker_func);
INIT_DELAYED_WORK(&ct->safe_mode_worker, safe_mode_worker_func);
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
- spin_lock_init(&ct->dead.lock);
- INIT_WORK(&ct->dead.worker, ct_dead_worker_func);
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
- stack_depot_init();
-#endif
-#endif
+
+ ct_dead_init(ct);
init_waitqueue_head(&ct->wq);
init_waitqueue_head(&ct->g2h_fence_wq);
@@ -326,7 +408,7 @@ int xe_guc_ct_init_post_hwconfig(struct xe_guc_ct *ct)
static void guc_ct_ctb_h2g_init(struct xe_device *xe, struct guc_ctb *h2g,
struct iosys_map *map)
{
- h2g->info.size = CTB_H2G_BUFFER_SIZE / sizeof(u32);
+ h2g->info.size = CTB_H2G_BUFFER_DWORDS;
h2g->info.resv_space = 0;
h2g->info.tail = 0;
h2g->info.head = 0;
@@ -344,8 +426,8 @@ static void guc_ct_ctb_h2g_init(struct xe_device *xe, struct guc_ctb *h2g,
static void guc_ct_ctb_g2h_init(struct xe_device *xe, struct guc_ctb *g2h,
struct iosys_map *map)
{
- g2h->info.size = CTB_G2H_BUFFER_SIZE / sizeof(u32);
- g2h->info.resv_space = G2H_ROOM_BUFFER_SIZE / sizeof(u32);
+ g2h->info.size = CTB_G2H_BUFFER_DWORDS;
+ g2h->info.resv_space = G2H_ROOM_BUFFER_DWORDS;
g2h->info.head = 0;
g2h->info.tail = 0;
g2h->info.space = CIRC_SPACE(g2h->info.tail, g2h->info.head,
@@ -447,7 +529,12 @@ static void guc_ct_change_state(struct xe_guc_ct *ct,
if (ct->g2h_outstanding)
xe_pm_runtime_put(ct_to_xe(ct));
ct->g2h_outstanding = 0;
- ct->state = state;
+
+ /*
+ * WRITE_ONCE pairs with READ_ONCEs in xe_guc_ct_initialized and
+ * xe_guc_ct_enabled.
+ */
+ WRITE_ONCE(ct->state, state);
xe_gt_dbg(gt, "GuC CT communication channel %s\n",
state == XE_GUC_CT_STATE_STOPPED ? "stopped" :
@@ -640,6 +727,39 @@ void xe_guc_ct_stop(struct xe_guc_ct *ct)
stop_g2h_handler(ct);
}
+/**
+ * xe_guc_ct_runtime_suspend() - GuC CT runtime suspend
+ * @ct: the &xe_guc_ct
+ *
+ * Set GuC CT to disabled state.
+ */
+void xe_guc_ct_runtime_suspend(struct xe_guc_ct *ct)
+{
+ struct guc_ctb *g2h = &ct->ctbs.g2h;
+ u32 credits = CIRC_SPACE(0, 0, CTB_G2H_BUFFER_DWORDS) - G2H_ROOM_BUFFER_DWORDS;
+
+ /* We should be back to guc_ct_ctb_g2h_init() values */
+ xe_gt_assert(ct_to_gt(ct), g2h->info.space == credits);
+
+ /*
+ * Since we're already in runtime suspend path, we shouldn't have pending
+ * messages. But if there happen to be any, we'd probably want them to be
+ * thrown as errors for further investigation.
+ */
+ xe_guc_ct_disable(ct);
+}
+
+/**
+ * xe_guc_ct_runtime_resume() - GuC CT runtime resume
+ * @ct: the &xe_guc_ct
+ *
+ * Restart GuC CT and set it to enabled state.
+ */
+void xe_guc_ct_runtime_resume(struct xe_guc_ct *ct)
+{
+ xe_guc_ct_restart(ct);
+}
+
static bool h2g_has_room(struct xe_guc_ct *ct, u32 cmd_len)
{
struct guc_ctb *h2g = &ct->ctbs.h2g;
@@ -747,28 +867,6 @@ static void g2h_release_space(struct xe_guc_ct *ct, u32 g2h_len)
spin_unlock_irq(&ct->fast_lock);
}
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
-static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action)
-{
- unsigned int slot = fence % ARRAY_SIZE(ct->fast_req);
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
- unsigned long entries[SZ_32];
- unsigned int n;
-
- n = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
-
- /* May be called under spinlock, so avoid sleeping */
- ct->fast_req[slot].stack = stack_depot_save(entries, n, GFP_NOWAIT);
-#endif
- ct->fast_req[slot].fence = fence;
- ct->fast_req[slot].action = action;
-}
-#else
-static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action)
-{
-}
-#endif
-
/*
* The CT protocol accepts a 16 bits fence. This field is fully owned by the
* driver, the GuC will just copy it to the reply message. Since we need to
@@ -1203,10 +1301,13 @@ retry_same_fence:
return ret;
}
- ret = wait_event_timeout(ct->g2h_fence_wq, g2h_fence.done, HZ);
+ /* READ_ONCEs pairs with WRITE_ONCEs in parse_g2h_response
+ * and g2h_fence_cancel.
+ */
+ ret = wait_event_timeout(ct->g2h_fence_wq, READ_ONCE(g2h_fence.done), HZ);
if (!ret) {
LNL_FLUSH_WORK(&ct->g2h_worker);
- if (g2h_fence.done) {
+ if (READ_ONCE(g2h_fence.done)) {
xe_gt_warn(gt, "G2H fence %u, action %04x, done\n",
g2h_fence.seqno, action[0]);
ret = 1;
@@ -1310,10 +1411,12 @@ static int parse_g2h_event(struct xe_guc_ct *ct, u32 *msg, u32 len)
lockdep_assert_held(&ct->lock);
switch (action) {
+ case XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE:
case XE_GUC_ACTION_SCHED_CONTEXT_MODE_DONE:
case XE_GUC_ACTION_DEREGISTER_CONTEXT_DONE:
case XE_GUC_ACTION_SCHED_ENGINE_MODE_DONE:
case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
+ case XE_GUC_ACTION_PAGE_RECLAMATION_DONE:
g2h_release_space(ct, len);
}
@@ -1338,55 +1441,6 @@ static int guc_crash_process_msg(struct xe_guc_ct *ct, u32 action)
return 0;
}
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
-static void fast_req_report(struct xe_guc_ct *ct, u16 fence)
-{
- u16 fence_min = U16_MAX, fence_max = 0;
- struct xe_gt *gt = ct_to_gt(ct);
- bool found = false;
- unsigned int n;
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
- char *buf;
-#endif
-
- lockdep_assert_held(&ct->lock);
-
- for (n = 0; n < ARRAY_SIZE(ct->fast_req); n++) {
- if (ct->fast_req[n].fence < fence_min)
- fence_min = ct->fast_req[n].fence;
- if (ct->fast_req[n].fence > fence_max)
- fence_max = ct->fast_req[n].fence;
-
- if (ct->fast_req[n].fence != fence)
- continue;
- found = true;
-
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
- buf = kmalloc(SZ_4K, GFP_NOWAIT);
- if (buf && stack_depot_snprint(ct->fast_req[n].stack, buf, SZ_4K, 0))
- xe_gt_err(gt, "Fence 0x%x was used by action %#04x sent at:\n%s",
- fence, ct->fast_req[n].action, buf);
- else
- xe_gt_err(gt, "Fence 0x%x was used by action %#04x [failed to retrieve stack]\n",
- fence, ct->fast_req[n].action);
- kfree(buf);
-#else
- xe_gt_err(gt, "Fence 0x%x was used by action %#04x\n",
- fence, ct->fast_req[n].action);
-#endif
- break;
- }
-
- if (!found)
- xe_gt_warn(gt, "Fence 0x%x not found - tracking buffer wrapped? [range = 0x%x -> 0x%x, next = 0x%X]\n",
- fence, fence_min, fence_max, ct->fence_seqno);
-}
-#else
-static void fast_req_report(struct xe_guc_ct *ct, u16 fence)
-{
-}
-#endif
-
static int parse_g2h_response(struct xe_guc_ct *ct, u32 *msg, u32 len)
{
struct xe_gt *gt = ct_to_gt(ct);
@@ -1454,7 +1508,8 @@ static int parse_g2h_response(struct xe_guc_ct *ct, u32 *msg, u32 len)
g2h_release_space(ct, GUC_CTB_HXG_MSG_MAX_LEN);
- g2h_fence->done = true;
+ /* WRITE_ONCE pairs with READ_ONCEs in guc_ct_send_recv. */
+ WRITE_ONCE(g2h_fence->done, true);
smp_mb();
wake_up_all(&ct->g2h_fence_wq);
@@ -1549,6 +1604,15 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len)
ret = xe_guc_pagefault_handler(guc, payload, adj_len);
break;
case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
+ case XE_GUC_ACTION_PAGE_RECLAMATION_DONE:
+ /*
+ * Page reclamation is an extension of TLB invalidation. Both
+ * operations share the same seqno and fence. When either
+ * action completes, we need to signal the corresponding
+ * fence. Since the handling logic (lookup fence by seqno,
+ * fence signalling) is identical, we use the same handler
+ * for both G2H events.
+ */
ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len);
break;
case XE_GUC_ACTION_GUC2PF_RELAY_FROM_VF:
@@ -1572,6 +1636,13 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len)
ret = xe_guc_g2g_test_notification(guc, payload, adj_len);
break;
#endif
+ case XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE:
+ ret = xe_guc_exec_queue_cgp_sync_done_handler(guc, payload, adj_len);
+ break;
+ case XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CGP_CONTEXT_ERROR:
+ ret = xe_guc_exec_queue_cgp_context_error_handler(guc, payload,
+ adj_len);
+ break;
default:
xe_gt_err(gt, "unexpected G2H action 0x%04x\n", action);
}
@@ -1714,6 +1785,7 @@ static int g2h_read(struct xe_guc_ct *ct, u32 *msg, bool fast_path)
switch (action) {
case XE_GUC_ACTION_REPORT_PAGE_FAULT_REQ_DESC:
case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
+ case XE_GUC_ACTION_PAGE_RECLAMATION_DONE:
break; /* Process these in fast-path */
default:
return 0;
@@ -1750,6 +1822,12 @@ static void g2h_fast_path(struct xe_guc_ct *ct, u32 *msg, u32 len)
ret = xe_guc_pagefault_handler(guc, payload, adj_len);
break;
case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
+ case XE_GUC_ACTION_PAGE_RECLAMATION_DONE:
+ /*
+ * Seqno and fence handling of page reclamation and TLB
+ * invalidation is identical, so we can use the same handler
+ * for both actions.
+ */
__g2h_release_space(ct, len);
ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len);
break;
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.h b/drivers/gpu/drm/xe/xe_guc_ct.h
index ca1ce2b3c354..767365a33dee 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.h
+++ b/drivers/gpu/drm/xe/xe_guc_ct.h
@@ -17,6 +17,8 @@ int xe_guc_ct_init_post_hwconfig(struct xe_guc_ct *ct);
int xe_guc_ct_enable(struct xe_guc_ct *ct);
int xe_guc_ct_restart(struct xe_guc_ct *ct);
void xe_guc_ct_disable(struct xe_guc_ct *ct);
+void xe_guc_ct_runtime_resume(struct xe_guc_ct *ct);
+void xe_guc_ct_runtime_suspend(struct xe_guc_ct *ct);
void xe_guc_ct_stop(struct xe_guc_ct *ct);
void xe_guc_ct_flush_and_stop(struct xe_guc_ct *ct);
void xe_guc_ct_fast_path(struct xe_guc_ct *ct);
@@ -28,12 +30,14 @@ void xe_guc_ct_print(struct xe_guc_ct *ct, struct drm_printer *p, bool want_ctb)
static inline bool xe_guc_ct_initialized(struct xe_guc_ct *ct)
{
- return ct->state != XE_GUC_CT_STATE_NOT_INITIALIZED;
+ /* READ_ONCE pairs with WRITE_ONCE in guc_ct_change_state. */
+ return READ_ONCE(ct->state) != XE_GUC_CT_STATE_NOT_INITIALIZED;
}
static inline bool xe_guc_ct_enabled(struct xe_guc_ct *ct)
{
- return ct->state == XE_GUC_CT_STATE_ENABLED;
+ /* READ_ONCE pairs with WRITE_ONCE in guc_ct_change_state. */
+ return READ_ONCE(ct->state) == XE_GUC_CT_STATE_ENABLED;
}
static inline void xe_guc_ct_irq_handler(struct xe_guc_ct *ct)
diff --git a/drivers/gpu/drm/xe/xe_guc_debugfs.c b/drivers/gpu/drm/xe/xe_guc_debugfs.c
index 0b102ab46c4d..2f23119686d4 100644
--- a/drivers/gpu/drm/xe/xe_guc_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_guc_debugfs.c
@@ -8,13 +8,12 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_managed.h>
-#include "xe_device.h"
-#include "xe_gt.h"
+#include "xe_device_types.h"
+#include "xe_gt_types.h"
#include "xe_guc.h"
#include "xe_guc_ct.h"
#include "xe_guc_log.h"
#include "xe_guc_pc.h"
-#include "xe_macros.h"
#include "xe_pm.h"
/*
@@ -70,13 +69,9 @@ static int guc_debugfs_show(struct seq_file *m, void *data)
struct xe_gt *gt = grandparent->d_inode->i_private;
struct xe_device *xe = gt_to_xe(gt);
int (*print)(struct xe_guc *, struct drm_printer *) = node->info_ent->data;
- int ret;
- xe_pm_runtime_get(xe);
- ret = print(&gt->uc.guc, &p);
- xe_pm_runtime_put(xe);
-
- return ret;
+ guard(xe_pm_runtime)(xe);
+ return print(&gt->uc.guc, &p);
}
static int guc_log(struct xe_guc *guc, struct drm_printer *p)
@@ -85,6 +80,12 @@ static int guc_log(struct xe_guc *guc, struct drm_printer *p)
return 0;
}
+static int guc_log_lfd(struct xe_guc *guc, struct drm_printer *p)
+{
+ xe_guc_log_print_lfd(&guc->log, p);
+ return 0;
+}
+
static int guc_log_dmesg(struct xe_guc *guc, struct drm_printer *p)
{
xe_guc_log_print_dmesg(&guc->log);
@@ -121,6 +122,7 @@ static const struct drm_info_list slpc_debugfs_list[] = {
/* everything else should be added here */
static const struct drm_info_list pf_only_debugfs_list[] = {
{ "guc_log", .show = guc_debugfs_show, .data = guc_log },
+ { "guc_log_lfd", .show = guc_debugfs_show, .data = guc_log_lfd },
{ "guc_log_dmesg", .show = guc_debugfs_show, .data = guc_log_dmesg },
};
diff --git a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
index a3b034e4b205..fd0915ed8eb1 100644
--- a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
@@ -33,8 +33,6 @@ struct xe_guc_exec_queue {
*/
#define MAX_STATIC_MSG_TYPE 3
struct xe_sched_msg static_msgs[MAX_STATIC_MSG_TYPE];
- /** @lr_tdr: long running TDR worker */
- struct work_struct lr_tdr;
/** @destroy_async: do final destroy async from this worker */
struct work_struct destroy_async;
/** @resume_time: time of last resume */
diff --git a/drivers/gpu/drm/xe/xe_guc_fwif.h b/drivers/gpu/drm/xe/xe_guc_fwif.h
index c90dd266e9cf..a33ea288b907 100644
--- a/drivers/gpu/drm/xe/xe_guc_fwif.h
+++ b/drivers/gpu/drm/xe/xe_guc_fwif.h
@@ -10,63 +10,15 @@
#include "abi/guc_capture_abi.h"
#include "abi/guc_klvs_abi.h"
+#include "abi/guc_scheduler_abi.h"
#include "xe_hw_engine_types.h"
#define G2H_LEN_DW_SCHED_CONTEXT_MODE_SET 4
#define G2H_LEN_DW_DEREGISTER_CONTEXT 3
#define G2H_LEN_DW_TLB_INVALIDATE 3
#define G2H_LEN_DW_G2G_NOTIFY_MIN 3
-
-#define GUC_ID_MAX 65535
-#define GUC_ID_UNKNOWN 0xffffffff
-
-#define GUC_CONTEXT_DISABLE 0
-#define GUC_CONTEXT_ENABLE 1
-
-#define GUC_CLIENT_PRIORITY_KMD_HIGH 0
-#define GUC_CLIENT_PRIORITY_HIGH 1
-#define GUC_CLIENT_PRIORITY_KMD_NORMAL 2
-#define GUC_CLIENT_PRIORITY_NORMAL 3
-#define GUC_CLIENT_PRIORITY_NUM 4
-
-#define GUC_RENDER_ENGINE 0
-#define GUC_VIDEO_ENGINE 1
-#define GUC_BLITTER_ENGINE 2
-#define GUC_VIDEOENHANCE_ENGINE 3
-#define GUC_VIDEO_ENGINE2 4
-#define GUC_MAX_ENGINES_NUM (GUC_VIDEO_ENGINE2 + 1)
-
-#define GUC_RENDER_CLASS 0
-#define GUC_VIDEO_CLASS 1
-#define GUC_VIDEOENHANCE_CLASS 2
-#define GUC_BLITTER_CLASS 3
-#define GUC_COMPUTE_CLASS 4
-#define GUC_GSC_OTHER_CLASS 5
-#define GUC_LAST_ENGINE_CLASS GUC_GSC_OTHER_CLASS
-#define GUC_MAX_ENGINE_CLASSES 16
-#define GUC_MAX_INSTANCES_PER_CLASS 32
-
-#define GUC_CONTEXT_NORMAL 0
-#define GUC_CONTEXT_COMPRESSION_SAVE 1
-#define GUC_CONTEXT_COMPRESSION_RESTORE 2
-#define GUC_CONTEXT_COUNT (GUC_CONTEXT_COMPRESSION_RESTORE + 1)
-
-/* Helper for context registration H2G */
-struct guc_ctxt_registration_info {
- u32 flags;
- u32 context_idx;
- u32 engine_class;
- u32 engine_submit_mask;
- u32 wq_desc_lo;
- u32 wq_desc_hi;
- u32 wq_base_lo;
- u32 wq_base_hi;
- u32 wq_size;
- u32 hwlrca_lo;
- u32 hwlrca_hi;
-};
-#define CONTEXT_REGISTRATION_FLAG_KMD BIT(0)
-#define CONTEXT_REGISTRATION_FLAG_TYPE GENMASK(2, 1)
+#define G2H_LEN_DW_MULTI_QUEUE_CONTEXT 3
+#define G2H_LEN_DW_PAGE_RECLAMATION 3
/* 32-bit KLV structure as used by policy updates and others */
struct guc_klv_generic_dw_t {
@@ -91,9 +43,9 @@ struct guc_update_exec_queue_policy {
#define GUC_LOG_NOTIFY_ON_HALF_FULL BIT(1)
#define GUC_LOG_CAPTURE_ALLOC_UNITS BIT(2)
#define GUC_LOG_LOG_ALLOC_UNITS BIT(3)
-#define GUC_LOG_CRASH REG_GENMASK(5, 4)
-#define GUC_LOG_DEBUG REG_GENMASK(9, 6)
-#define GUC_LOG_CAPTURE REG_GENMASK(11, 10)
+#define GUC_LOG_CRASH_DUMP REG_GENMASK(5, 4)
+#define GUC_LOG_EVENT_DATA REG_GENMASK(9, 6)
+#define GUC_LOG_STATE_CAPTURE REG_GENMASK(11, 10)
#define GUC_LOG_BUF_ADDR REG_GENMASK(31, 12)
#define GUC_CTL_WA 1
diff --git a/drivers/gpu/drm/xe/xe_guc_hwconfig.c b/drivers/gpu/drm/xe/xe_guc_hwconfig.c
index af2c817d552c..b300901dbb8e 100644
--- a/drivers/gpu/drm/xe/xe_guc_hwconfig.c
+++ b/drivers/gpu/drm/xe/xe_guc_hwconfig.c
@@ -10,8 +10,8 @@
#include "abi/guc_actions_abi.h"
#include "xe_bo.h"
-#include "xe_device.h"
-#include "xe_gt.h"
+#include "xe_device_types.h"
+#include "xe_gt_types.h"
#include "xe_guc.h"
#include "xe_map.h"
diff --git a/drivers/gpu/drm/xe/xe_guc_klv_helpers.c b/drivers/gpu/drm/xe/xe_guc_klv_helpers.c
index 146a6eda9e06..97600edda837 100644
--- a/drivers/gpu/drm/xe/xe_guc_klv_helpers.c
+++ b/drivers/gpu/drm/xe/xe_guc_klv_helpers.c
@@ -21,11 +21,16 @@
const char *xe_guc_klv_key_to_string(u16 key)
{
switch (key) {
+ /* GuC Global Config KLVs */
+ case GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE_KEY:
+ return "group_scheduling_available";
/* VGT POLICY keys */
case GUC_KLV_VGT_POLICY_SCHED_IF_IDLE_KEY:
return "sched_if_idle";
case GUC_KLV_VGT_POLICY_ADVERSE_SAMPLE_PERIOD_KEY:
return "sample_period";
+ case GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_KEY:
+ return "engine_group_config";
case GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH_KEY:
return "reset_engine";
/* VF CFG keys */
@@ -51,6 +56,10 @@ const char *xe_guc_klv_key_to_string(u16 key)
return "begin_ctx_id";
case GUC_KLV_VF_CFG_SCHED_PRIORITY_KEY:
return "sched_priority";
+ case GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY:
+ return "sched_groups_exec_quantum";
+ case GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY:
+ return "sched_groups_preempt_timeout";
/* VF CFG threshold keys */
#define define_threshold_key_to_string_case(TAG, NAME, ...) \
diff --git a/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h b/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h
index 0a028c94756d..45ab5a3b5218 100644
--- a/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h
@@ -24,6 +24,11 @@
* ABI and the associated &NAME, that may be used in code or debugfs/sysfs::
*
* define(TAG, NAME)
+ *
+ * If required, KLVs can be labeled with GuC firmware version that added them::
+ *
+ * define(TAG, NAME, MAJOR, MINOR)
+ * define(TAG, NAME, MAJOR, MINOR, PATCH)
*/
#define MAKE_XE_GUC_KLV_THRESHOLDS_SET(define) \
define(CAT_ERR, cat_error_count) \
@@ -32,6 +37,7 @@
define(H2G_STORM, guc_time_us) \
define(IRQ_STORM, irq_time_us) \
define(DOORBELL_STORM, doorbell_time_us) \
+ define(MULTI_LRC_COUNT, multi_lrc_count, 70, 53)\
/* end */
/**
diff --git a/drivers/gpu/drm/xe/xe_guc_log.c b/drivers/gpu/drm/xe/xe_guc_log.c
index c01ccb35dc75..acac66a4eca7 100644
--- a/drivers/gpu/drm/xe/xe_guc_log.c
+++ b/drivers/gpu/drm/xe/xe_guc_log.c
@@ -7,18 +7,91 @@
#include <linux/fault-inject.h>
+#include <linux/utsname.h>
#include <drm/drm_managed.h>
+#include "abi/guc_lfd_abi.h"
#include "regs/xe_guc_regs.h"
#include "xe_bo.h"
#include "xe_devcoredump.h"
#include "xe_force_wake.h"
-#include "xe_gt.h"
#include "xe_gt_printk.h"
+#include "xe_gt_types.h"
#include "xe_map.h"
#include "xe_mmio.h"
#include "xe_module.h"
+#define GUC_LOG_CHUNK_SIZE SZ_2M
+
+/* Magic keys define */
+#define GUC_LFD_DRIVER_KEY_STREAMING 0x8086AAAA474C5346
+#define GUC_LFD_LOG_BUFFER_MARKER_2 0xDEADFEED
+#define GUC_LFD_CRASH_DUMP_BUFFER_MARKER_2 0x8086DEAD
+#define GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_2 0xBEEFFEED
+#define GUC_LFD_LOG_BUFFER_MARKER_1V2 0xCABBA9E6
+#define GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_1V2 0xCABBA9F7
+#define GUC_LFD_DATA_HEADER_MAGIC 0x8086
+
+/* LFD supported LIC type range */
+#define GUC_LIC_TYPE_FIRST GUC_LIC_TYPE_GUC_SW_VERSION
+#define GUC_LIC_TYPE_LAST GUC_LIC_TYPE_BUILD_PLATFORM_ID
+#define GUC_LFD_TYPE_FW_RANGE_FIRST GUC_LFD_TYPE_FW_VERSION
+#define GUC_LFD_TYPE_FW_RANGE_LAST GUC_LFD_TYPE_BUILD_PLATFORM_ID
+
+#define GUC_LOG_BUFFER_STATE_HEADER_LENGTH 4096
+#define GUC_LOG_BUFFER_INIT_CONFIG 3
+
+struct guc_log_buffer_entry_list {
+ u32 offset;
+ u32 rd_ptr;
+ u32 wr_ptr;
+ u32 wrap_offset;
+ u32 buf_size;
+};
+
+struct guc_lic_save {
+ u32 version;
+ /*
+ * Array of init config KLV values.
+ * Range from GUC_LOG_LIC_TYPE_FIRST to GUC_LOG_LIC_TYPE_LAST
+ */
+ u32 values[GUC_LIC_TYPE_LAST - GUC_LIC_TYPE_FIRST + 1];
+ struct guc_log_buffer_entry_list entry[GUC_LOG_BUFFER_INIT_CONFIG];
+};
+
+static struct guc_log_buffer_entry_markers {
+ u32 key[2];
+} const entry_markers[GUC_LOG_BUFFER_INIT_CONFIG + 1] = {
+ {{
+ GUC_LFD_LOG_BUFFER_MARKER_1V2,
+ GUC_LFD_LOG_BUFFER_MARKER_2
+ }},
+ {{
+ GUC_LFD_LOG_BUFFER_MARKER_1V2,
+ GUC_LFD_CRASH_DUMP_BUFFER_MARKER_2
+ }},
+ {{
+ GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_1V2,
+ GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_2
+ }},
+ {{
+ GUC_LIC_MAGIC,
+ (FIELD_PREP_CONST(GUC_LIC_VERSION_MASK_MAJOR, GUC_LIC_VERSION_MAJOR) |
+ FIELD_PREP_CONST(GUC_LIC_VERSION_MASK_MINOR, GUC_LIC_VERSION_MINOR))
+ }}
+};
+
+static struct guc_log_lic_lfd_map {
+ u32 lic;
+ u32 lfd;
+} const lic_lfd_type_map[] = {
+ {GUC_LIC_TYPE_GUC_SW_VERSION, GUC_LFD_TYPE_FW_VERSION},
+ {GUC_LIC_TYPE_GUC_DEVICE_ID, GUC_LFD_TYPE_GUC_DEVICE_ID},
+ {GUC_LIC_TYPE_TSC_FREQUENCY, GUC_LFD_TYPE_TSC_FREQUENCY},
+ {GUC_LIC_TYPE_GMD_ID, GUC_LFD_TYPE_GMD_ID},
+ {GUC_LIC_TYPE_BUILD_PLATFORM_ID, GUC_LFD_TYPE_BUILD_PLATFORM_ID}
+};
+
static struct xe_guc *
log_to_guc(struct xe_guc_log *log)
{
@@ -37,33 +110,6 @@ log_to_xe(struct xe_guc_log *log)
return gt_to_xe(log_to_gt(log));
}
-static size_t guc_log_size(void)
-{
- /*
- * GuC Log buffer Layout
- *
- * +===============================+ 00B
- * | Crash dump state header |
- * +-------------------------------+ 32B
- * | Debug state header |
- * +-------------------------------+ 64B
- * | Capture state header |
- * +-------------------------------+ 96B
- * | |
- * +===============================+ PAGE_SIZE (4KB)
- * | Crash Dump logs |
- * +===============================+ + CRASH_SIZE
- * | Debug logs |
- * +===============================+ + DEBUG_SIZE
- * | Capture logs |
- * +===============================+ + CAPTURE_SIZE
- */
- return PAGE_SIZE + CRASH_BUFFER_SIZE + DEBUG_BUFFER_SIZE +
- CAPTURE_BUFFER_SIZE;
-}
-
-#define GUC_LOG_CHUNK_SIZE SZ_2M
-
static struct xe_guc_log_snapshot *xe_guc_log_snapshot_alloc(struct xe_guc_log *log, bool atomic)
{
struct xe_guc_log_snapshot *snapshot;
@@ -145,7 +191,6 @@ struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log,
struct xe_device *xe = log_to_xe(log);
struct xe_guc *guc = log_to_guc(log);
struct xe_gt *gt = log_to_gt(log);
- unsigned int fw_ref;
size_t remain;
int i;
@@ -165,13 +210,12 @@ struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log,
remain -= size;
}
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref) {
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
snapshot->stamp = ~0ULL;
- } else {
+ else
snapshot->stamp = xe_mmio_read64_2x32(&gt->mmio, GUC_PMTIMESTAMP_LO);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
- }
+
snapshot->ktime = ktime_get_boottime_ns();
snapshot->level = log->level;
snapshot->ver_found = guc->fw.versions.found[XE_UC_FW_VER_RELEASE];
@@ -216,6 +260,318 @@ void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_
}
}
+static inline void lfd_output_binary(struct drm_printer *p, char *buf, int buf_size)
+{
+ seq_write(p->arg, buf, buf_size);
+}
+
+static inline int xe_guc_log_add_lfd_header(struct guc_lfd_data *lfd)
+{
+ lfd->header = FIELD_PREP_CONST(GUC_LFD_DATA_HEADER_MASK_MAGIC, GUC_LFD_DATA_HEADER_MAGIC);
+ return offsetof(struct guc_lfd_data, data);
+}
+
+static int xe_guc_log_add_typed_payload(struct drm_printer *p, u32 type,
+ u32 data_len, void *data)
+{
+ struct guc_lfd_data lfd;
+ int len;
+
+ len = xe_guc_log_add_lfd_header(&lfd);
+ lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, type);
+ /* make length DW aligned */
+ lfd.data_count = DIV_ROUND_UP(data_len, sizeof(u32));
+ lfd_output_binary(p, (char *)&lfd, len);
+
+ lfd_output_binary(p, data, data_len);
+ len += lfd.data_count * sizeof(u32);
+
+ return len;
+}
+
+static inline int lic_type_to_index(u32 lic_type)
+{
+ XE_WARN_ON(lic_type < GUC_LIC_TYPE_FIRST || lic_type > GUC_LIC_TYPE_LAST);
+
+ return lic_type - GUC_LIC_TYPE_FIRST;
+}
+
+static inline int lfd_type_to_index(u32 lfd_type)
+{
+ int i, lic_type = 0;
+
+ XE_WARN_ON(lfd_type < GUC_LFD_TYPE_FW_RANGE_FIRST || lfd_type > GUC_LFD_TYPE_FW_RANGE_LAST);
+
+ for (i = 0; i < ARRAY_SIZE(lic_lfd_type_map); i++)
+ if (lic_lfd_type_map[i].lfd == lfd_type)
+ lic_type = lic_lfd_type_map[i].lic;
+
+ /* If not found, lic_type_to_index will warning invalid type */
+ return lic_type_to_index(lic_type);
+}
+
+static int xe_guc_log_add_klv(struct drm_printer *p, u32 lfd_type,
+ struct guc_lic_save *config)
+{
+ int klv_index = lfd_type_to_index(lfd_type);
+
+ return xe_guc_log_add_typed_payload(p, lfd_type, sizeof(u32), &config->values[klv_index]);
+}
+
+static int xe_guc_log_add_os_id(struct drm_printer *p, u32 id)
+{
+ struct guc_lfd_data_os_info os_id;
+ struct guc_lfd_data lfd;
+ int len, info_len, section_len;
+ char *version;
+ u32 blank = 0;
+
+ len = xe_guc_log_add_lfd_header(&lfd);
+ lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, GUC_LFD_TYPE_OS_ID);
+
+ os_id.os_id = id;
+ section_len = offsetof(struct guc_lfd_data_os_info, build_version);
+
+ version = init_utsname()->release;
+ info_len = strlen(version);
+
+ /* make length DW aligned */
+ lfd.data_count = DIV_ROUND_UP(section_len + info_len, sizeof(u32));
+ lfd_output_binary(p, (char *)&lfd, len);
+ lfd_output_binary(p, (char *)&os_id, section_len);
+ lfd_output_binary(p, version, info_len);
+
+ /* Padding with 0 */
+ section_len = lfd.data_count * sizeof(u32) - section_len - info_len;
+ if (section_len)
+ lfd_output_binary(p, (char *)&blank, section_len);
+
+ len += lfd.data_count * sizeof(u32);
+ return len;
+}
+
+static void xe_guc_log_loop_log_init(struct guc_lic *init, struct guc_lic_save *config)
+{
+ struct guc_klv_generic_dw_t *p = (void *)init->data;
+ int i;
+
+ for (i = 0; i < init->data_count;) {
+ int klv_len = FIELD_GET(GUC_KLV_0_LEN, p->kl) + 1;
+ int key = FIELD_GET(GUC_KLV_0_KEY, p->kl);
+
+ if (key < GUC_LIC_TYPE_FIRST || key > GUC_LIC_TYPE_LAST) {
+ XE_WARN_ON(key < GUC_LIC_TYPE_FIRST || key > GUC_LIC_TYPE_LAST);
+ break;
+ }
+ config->values[lic_type_to_index(key)] = p->value;
+ i += klv_len + 1; /* Whole KLV structure length in dwords */
+ p = (void *)((u32 *)p + klv_len);
+ }
+}
+
+static int find_marker(u32 mark0, u32 mark1)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(entry_markers); i++)
+ if (mark0 == entry_markers[i].key[0] && mark1 == entry_markers[i].key[1])
+ return i;
+
+ return ARRAY_SIZE(entry_markers);
+}
+
+static void xe_guc_log_load_lic(void *guc_log, struct guc_lic_save *config)
+{
+ u32 offset = GUC_LOG_BUFFER_STATE_HEADER_LENGTH;
+ struct guc_log_buffer_state *p = guc_log;
+
+ config->version = p->version;
+ while (p->marker[0]) {
+ int index;
+
+ index = find_marker(p->marker[0], p->marker[1]);
+
+ if (index < ARRAY_SIZE(entry_markers)) {
+ if (index == GUC_LOG_BUFFER_INIT_CONFIG) {
+ /* Load log init config */
+ xe_guc_log_loop_log_init((void *)p, config);
+
+ /* LIC structure is the last */
+ return;
+ }
+ config->entry[index].offset = offset;
+ config->entry[index].rd_ptr = p->read_ptr;
+ config->entry[index].wr_ptr = p->write_ptr;
+ config->entry[index].wrap_offset = p->wrap_offset;
+ config->entry[index].buf_size = p->size;
+ }
+ offset += p->size;
+ p++;
+ }
+}
+
+static int
+xe_guc_log_output_lfd_init(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot,
+ struct guc_lic_save *config)
+{
+ int type, len;
+ size_t size = 0;
+
+ /* FW required types */
+ for (type = GUC_LFD_TYPE_FW_RANGE_FIRST; type <= GUC_LFD_TYPE_FW_RANGE_LAST; type++)
+ size += xe_guc_log_add_klv(p, type, config);
+
+ /* KMD required type(s) */
+ len = xe_guc_log_add_os_id(p, GUC_LFD_OS_TYPE_OSID_LIN);
+ size += len;
+
+ return size;
+}
+
+static void
+xe_guc_log_print_chunks(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot,
+ u32 from, u32 to)
+{
+ int chunk_from = from % GUC_LOG_CHUNK_SIZE;
+ int chunk_id = from / GUC_LOG_CHUNK_SIZE;
+ int to_chunk_id = to / GUC_LOG_CHUNK_SIZE;
+ int chunk_to = to % GUC_LOG_CHUNK_SIZE;
+ int pos = from;
+
+ do {
+ size_t size = (to_chunk_id == chunk_id ? chunk_to : GUC_LOG_CHUNK_SIZE) -
+ chunk_from;
+
+ lfd_output_binary(p, snapshot->copy[chunk_id] + chunk_from, size);
+ pos += size;
+ chunk_id++;
+ chunk_from = 0;
+ } while (pos < to);
+}
+
+static inline int
+xe_guc_log_add_log_event(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot,
+ struct guc_lic_save *config)
+{
+ size_t size;
+ u32 data_len, section_len;
+ struct guc_lfd_data lfd;
+ struct guc_log_buffer_entry_list *entry;
+ struct guc_lfd_data_log_events_buf events_buf;
+
+ entry = &config->entry[GUC_LOG_TYPE_EVENT_DATA];
+
+ /* Skip empty log */
+ if (entry->rd_ptr == entry->wr_ptr)
+ return 0;
+
+ size = xe_guc_log_add_lfd_header(&lfd);
+ lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, GUC_LFD_TYPE_LOG_EVENTS_BUFFER);
+ events_buf.log_events_format_version = config->version;
+
+ /* Adjust to log_format_buf */
+ section_len = offsetof(struct guc_lfd_data_log_events_buf, log_event);
+ data_len = section_len;
+
+ /* Calculate data length */
+ data_len += entry->rd_ptr < entry->wr_ptr ? (entry->wr_ptr - entry->rd_ptr) :
+ (entry->wr_ptr + entry->wrap_offset - entry->rd_ptr);
+ /* make length u32 aligned */
+ lfd.data_count = DIV_ROUND_UP(data_len, sizeof(u32));
+
+ /* Output GUC_LFD_TYPE_LOG_EVENTS_BUFFER header */
+ lfd_output_binary(p, (char *)&lfd, size);
+ lfd_output_binary(p, (char *)&events_buf, section_len);
+
+ /* Output data from guc log chunks directly */
+ if (entry->rd_ptr < entry->wr_ptr) {
+ xe_guc_log_print_chunks(p, snapshot, entry->offset + entry->rd_ptr,
+ entry->offset + entry->wr_ptr);
+ } else {
+ /* 1st, print from rd to wrap offset */
+ xe_guc_log_print_chunks(p, snapshot, entry->offset + entry->rd_ptr,
+ entry->offset + entry->wrap_offset);
+
+ /* 2nd, print from buf start to wr */
+ xe_guc_log_print_chunks(p, snapshot, entry->offset, entry->offset + entry->wr_ptr);
+ }
+ return size;
+}
+
+static int
+xe_guc_log_add_crash_dump(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot,
+ struct guc_lic_save *config)
+{
+ struct guc_log_buffer_entry_list *entry;
+ int chunk_from, chunk_id;
+ int from, to, i;
+ size_t size = 0;
+ u32 *buf32;
+
+ entry = &config->entry[GUC_LOG_TYPE_CRASH_DUMP];
+
+ /* Skip zero sized crash dump */
+ if (!entry->buf_size)
+ return 0;
+
+ /* Check if crash dump section are all zero */
+ from = entry->offset;
+ to = entry->offset + entry->buf_size;
+ chunk_from = from % GUC_LOG_CHUNK_SIZE;
+ chunk_id = from / GUC_LOG_CHUNK_SIZE;
+ buf32 = snapshot->copy[chunk_id] + chunk_from;
+
+ for (i = 0; i < entry->buf_size / sizeof(u32); i++)
+ if (buf32[i])
+ break;
+
+ /* Buffer has non-zero data? */
+ if (i < entry->buf_size / sizeof(u32)) {
+ struct guc_lfd_data lfd;
+
+ size = xe_guc_log_add_lfd_header(&lfd);
+ lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, GUC_LFD_TYPE_FW_CRASH_DUMP);
+ /* Calculate data length */
+ lfd.data_count = DIV_ROUND_UP(entry->buf_size, sizeof(u32));
+ /* Output GUC_LFD_TYPE_FW_CRASH_DUMP header */
+ lfd_output_binary(p, (char *)&lfd, size);
+
+ /* rd/wr ptr is not used for crash dump */
+ xe_guc_log_print_chunks(p, snapshot, from, to);
+ }
+ return size;
+}
+
+static void
+xe_guc_log_snapshot_print_lfd(struct xe_guc_log_snapshot *snapshot, struct drm_printer *p)
+{
+ struct guc_lfd_file_header header;
+ struct guc_lic_save config;
+ size_t size;
+
+ if (!snapshot || !snapshot->size)
+ return;
+
+ header.magic = GUC_LFD_DRIVER_KEY_STREAMING;
+ header.version = FIELD_PREP_CONST(GUC_LFD_FILE_HEADER_VERSION_MASK_MINOR,
+ GUC_LFD_FORMAT_VERSION_MINOR) |
+ FIELD_PREP_CONST(GUC_LFD_FILE_HEADER_VERSION_MASK_MAJOR,
+ GUC_LFD_FORMAT_VERSION_MAJOR);
+
+ /* Output LFD file header */
+ lfd_output_binary(p, (char *)&header,
+ offsetof(struct guc_lfd_file_header, stream));
+
+ /* Output LFD stream */
+ xe_guc_log_load_lic(snapshot->copy[0], &config);
+ size = xe_guc_log_output_lfd_init(p, snapshot, &config);
+ if (!size)
+ return;
+
+ xe_guc_log_add_log_event(p, snapshot, &config);
+ xe_guc_log_add_crash_dump(p, snapshot, &config);
+}
+
/**
* xe_guc_log_print_dmesg - dump a copy of the GuC log to dmesg
* @log: GuC log structure
@@ -251,13 +607,27 @@ void xe_guc_log_print(struct xe_guc_log *log, struct drm_printer *p)
xe_guc_log_snapshot_free(snapshot);
}
+/**
+ * xe_guc_log_print_lfd - dump a copy of the GuC log in LFD format
+ * @log: GuC log structure
+ * @p: the printer object to output to
+ */
+void xe_guc_log_print_lfd(struct xe_guc_log *log, struct drm_printer *p)
+{
+ struct xe_guc_log_snapshot *snapshot;
+
+ snapshot = xe_guc_log_snapshot_capture(log, false);
+ xe_guc_log_snapshot_print_lfd(snapshot, p);
+ xe_guc_log_snapshot_free(snapshot);
+}
+
int xe_guc_log_init(struct xe_guc_log *log)
{
struct xe_device *xe = log_to_xe(log);
struct xe_tile *tile = gt_to_tile(log_to_gt(log));
struct xe_bo *bo;
- bo = xe_managed_bo_create_pin_map(xe, tile, guc_log_size(),
+ bo = xe_managed_bo_create_pin_map(xe, tile, GUC_LOG_SIZE,
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE |
@@ -265,7 +635,7 @@ int xe_guc_log_init(struct xe_guc_log *log)
if (IS_ERR(bo))
return PTR_ERR(bo);
- xe_map_memset(xe, &bo->vmap, 0, 0, guc_log_size());
+ xe_map_memset(xe, &bo->vmap, 0, 0, xe_bo_size(bo));
log->bo = bo;
log->level = xe_modparam.guc_log_level;
@@ -274,71 +644,6 @@ int xe_guc_log_init(struct xe_guc_log *log)
ALLOW_ERROR_INJECTION(xe_guc_log_init, ERRNO); /* See xe_pci_probe() */
-static u32 xe_guc_log_section_size_crash(struct xe_guc_log *log)
-{
- return CRASH_BUFFER_SIZE;
-}
-
-static u32 xe_guc_log_section_size_debug(struct xe_guc_log *log)
-{
- return DEBUG_BUFFER_SIZE;
-}
-
-/**
- * xe_guc_log_section_size_capture - Get capture buffer size within log sections.
- * @log: The log object.
- *
- * This function will return the capture buffer size within log sections.
- *
- * Return: capture buffer size.
- */
-u32 xe_guc_log_section_size_capture(struct xe_guc_log *log)
-{
- return CAPTURE_BUFFER_SIZE;
-}
-
-/**
- * xe_guc_get_log_buffer_size - Get log buffer size for a type.
- * @log: The log object.
- * @type: The log buffer type
- *
- * Return: buffer size.
- */
-u32 xe_guc_get_log_buffer_size(struct xe_guc_log *log, enum guc_log_buffer_type type)
-{
- switch (type) {
- case GUC_LOG_BUFFER_CRASH_DUMP:
- return xe_guc_log_section_size_crash(log);
- case GUC_LOG_BUFFER_DEBUG:
- return xe_guc_log_section_size_debug(log);
- case GUC_LOG_BUFFER_CAPTURE:
- return xe_guc_log_section_size_capture(log);
- }
- return 0;
-}
-
-/**
- * xe_guc_get_log_buffer_offset - Get offset in log buffer for a type.
- * @log: The log object.
- * @type: The log buffer type
- *
- * This function will return the offset in the log buffer for a type.
- * Return: buffer offset.
- */
-u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_type type)
-{
- enum guc_log_buffer_type i;
- u32 offset = PAGE_SIZE;/* for the log_buffer_states */
-
- for (i = GUC_LOG_BUFFER_CRASH_DUMP; i < GUC_LOG_BUFFER_TYPE_MAX; ++i) {
- if (i == type)
- break;
- offset += xe_guc_get_log_buffer_size(log, i);
- }
-
- return offset;
-}
-
/**
* xe_guc_check_log_buf_overflow - Check if log buffer overflowed
* @log: The log object.
@@ -352,7 +657,7 @@ u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_typ
*
* Return: True if overflowed.
*/
-bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log, enum guc_log_buffer_type type,
+bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log, enum guc_log_type type,
unsigned int full_cnt)
{
unsigned int prev_full_cnt = log->stats[type].sampled_overflow;
diff --git a/drivers/gpu/drm/xe/xe_guc_log.h b/drivers/gpu/drm/xe/xe_guc_log.h
index 98a47ac42b08..1b05bb60c1c7 100644
--- a/drivers/gpu/drm/xe/xe_guc_log.h
+++ b/drivers/gpu/drm/xe/xe_guc_log.h
@@ -13,14 +13,26 @@ struct drm_printer;
struct xe_device;
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
-#define CRASH_BUFFER_SIZE SZ_1M
-#define DEBUG_BUFFER_SIZE SZ_8M
-#define CAPTURE_BUFFER_SIZE SZ_2M
+#define XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE SZ_8M
+#define XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE SZ_1M
+#define XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE SZ_2M
#else
-#define CRASH_BUFFER_SIZE SZ_16K
-#define DEBUG_BUFFER_SIZE SZ_64K
-#define CAPTURE_BUFFER_SIZE SZ_1M
+#define XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE SZ_64K
+#define XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE SZ_16K
+#define XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE SZ_1M
#endif
+
+#define GUC_LOG_SIZE (SZ_4K + \
+ XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE + \
+ XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE + \
+ XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE)
+
+#define XE_GUC_LOG_EVENT_DATA_OFFSET SZ_4K
+#define XE_GUC_LOG_CRASH_DUMP_OFFSET (XE_GUC_LOG_EVENT_DATA_OFFSET + \
+ XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE)
+#define XE_GUC_LOG_STATE_CAPTURE_OFFSET (XE_GUC_LOG_CRASH_DUMP_OFFSET + \
+ XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE)
+
/*
* While we're using plain log level in i915, GuC controls are much more...
* "elaborate"? We have a couple of bits for verbosity, separate bit for actual
@@ -40,6 +52,7 @@ struct xe_device;
int xe_guc_log_init(struct xe_guc_log *log);
void xe_guc_log_print(struct xe_guc_log *log, struct drm_printer *p);
+void xe_guc_log_print_lfd(struct xe_guc_log *log, struct drm_printer *p);
void xe_guc_log_print_dmesg(struct xe_guc_log *log);
struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log, bool atomic);
void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_printer *p);
@@ -51,11 +64,8 @@ xe_guc_log_get_level(struct xe_guc_log *log)
return log->level;
}
-u32 xe_guc_log_section_size_capture(struct xe_guc_log *log);
-u32 xe_guc_get_log_buffer_size(struct xe_guc_log *log, enum guc_log_buffer_type type);
-u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_type type);
bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log,
- enum guc_log_buffer_type type,
+ enum guc_log_type type,
unsigned int full_cnt);
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c
index 951a49fb1d3e..5e5495a39a3c 100644
--- a/drivers/gpu/drm/xe/xe_guc_pc.c
+++ b/drivers/gpu/drm/xe/xe_guc_pc.c
@@ -76,7 +76,7 @@
* exposes a programming interface to the host for the control of SLPC.
*
* Frequency management:
- * =====================
+ * ---------------------
*
* Xe driver enables SLPC with all of its defaults features and frequency
* selection, which varies per platform.
@@ -87,7 +87,7 @@
* for any workload.
*
* Render-C States:
- * ================
+ * ----------------
*
* Render-C states is also a GuC PC feature that is now enabled in Xe for
* all platforms.
@@ -499,21 +499,17 @@ u32 xe_guc_pc_get_cur_freq_fw(struct xe_guc_pc *pc)
int xe_guc_pc_get_cur_freq(struct xe_guc_pc *pc, u32 *freq)
{
struct xe_gt *gt = pc_to_gt(pc);
- unsigned int fw_ref;
/*
* GuC SLPC plays with cur freq request when GuCRC is enabled
* Block RC6 for a more reliable read.
*/
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT)) {
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT))
return -ETIMEDOUT;
- }
*freq = get_cur_freq(gt);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
return 0;
}
@@ -1087,13 +1083,8 @@ int xe_guc_pc_gucrc_disable(struct xe_guc_pc *pc)
*/
int xe_guc_pc_override_gucrc_mode(struct xe_guc_pc *pc, enum slpc_gucrc_mode mode)
{
- int ret;
-
- xe_pm_runtime_get(pc_to_xe(pc));
- ret = pc_action_set_param(pc, SLPC_PARAM_PWRGATE_RC_MODE, mode);
- xe_pm_runtime_put(pc_to_xe(pc));
-
- return ret;
+ guard(xe_pm_runtime)(pc_to_xe(pc));
+ return pc_action_set_param(pc, SLPC_PARAM_PWRGATE_RC_MODE, mode);
}
/**
@@ -1104,13 +1095,8 @@ int xe_guc_pc_override_gucrc_mode(struct xe_guc_pc *pc, enum slpc_gucrc_mode mod
*/
int xe_guc_pc_unset_gucrc_mode(struct xe_guc_pc *pc)
{
- int ret;
-
- xe_pm_runtime_get(pc_to_xe(pc));
- ret = pc_action_unset_param(pc, SLPC_PARAM_PWRGATE_RC_MODE);
- xe_pm_runtime_put(pc_to_xe(pc));
-
- return ret;
+ guard(xe_pm_runtime)(pc_to_xe(pc));
+ return pc_action_unset_param(pc, SLPC_PARAM_PWRGATE_RC_MODE);
}
static void pc_init_pcode_freq(struct xe_guc_pc *pc)
@@ -1198,7 +1184,7 @@ int xe_guc_pc_set_power_profile(struct xe_guc_pc *pc, const char *buf)
return -EINVAL;
guard(mutex)(&pc->freq_lock);
- xe_pm_runtime_get_noresume(pc_to_xe(pc));
+ guard(xe_pm_runtime_noresume)(pc_to_xe(pc));
ret = pc_action_set_param(pc,
SLPC_PARAM_POWER_PROFILE,
@@ -1209,7 +1195,35 @@ int xe_guc_pc_set_power_profile(struct xe_guc_pc *pc, const char *buf)
else
pc->power_profile = val;
- xe_pm_runtime_put(pc_to_xe(pc));
+ return ret;
+}
+
+static int pc_action_set_dcc(struct xe_guc_pc *pc, bool enable)
+{
+ int ret;
+
+ ret = pc_action_set_param(pc,
+ SLPC_PARAM_TASK_ENABLE_DCC,
+ enable);
+ if (!ret)
+ return pc_action_set_param(pc,
+ SLPC_PARAM_TASK_DISABLE_DCC,
+ !enable);
+ else
+ return ret;
+}
+
+static int pc_modify_defaults(struct xe_guc_pc *pc)
+{
+ struct xe_device *xe = pc_to_xe(pc);
+ struct xe_gt *gt = pc_to_gt(pc);
+ int ret = 0;
+
+ if (xe->info.platform == XE_PANTHERLAKE) {
+ ret = pc_action_set_dcc(pc, false);
+ if (unlikely(ret))
+ xe_gt_err(gt, "Failed to modify DCC default: %pe\n", ERR_PTR(ret));
+ }
return ret;
}
@@ -1223,17 +1237,14 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
struct xe_device *xe = pc_to_xe(pc);
struct xe_gt *gt = pc_to_gt(pc);
u32 size = PAGE_ALIGN(sizeof(struct slpc_shared_data));
- unsigned int fw_ref;
ktime_t earlier;
int ret;
xe_gt_assert(gt, xe_device_uc_enabled(xe));
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT)) {
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT))
return -ETIMEDOUT;
- }
if (xe->info.skip_guc_pc) {
if (xe->info.platform != XE_PVC)
@@ -1241,9 +1252,7 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
/* Request max possible since dynamic freq mgmt is not enabled */
pc_set_cur_freq(pc, UINT_MAX);
-
- ret = 0;
- goto out;
+ return 0;
}
xe_map_memset(xe, &pc->bo->vmap, 0, 0, size);
@@ -1252,7 +1261,7 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
earlier = ktime_get();
ret = pc_action_reset(pc);
if (ret)
- goto out;
+ return ret;
if (wait_for_pc_state(pc, SLPC_GLOBAL_STATE_RUNNING,
SLPC_RESET_TIMEOUT_MS)) {
@@ -1263,31 +1272,33 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
if (wait_for_pc_state(pc, SLPC_GLOBAL_STATE_RUNNING,
SLPC_RESET_EXTENDED_TIMEOUT_MS)) {
xe_gt_err(gt, "GuC PC Start failed: Dynamic GT frequency control and GT sleep states are now disabled.\n");
- ret = -EIO;
- goto out;
+ return -EIO;
}
xe_gt_warn(gt, "GuC PC excessive start time: %lldms",
ktime_ms_delta(ktime_get(), earlier));
}
+ ret = pc_modify_defaults(pc);
+ if (ret)
+ return ret;
+
ret = pc_init_freqs(pc);
if (ret)
- goto out;
+ return ret;
ret = pc_set_mert_freq_cap(pc);
if (ret)
- goto out;
+ return ret;
if (xe->info.platform == XE_PVC) {
xe_guc_pc_gucrc_disable(pc);
- ret = 0;
- goto out;
+ return 0;
}
ret = pc_action_setup_gucrc(pc, GUCRC_FIRMWARE_CONTROL);
if (ret)
- goto out;
+ return ret;
/* Enable SLPC Optimized Strategy for compute */
ret = pc_action_set_strategy(pc, SLPC_OPTIMIZED_STRATEGY_COMPUTE);
@@ -1297,8 +1308,6 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
if (unlikely(ret))
xe_gt_err(gt, "Failed to set SLPC power profile: %pe\n", ERR_PTR(ret));
-out:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
return ret;
}
@@ -1330,19 +1339,16 @@ static void xe_guc_pc_fini_hw(void *arg)
{
struct xe_guc_pc *pc = arg;
struct xe_device *xe = pc_to_xe(pc);
- unsigned int fw_ref;
if (xe_device_wedged(xe))
return;
- fw_ref = xe_force_wake_get(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL);
xe_guc_pc_gucrc_disable(pc);
XE_WARN_ON(xe_guc_pc_stop(pc));
/* Bind requested freq to mert_freq_cap before unload */
pc_set_cur_freq(pc, min(pc_max_freq_cap(pc), xe_guc_pc_get_rpe_freq(pc)));
-
- xe_force_wake_put(gt_to_fw(pc_to_gt(pc)), fw_ref);
}
/**
diff --git a/drivers/gpu/drm/xe/xe_guc_relay.c b/drivers/gpu/drm/xe/xe_guc_relay.c
index 0c0ff24ba62a..577a315854af 100644
--- a/drivers/gpu/drm/xe/xe_guc_relay.c
+++ b/drivers/gpu/drm/xe/xe_guc_relay.c
@@ -17,8 +17,7 @@
#include "abi/guc_relay_communication_abi.h"
#include "xe_assert.h"
-#include "xe_device.h"
-#include "xe_gt.h"
+#include "xe_device_types.h"
#include "xe_gt_sriov_printk.h"
#include "xe_gt_sriov_pf_service.h"
#include "xe_guc.h"
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index ed7be50b2f72..1b2f66f4425b 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -17,8 +17,8 @@
#include "abi/guc_actions_abi.h"
#include "abi/guc_actions_slpc_abi.h"
#include "abi/guc_klvs_abi.h"
-#include "regs/xe_lrc_layout.h"
#include "xe_assert.h"
+#include "xe_bo.h"
#include "xe_devcoredump.h"
#include "xe_device.h"
#include "xe_exec_queue.h"
@@ -35,7 +35,6 @@
#include "xe_guc_klv_helpers.h"
#include "xe_guc_submit_types.h"
#include "xe_hw_engine.h"
-#include "xe_hw_fence.h"
#include "xe_lrc.h"
#include "xe_macros.h"
#include "xe_map.h"
@@ -47,6 +46,8 @@
#include "xe_uc_fw.h"
#include "xe_vm.h"
+#define XE_GUC_EXEC_QUEUE_CGP_CONTEXT_ERROR_LEN 6
+
static struct xe_guc *
exec_queue_to_guc(struct xe_exec_queue *q)
{
@@ -68,10 +69,8 @@ exec_queue_to_guc(struct xe_exec_queue *q)
#define EXEC_QUEUE_STATE_KILLED (1 << 7)
#define EXEC_QUEUE_STATE_WEDGED (1 << 8)
#define EXEC_QUEUE_STATE_BANNED (1 << 9)
-#define EXEC_QUEUE_STATE_CHECK_TIMEOUT (1 << 10)
-#define EXEC_QUEUE_STATE_EXTRA_REF (1 << 11)
-#define EXEC_QUEUE_STATE_PENDING_RESUME (1 << 12)
-#define EXEC_QUEUE_STATE_PENDING_TDR_EXIT (1 << 13)
+#define EXEC_QUEUE_STATE_PENDING_RESUME (1 << 10)
+#define EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND (1 << 11)
static bool exec_queue_registered(struct xe_exec_queue *q)
{
@@ -203,36 +202,6 @@ static void set_exec_queue_wedged(struct xe_exec_queue *q)
atomic_or(EXEC_QUEUE_STATE_WEDGED, &q->guc->state);
}
-static bool exec_queue_check_timeout(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_CHECK_TIMEOUT;
-}
-
-static void set_exec_queue_check_timeout(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_CHECK_TIMEOUT, &q->guc->state);
-}
-
-static void clear_exec_queue_check_timeout(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_CHECK_TIMEOUT, &q->guc->state);
-}
-
-static bool exec_queue_extra_ref(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_EXTRA_REF;
-}
-
-static void set_exec_queue_extra_ref(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_EXTRA_REF, &q->guc->state);
-}
-
-static void clear_exec_queue_extra_ref(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_EXTRA_REF, &q->guc->state);
-}
-
static bool exec_queue_pending_resume(struct xe_exec_queue *q)
{
return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_RESUME;
@@ -248,19 +217,19 @@ static void clear_exec_queue_pending_resume(struct xe_exec_queue *q)
atomic_and(~EXEC_QUEUE_STATE_PENDING_RESUME, &q->guc->state);
}
-static bool exec_queue_pending_tdr_exit(struct xe_exec_queue *q)
+static bool exec_queue_idle_skip_suspend(struct xe_exec_queue *q)
{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_TDR_EXIT;
+ return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND;
}
-static void set_exec_queue_pending_tdr_exit(struct xe_exec_queue *q)
+static void set_exec_queue_idle_skip_suspend(struct xe_exec_queue *q)
{
- atomic_or(EXEC_QUEUE_STATE_PENDING_TDR_EXIT, &q->guc->state);
+ atomic_or(EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state);
}
-static void clear_exec_queue_pending_tdr_exit(struct xe_exec_queue *q)
+static void clear_exec_queue_idle_skip_suspend(struct xe_exec_queue *q)
{
- atomic_and(~EXEC_QUEUE_STATE_PENDING_TDR_EXIT, &q->guc->state);
+ atomic_and(~EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state);
}
static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q)
@@ -388,7 +357,7 @@ static int guc_init_global_schedule_policy(struct xe_guc *guc)
*emit++ = XE_GUC_ACTION_UPDATE_SCHEDULING_POLICIES_KLV;
- if (CCS_MASK(guc_to_gt(guc)))
+ if (CCS_INSTANCES(guc_to_gt(guc)))
emit = emit_render_compute_yield_klv(emit);
count = emit - data;
@@ -541,7 +510,8 @@ static void init_policies(struct xe_guc *guc, struct xe_exec_queue *q)
u32 slpc_exec_queue_freq_req = 0;
u32 preempt_timeout_us = q->sched_props.preempt_timeout_us;
- xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
+ xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q) &&
+ !xe_exec_queue_is_multi_queue_secondary(q));
if (q->flags & EXEC_QUEUE_FLAG_LOW_LATENCY)
slpc_exec_queue_freq_req |= SLPC_CTX_FREQ_REQ_IS_COMPUTE;
@@ -561,6 +531,8 @@ static void set_min_preemption_timeout(struct xe_guc *guc, struct xe_exec_queue
{
struct exec_queue_policy policy;
+ xe_assert(guc_to_xe(guc), !xe_exec_queue_is_multi_queue_secondary(q));
+
__guc_exec_queue_policy_start_klv(&policy, q->guc->id);
__guc_exec_queue_policy_add_preemption_timeout(&policy, 1);
@@ -568,6 +540,103 @@ static void set_min_preemption_timeout(struct xe_guc *guc, struct xe_exec_queue
__guc_exec_queue_policy_action_size(&policy), 0, 0);
}
+static bool vf_recovery(struct xe_guc *guc)
+{
+ return xe_gt_recovery_pending(guc_to_gt(guc));
+}
+
+static void xe_guc_exec_queue_trigger_cleanup(struct xe_exec_queue *q)
+{
+ struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_device *xe = guc_to_xe(guc);
+
+ /** to wakeup xe_wait_user_fence ioctl if exec queue is reset */
+ wake_up_all(&xe->ufence_wq);
+
+ xe_sched_tdr_queue_imm(&q->guc->sched);
+}
+
+static void xe_guc_exec_queue_group_trigger_cleanup(struct xe_exec_queue *q)
+{
+ struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q);
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+ struct xe_exec_queue *eq;
+
+ xe_gt_assert(guc_to_gt(exec_queue_to_guc(q)),
+ xe_exec_queue_is_multi_queue(q));
+
+ /* Group banned, skip timeout check in TDR */
+ WRITE_ONCE(group->banned, true);
+ xe_guc_exec_queue_trigger_cleanup(primary);
+
+ mutex_lock(&group->list_lock);
+ list_for_each_entry(eq, &group->list, multi_queue.link)
+ xe_guc_exec_queue_trigger_cleanup(eq);
+ mutex_unlock(&group->list_lock);
+}
+
+static void xe_guc_exec_queue_reset_trigger_cleanup(struct xe_exec_queue *q)
+{
+ if (xe_exec_queue_is_multi_queue(q)) {
+ struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q);
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+ struct xe_exec_queue *eq;
+
+ /* Group banned, skip timeout check in TDR */
+ WRITE_ONCE(group->banned, true);
+
+ set_exec_queue_reset(primary);
+ if (!exec_queue_banned(primary))
+ xe_guc_exec_queue_trigger_cleanup(primary);
+
+ mutex_lock(&group->list_lock);
+ list_for_each_entry(eq, &group->list, multi_queue.link) {
+ set_exec_queue_reset(eq);
+ if (!exec_queue_banned(eq))
+ xe_guc_exec_queue_trigger_cleanup(eq);
+ }
+ mutex_unlock(&group->list_lock);
+ } else {
+ set_exec_queue_reset(q);
+ if (!exec_queue_banned(q))
+ xe_guc_exec_queue_trigger_cleanup(q);
+ }
+}
+
+static void set_exec_queue_group_banned(struct xe_exec_queue *q)
+{
+ struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q);
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+ struct xe_exec_queue *eq;
+
+ /* Ban all queues of the multi-queue group */
+ xe_gt_assert(guc_to_gt(exec_queue_to_guc(q)),
+ xe_exec_queue_is_multi_queue(q));
+ set_exec_queue_banned(primary);
+
+ mutex_lock(&group->list_lock);
+ list_for_each_entry(eq, &group->list, multi_queue.link)
+ set_exec_queue_banned(eq);
+ mutex_unlock(&group->list_lock);
+}
+
+/* Helper for context registration H2G */
+struct guc_ctxt_registration_info {
+ u32 flags;
+ u32 context_idx;
+ u32 engine_class;
+ u32 engine_submit_mask;
+ u32 wq_desc_lo;
+ u32 wq_desc_hi;
+ u32 wq_base_lo;
+ u32 wq_base_hi;
+ u32 wq_size;
+ u32 cgp_lo;
+ u32 cgp_hi;
+ u32 hwlrca_lo;
+ u32 hwlrca_hi;
+};
+
#define parallel_read(xe_, map_, field_) \
xe_map_rd_field(xe_, &map_, 0, struct guc_submit_parallel_scratch, \
field_)
@@ -575,6 +644,181 @@ static void set_min_preemption_timeout(struct xe_guc *guc, struct xe_exec_queue
xe_map_wr_field(xe_, &map_, 0, struct guc_submit_parallel_scratch, \
field_, val_)
+/**
+ * DOC: Multi Queue Group GuC interface
+ *
+ * The multi queue group coordination between KMD and GuC is through a software
+ * construct called Context Group Page (CGP). The CGP is a KMD managed 4KB page
+ * allocated in the global GTT.
+ *
+ * CGP format:
+ *
+ * +-----------+---------------------------+---------------------------------------------+
+ * | DWORD | Name | Description |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 0 | Version | Bits [15:8]=Major ver, [7:0]=Minor ver |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 1..15 | RESERVED | MBZ |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 16 | KMD_QUEUE_UPDATE_MASK_DW0 | KMD queue mask for queues 31..0 |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 17 | KMD_QUEUE_UPDATE_MASK_DW1 | KMD queue mask for queues 63..32 |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 18..31 | RESERVED | MBZ |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 32 | Q0CD_DW0 | Queue 0 context LRC descriptor lower DWORD |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 33 | Q0ContextIndex | Context ID for Queue 0 |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 34 | Q1CD_DW0 | Queue 1 context LRC descriptor lower DWORD |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 35 | Q1ContextIndex | Context ID for Queue 1 |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | ... |... | ... |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 158 | Q63CD_DW0 | Queue 63 context LRC descriptor lower DWORD |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 159 | Q63ContextIndex | Context ID for Queue 63 |
+ * +-----------+---------------------------+---------------------------------------------+
+ * | 160..1024 | RESERVED | MBZ |
+ * +-----------+---------------------------+---------------------------------------------+
+ *
+ * While registering Q0 with GuC, CGP is updated with Q0 entry and GuC is notified
+ * through XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_QUEUE H2G message which specifies
+ * the CGP address. When the secondary queues are added to the group, the CGP is
+ * updated with entry for that queue and GuC is notified through the H2G interface
+ * XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC. GuC responds to these H2G messages
+ * with a XE_GUC_ACTION_NOTIFY_MULTIQ_CONTEXT_CGP_SYNC_DONE G2H message. GuC also
+ * sends a XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CGP_CONTEXT_ERROR notification for any
+ * error in the CGP. Only one of these CGP update messages can be outstanding
+ * (waiting for GuC response) at any time. The bits in KMD_QUEUE_UPDATE_MASK_DW*
+ * fields indicate which queue entry is being updated in the CGP.
+ *
+ * The primary queue (Q0) represents the multi queue group context in GuC and
+ * submission on any queue of the group must be through Q0 GuC interface only.
+ *
+ * As it is not required to register secondary queues with GuC, the secondary queue
+ * context ids in the CGP are populated with Q0 context id.
+ */
+
+#define CGP_VERSION_MAJOR_SHIFT 8
+
+static void xe_guc_exec_queue_group_cgp_update(struct xe_device *xe,
+ struct xe_exec_queue *q)
+{
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+ u32 guc_id = group->primary->guc->id;
+
+ /* Currently implementing CGP version 1.0 */
+ xe_map_wr(xe, &group->cgp_bo->vmap, 0, u32,
+ 1 << CGP_VERSION_MAJOR_SHIFT);
+
+ xe_map_wr(xe, &group->cgp_bo->vmap,
+ (32 + q->multi_queue.pos * 2) * sizeof(u32),
+ u32, lower_32_bits(xe_lrc_descriptor(q->lrc[0])));
+
+ xe_map_wr(xe, &group->cgp_bo->vmap,
+ (33 + q->multi_queue.pos * 2) * sizeof(u32),
+ u32, guc_id);
+
+ if (q->multi_queue.pos / 32) {
+ xe_map_wr(xe, &group->cgp_bo->vmap, 17 * sizeof(u32),
+ u32, BIT(q->multi_queue.pos % 32));
+ xe_map_wr(xe, &group->cgp_bo->vmap, 16 * sizeof(u32), u32, 0);
+ } else {
+ xe_map_wr(xe, &group->cgp_bo->vmap, 16 * sizeof(u32),
+ u32, BIT(q->multi_queue.pos));
+ xe_map_wr(xe, &group->cgp_bo->vmap, 17 * sizeof(u32), u32, 0);
+ }
+}
+
+static void xe_guc_exec_queue_group_cgp_sync(struct xe_guc *guc,
+ struct xe_exec_queue *q,
+ const u32 *action, u32 len)
+{
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+ struct xe_device *xe = guc_to_xe(guc);
+ long ret;
+
+ /*
+ * As all queues of a multi queue group use single drm scheduler
+ * submit workqueue, CGP synchronization with GuC are serialized.
+ * Hence, no locking is required here.
+ * Wait for any pending CGP_SYNC_DONE response before updating the
+ * CGP page and sending CGP_SYNC message.
+ *
+ * FIXME: Support VF migration
+ */
+ ret = wait_event_timeout(guc->ct.wq,
+ !READ_ONCE(group->sync_pending) ||
+ xe_guc_read_stopped(guc), HZ);
+ if (!ret || xe_guc_read_stopped(guc)) {
+ /* CGP_SYNC failed. Reset gt, cleanup the group */
+ xe_gt_warn(guc_to_gt(guc), "Wait for CGP_SYNC_DONE response failed!\n");
+ set_exec_queue_group_banned(q);
+ xe_gt_reset_async(q->gt);
+ xe_guc_exec_queue_group_trigger_cleanup(q);
+ return;
+ }
+
+ xe_lrc_set_multi_queue_priority(q->lrc[0], q->multi_queue.priority);
+ xe_guc_exec_queue_group_cgp_update(xe, q);
+
+ WRITE_ONCE(group->sync_pending, true);
+ xe_guc_ct_send(&guc->ct, action, len, G2H_LEN_DW_MULTI_QUEUE_CONTEXT, 1);
+}
+
+static void __register_exec_queue_group(struct xe_guc *guc,
+ struct xe_exec_queue *q,
+ struct guc_ctxt_registration_info *info)
+{
+#define MAX_MULTI_QUEUE_REG_SIZE (8)
+ u32 action[MAX_MULTI_QUEUE_REG_SIZE];
+ int len = 0;
+
+ action[len++] = XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_QUEUE;
+ action[len++] = info->flags;
+ action[len++] = info->context_idx;
+ action[len++] = info->engine_class;
+ action[len++] = info->engine_submit_mask;
+ action[len++] = 0; /* Reserved */
+ action[len++] = info->cgp_lo;
+ action[len++] = info->cgp_hi;
+
+ xe_gt_assert(guc_to_gt(guc), len <= MAX_MULTI_QUEUE_REG_SIZE);
+#undef MAX_MULTI_QUEUE_REG_SIZE
+
+ /*
+ * The above XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_QUEUE do expect a
+ * XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE response
+ * from guc.
+ */
+ xe_guc_exec_queue_group_cgp_sync(guc, q, action, len);
+}
+
+static void xe_guc_exec_queue_group_add(struct xe_guc *guc,
+ struct xe_exec_queue *q)
+{
+#define MAX_MULTI_QUEUE_CGP_SYNC_SIZE (2)
+ u32 action[MAX_MULTI_QUEUE_CGP_SYNC_SIZE];
+ int len = 0;
+
+ xe_gt_assert(guc_to_gt(guc), xe_exec_queue_is_multi_queue_secondary(q));
+
+ action[len++] = XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC;
+ action[len++] = q->multi_queue.group->primary->guc->id;
+
+ xe_gt_assert(guc_to_gt(guc), len <= MAX_MULTI_QUEUE_CGP_SYNC_SIZE);
+#undef MAX_MULTI_QUEUE_CGP_SYNC_SIZE
+
+ /*
+ * The above XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC do expect a
+ * XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE response
+ * from guc.
+ */
+ xe_guc_exec_queue_group_cgp_sync(guc, q, action, len);
+}
+
static void __register_mlrc_exec_queue(struct xe_guc *guc,
struct xe_exec_queue *q,
struct guc_ctxt_registration_info *info)
@@ -670,6 +914,13 @@ static void register_exec_queue(struct xe_exec_queue *q, int ctx_type)
info.flags = CONTEXT_REGISTRATION_FLAG_KMD |
FIELD_PREP(CONTEXT_REGISTRATION_FLAG_TYPE, ctx_type);
+ if (xe_exec_queue_is_multi_queue(q)) {
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+
+ info.cgp_lo = xe_bo_ggtt_addr(group->cgp_bo);
+ info.cgp_hi = 0;
+ }
+
if (xe_exec_queue_is_parallel(q)) {
u64 ggtt_addr = xe_lrc_parallel_ggtt_addr(lrc);
struct iosys_map map = xe_lrc_parallel_map(lrc);
@@ -690,21 +941,20 @@ static void register_exec_queue(struct xe_exec_queue *q, int ctx_type)
parallel_write(xe, map, wq_desc.wq_status, WQ_STATUS_ACTIVE);
}
- /*
- * We must keep a reference for LR engines if engine is registered with
- * the GuC as jobs signal immediately and can't destroy an engine if the
- * GuC has a reference to it.
- */
- if (xe_exec_queue_is_lr(q))
- xe_exec_queue_get(q);
-
set_exec_queue_registered(q);
trace_xe_exec_queue_register(q);
- if (xe_exec_queue_is_parallel(q))
+ if (xe_exec_queue_is_multi_queue_primary(q))
+ __register_exec_queue_group(guc, q, &info);
+ else if (xe_exec_queue_is_parallel(q))
__register_mlrc_exec_queue(guc, q, &info);
- else
+ else if (!xe_exec_queue_is_multi_queue_secondary(q))
__register_exec_queue(guc, &info);
- init_policies(guc, q);
+
+ if (!xe_exec_queue_is_multi_queue_secondary(q))
+ init_policies(guc, q);
+
+ if (xe_exec_queue_is_multi_queue_secondary(q))
+ xe_guc_exec_queue_group_add(guc, q);
}
static u32 wq_space_until_wrap(struct xe_exec_queue *q)
@@ -712,9 +962,22 @@ static u32 wq_space_until_wrap(struct xe_exec_queue *q)
return (WQ_SIZE - q->guc->wqi_tail);
}
-static bool vf_recovery(struct xe_guc *guc)
+static inline void relaxed_ms_sleep(unsigned int delay_ms)
{
- return xe_gt_recovery_pending(guc_to_gt(guc));
+ unsigned long min_us, max_us;
+
+ if (!delay_ms)
+ return;
+
+ if (delay_ms > 20) {
+ msleep(delay_ms);
+ return;
+ }
+
+ min_us = mul_u32_u32(delay_ms, 1000);
+ max_us = min_us + 500;
+
+ usleep_range(min_us, max_us);
}
static int wq_wait_for_space(struct xe_exec_queue *q, u32 wqi_size)
@@ -722,21 +985,23 @@ static int wq_wait_for_space(struct xe_exec_queue *q, u32 wqi_size)
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
- unsigned int sleep_period_ms = 1;
+ unsigned int sleep_period_ms = 1, sleep_total_ms = 0;
#define AVAILABLE_SPACE \
CIRC_SPACE(q->guc->wqi_tail, q->guc->wqi_head, WQ_SIZE)
if (wqi_size > AVAILABLE_SPACE && !vf_recovery(guc)) {
try_again:
q->guc->wqi_head = parallel_read(xe, map, wq_desc.head);
- if (wqi_size > AVAILABLE_SPACE) {
- if (sleep_period_ms == 1024) {
+ if (wqi_size > AVAILABLE_SPACE && !vf_recovery(guc)) {
+ if (sleep_total_ms > 2000) {
xe_gt_reset_async(q->gt);
return -ENODEV;
}
msleep(sleep_period_ms);
- sleep_period_ms <<= 1;
+ sleep_total_ms += sleep_period_ms;
+ if (sleep_period_ms < 64)
+ sleep_period_ms <<= 1;
goto try_again;
}
}
@@ -825,7 +1090,7 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job)
if (!job->restore_replay || job->last_replay) {
if (xe_exec_queue_is_parallel(q))
wq_item_append(q);
- else
+ else if (!exec_queue_idle_skip_suspend(q))
xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
job->last_replay = false;
}
@@ -833,6 +1098,12 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job)
if (exec_queue_suspended(q) && !xe_exec_queue_is_parallel(q))
return;
+ /*
+ * All queues in a multi-queue group will use the primary queue
+ * of the group to interface with GuC.
+ */
+ q = xe_exec_queue_multi_queue_primary(q);
+
if (!exec_queue_enabled(q) && !exec_queue_suspended(q)) {
action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET;
action[len++] = q->guc->id;
@@ -870,7 +1141,7 @@ guc_exec_queue_run_job(struct drm_sched_job *drm_job)
struct xe_sched_job *job = to_xe_sched_job(drm_job);
struct xe_exec_queue *q = job->q;
struct xe_guc *guc = exec_queue_to_guc(q);
- bool lr = xe_exec_queue_is_lr(q), killed_or_banned_or_wedged =
+ bool killed_or_banned_or_wedged =
exec_queue_killed_or_banned_or_wedged(q);
xe_gt_assert(guc_to_gt(guc), !(exec_queue_destroyed(q) || exec_queue_pending_disable(q)) ||
@@ -879,6 +1150,18 @@ guc_exec_queue_run_job(struct drm_sched_job *drm_job)
trace_xe_sched_job_run(job);
if (!killed_or_banned_or_wedged && !xe_sched_job_is_error(job)) {
+ if (xe_exec_queue_is_multi_queue_secondary(q)) {
+ struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q);
+
+ if (exec_queue_killed_or_banned_or_wedged(primary)) {
+ killed_or_banned_or_wedged = true;
+ goto run_job_out;
+ }
+
+ if (!exec_queue_registered(primary))
+ register_exec_queue(primary, GUC_CONTEXT_NORMAL);
+ }
+
if (!exec_queue_registered(q))
register_exec_queue(q, GUC_CONTEXT_NORMAL);
if (!job->restore_replay)
@@ -887,14 +1170,7 @@ guc_exec_queue_run_job(struct drm_sched_job *drm_job)
job->restore_replay = false;
}
- /*
- * We don't care about job-fence ordering in LR VMs because these fences
- * are never exported; they are used solely to keep jobs on the pending
- * list. Once a queue enters an error state, there's no need to track
- * them.
- */
- if (killed_or_banned_or_wedged && lr)
- xe_sched_job_set_error(job, -ECANCELED);
+run_job_out:
return job->fence;
}
@@ -912,6 +1188,11 @@ int xe_guc_read_stopped(struct xe_guc *guc)
return atomic_read(&guc->submission_state.stopped);
}
+static void handle_multi_queue_secondary_sched_done(struct xe_guc *guc,
+ struct xe_exec_queue *q,
+ u32 runnable_state);
+static void handle_deregister_done(struct xe_guc *guc, struct xe_exec_queue *q);
+
#define MAKE_SCHED_CONTEXT_ACTION(q, enable_disable) \
u32 action[] = { \
XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET, \
@@ -925,7 +1206,9 @@ static void disable_scheduling_deregister(struct xe_guc *guc,
MAKE_SCHED_CONTEXT_ACTION(q, DISABLE);
int ret;
- set_min_preemption_timeout(guc, q);
+ if (!xe_exec_queue_is_multi_queue_secondary(q))
+ set_min_preemption_timeout(guc, q);
+
smp_rmb();
ret = wait_event_timeout(guc->ct.wq,
(!exec_queue_pending_enable(q) &&
@@ -939,8 +1222,7 @@ static void disable_scheduling_deregister(struct xe_guc *guc,
xe_gt_warn(q->gt, "Pending enable/disable failed to respond\n");
xe_sched_submission_start(sched);
xe_gt_reset_async(q->gt);
- if (!xe_exec_queue_is_lr(q))
- xe_sched_tdr_queue_imm(sched);
+ xe_sched_tdr_queue_imm(sched);
return;
}
@@ -953,23 +1235,12 @@ static void disable_scheduling_deregister(struct xe_guc *guc,
* Reserve space for both G2H here as the 2nd G2H is sent from a G2H
* handler and we are not allowed to reserved G2H space in handlers.
*/
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_SCHED_CONTEXT_MODE_SET +
- G2H_LEN_DW_DEREGISTER_CONTEXT, 2);
-}
-
-static void xe_guc_exec_queue_trigger_cleanup(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
-
- /** to wakeup xe_wait_user_fence ioctl if exec queue is reset */
- wake_up_all(&xe->ufence_wq);
-
- if (xe_exec_queue_is_lr(q))
- queue_work(guc_to_gt(guc)->ordered_wq, &q->guc->lr_tdr);
+ if (xe_exec_queue_is_multi_queue_secondary(q))
+ handle_multi_queue_secondary_sched_done(guc, q, 0);
else
- xe_sched_tdr_queue_imm(&q->guc->sched);
+ xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
+ G2H_LEN_DW_SCHED_CONTEXT_MODE_SET +
+ G2H_LEN_DW_DEREGISTER_CONTEXT, 2);
}
/**
@@ -998,8 +1269,9 @@ void xe_guc_submit_wedge(struct xe_guc *guc)
err = devm_add_action_or_reset(guc_to_xe(guc)->drm.dev,
guc_submit_wedged_fini, guc);
if (err) {
- xe_gt_err(gt, "Failed to register clean-up on wedged.mode=2; "
- "Although device is wedged.\n");
+ xe_gt_err(gt, "Failed to register clean-up in wedged.mode=%s; "
+ "Although device is wedged.\n",
+ xe_wedged_mode_to_string(XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET));
return;
}
@@ -1014,7 +1286,7 @@ static bool guc_submit_hint_wedged(struct xe_guc *guc)
{
struct xe_device *xe = guc_to_xe(guc);
- if (xe->wedged.mode != 2)
+ if (xe->wedged.mode != XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)
return false;
if (xe_device_wedged(xe))
@@ -1025,84 +1297,6 @@ static bool guc_submit_hint_wedged(struct xe_guc *guc)
return true;
}
-static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
-{
- struct xe_guc_exec_queue *ge =
- container_of(w, struct xe_guc_exec_queue, lr_tdr);
- struct xe_exec_queue *q = ge->q;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_gpu_scheduler *sched = &ge->sched;
- struct xe_sched_job *job;
- bool wedged = false;
-
- xe_gt_assert(guc_to_gt(guc), xe_exec_queue_is_lr(q));
-
- if (vf_recovery(guc))
- return;
-
- trace_xe_exec_queue_lr_cleanup(q);
-
- if (!exec_queue_killed(q))
- wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
-
- /* Kill the run_job / process_msg entry points */
- xe_sched_submission_stop(sched);
-
- /*
- * Engine state now mostly stable, disable scheduling / deregister if
- * needed. This cleanup routine might be called multiple times, where
- * the actual async engine deregister drops the final engine ref.
- * Calling disable_scheduling_deregister will mark the engine as
- * destroyed and fire off the CT requests to disable scheduling /
- * deregister, which we only want to do once. We also don't want to mark
- * the engine as pending_disable again as this may race with the
- * xe_guc_deregister_done_handler() which treats it as an unexpected
- * state.
- */
- if (!wedged && exec_queue_registered(q) && !exec_queue_destroyed(q)) {
- struct xe_guc *guc = exec_queue_to_guc(q);
- int ret;
-
- set_exec_queue_banned(q);
- disable_scheduling_deregister(guc, q);
-
- /*
- * Must wait for scheduling to be disabled before signalling
- * any fences, if GT broken the GT reset code should signal us.
- */
- ret = wait_event_timeout(guc->ct.wq,
- !exec_queue_pending_disable(q) ||
- xe_guc_read_stopped(guc) ||
- vf_recovery(guc), HZ * 5);
- if (vf_recovery(guc))
- return;
-
- if (!ret) {
- xe_gt_warn(q->gt, "Schedule disable failed to respond, guc_id=%d\n",
- q->guc->id);
- xe_devcoredump(q, NULL, "Schedule disable failed to respond, guc_id=%d\n",
- q->guc->id);
- xe_sched_submission_start(sched);
- xe_gt_reset_async(q->gt);
- return;
- }
- }
-
- if (!exec_queue_killed(q) && !xe_lrc_ring_is_idle(q->lrc[0]))
- xe_devcoredump(q, NULL, "LR job cleanup, guc_id=%d", q->guc->id);
-
- xe_hw_fence_irq_stop(q->fence_irq);
-
- xe_sched_submission_start(sched);
-
- spin_lock(&sched->base.job_list_lock);
- list_for_each_entry(job, &sched->base.pending_list, drm.list)
- xe_sched_job_set_error(job, -ECANCELED);
- spin_unlock(&sched->base.job_list_lock);
-
- xe_hw_fence_irq_start(q->fence_irq);
-}
-
#define ADJUST_FIVE_PERCENT(__t) mul_u64_u32_div(__t, 105, 100)
static bool check_timeout(struct xe_exec_queue *q, struct xe_sched_job *job)
@@ -1121,7 +1315,21 @@ static bool check_timeout(struct xe_exec_queue *q, struct xe_sched_job *job)
return xe_sched_invalidate_job(job, 2);
}
- ctx_timestamp = lower_32_bits(xe_lrc_ctx_timestamp(q->lrc[0]));
+ ctx_timestamp = lower_32_bits(xe_lrc_timestamp(q->lrc[0]));
+ if (ctx_timestamp == job->sample_timestamp) {
+ if (IS_SRIOV_VF(gt_to_xe(gt)))
+ xe_gt_notice(gt, "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, timestamp stuck",
+ xe_sched_job_seqno(job),
+ xe_sched_job_lrc_seqno(job), q->guc->id);
+ else
+ xe_gt_warn(gt, "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, timestamp stuck",
+ xe_sched_job_seqno(job),
+ xe_sched_job_lrc_seqno(job), q->guc->id);
+
+ return xe_sched_invalidate_job(job, 0);
+ }
+
+ job->sample_timestamp = ctx_timestamp;
ctx_job_timestamp = xe_lrc_ctx_job_timestamp(q->lrc[0]);
/*
@@ -1161,8 +1369,11 @@ static void enable_scheduling(struct xe_exec_queue *q)
set_exec_queue_enabled(q);
trace_xe_exec_queue_scheduling_enable(q);
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1);
+ if (xe_exec_queue_is_multi_queue_secondary(q))
+ handle_multi_queue_secondary_sched_done(guc, q, 1);
+ else
+ xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
+ G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1);
ret = wait_event_timeout(guc->ct.wq,
!exec_queue_pending_enable(q) ||
@@ -1172,8 +1383,7 @@ static void enable_scheduling(struct xe_exec_queue *q)
xe_gt_warn(guc_to_gt(guc), "Schedule enable failed to respond");
set_exec_queue_banned(q);
xe_gt_reset_async(q->gt);
- if (!xe_exec_queue_is_lr(q))
- xe_sched_tdr_queue_imm(&q->guc->sched);
+ xe_sched_tdr_queue_imm(&q->guc->sched);
}
}
@@ -1186,52 +1396,34 @@ static void disable_scheduling(struct xe_exec_queue *q, bool immediate)
xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
- if (immediate)
+ if (immediate && !xe_exec_queue_is_multi_queue_secondary(q))
set_min_preemption_timeout(guc, q);
clear_exec_queue_enabled(q);
set_exec_queue_pending_disable(q);
trace_xe_exec_queue_scheduling_disable(q);
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1);
-}
-
-static void __deregister_exec_queue(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- u32 action[] = {
- XE_GUC_ACTION_DEREGISTER_CONTEXT,
- q->guc->id,
- };
-
- xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q));
- xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_enable(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
-
- set_exec_queue_destroyed(q);
- trace_xe_exec_queue_deregister(q);
-
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_DEREGISTER_CONTEXT, 1);
+ if (xe_exec_queue_is_multi_queue_secondary(q))
+ handle_multi_queue_secondary_sched_done(guc, q, 0);
+ else
+ xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
+ G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1);
}
static enum drm_gpu_sched_stat
guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
{
struct xe_sched_job *job = to_xe_sched_job(drm_job);
- struct xe_sched_job *tmp_job;
+ struct drm_sched_job *tmp_job;
struct xe_exec_queue *q = job->q;
struct xe_gpu_scheduler *sched = &q->guc->sched;
struct xe_guc *guc = exec_queue_to_guc(q);
const char *process_name = "no process";
struct xe_device *xe = guc_to_xe(guc);
- unsigned int fw_ref;
int err = -ETIME;
pid_t pid = -1;
- int i = 0;
bool wedged = false, skip_timeout_check;
- xe_gt_assert(guc_to_gt(guc), !xe_exec_queue_is_lr(q));
+ xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q));
/*
* TDR has fired before free job worker. Common if exec queue
@@ -1248,8 +1440,24 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
/* Must check all state after stopping scheduler */
skip_timeout_check = exec_queue_reset(q) ||
- exec_queue_killed_or_banned_or_wedged(q) ||
- exec_queue_destroyed(q);
+ exec_queue_killed_or_banned_or_wedged(q);
+
+ /* Skip timeout check if multi-queue group is banned */
+ if (xe_exec_queue_is_multi_queue(q) &&
+ READ_ONCE(q->multi_queue.group->banned))
+ skip_timeout_check = true;
+
+ /* LR jobs can only get here if queue has been killed or hit an error */
+ if (xe_exec_queue_is_lr(q))
+ xe_gt_assert(guc_to_gt(guc), skip_timeout_check);
+
+ /*
+ * FIXME: In multi-queue scenario, the TDR must ensure that the whole
+ * multi-queue group is off the HW before signaling the fences to avoid
+ * possible memory corruptions. This means disabling scheduling on the
+ * primary queue before or during the secondary queue's TDR. Need to
+ * implement this in least obtrusive way.
+ */
/*
* If devcoredump not captured and GuC capture for the job is not ready
@@ -1258,33 +1466,32 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
if (!exec_queue_killed(q) && !xe->devcoredump.captured &&
!xe_guc_capture_get_matching_and_lock(q)) {
/* take force wake before engine register manual capture */
- fw_ref = xe_force_wake_get(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
xe_gt_info(q->gt, "failed to get forcewake for coredump capture\n");
xe_engine_snapshot_capture_for_queue(q);
-
- xe_force_wake_put(gt_to_fw(q->gt), fw_ref);
}
/*
- * XXX: Sampling timeout doesn't work in wedged mode as we have to
- * modify scheduling state to read timestamp. We could read the
- * timestamp from a register to accumulate current running time but this
- * doesn't work for SRIOV. For now assuming timeouts in wedged mode are
- * genuine timeouts.
+ * Check if job is actually timed out, if so restart job execution and TDR
*/
+ if (!skip_timeout_check && !check_timeout(q, job))
+ goto rearm;
+
if (!exec_queue_killed(q))
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
- /* Engine state now stable, disable scheduling to check timestamp */
- if (!wedged && exec_queue_registered(q)) {
+ set_exec_queue_banned(q);
+
+ /* Kick job / queue off hardware */
+ if (!wedged && (exec_queue_enabled(q) || exec_queue_pending_disable(q))) {
int ret;
if (exec_queue_reset(q))
err = -EIO;
- if (!exec_queue_destroyed(q)) {
+ if (xe_uc_fw_is_running(&guc->fw)) {
/*
* Wait for any pending G2H to flush out before
* modifying state
@@ -1299,13 +1506,6 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
if (!ret || xe_guc_read_stopped(guc))
goto trigger_reset;
- /*
- * Flag communicates to G2H handler that schedule
- * disable originated from a timeout check. The G2H then
- * avoid triggering cleanup or deregistering the exec
- * queue.
- */
- set_exec_queue_check_timeout(q);
disable_scheduling(q, skip_timeout_check);
}
@@ -1319,6 +1519,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
*/
smp_rmb();
ret = wait_event_timeout(guc->ct.wq,
+ !xe_uc_fw_is_running(&guc->fw) ||
!exec_queue_pending_disable(q) ||
xe_guc_read_stopped(guc) ||
vf_recovery(guc), HZ * 5);
@@ -1333,24 +1534,12 @@ trigger_reset:
xe_devcoredump(q, job,
"Schedule disable failed to respond, guc_id=%d, ret=%d, guc_read=%d",
q->guc->id, ret, xe_guc_read_stopped(guc));
- set_exec_queue_extra_ref(q);
- xe_exec_queue_get(q); /* GT reset owns this */
- set_exec_queue_banned(q);
xe_gt_reset_async(q->gt);
xe_sched_tdr_queue_imm(sched);
goto rearm;
}
}
- /*
- * Check if job is actually timed out, if so restart job execution and TDR
- */
- if (!wedged && !skip_timeout_check && !check_timeout(q, job) &&
- !exec_queue_reset(q) && exec_queue_registered(q)) {
- clear_exec_queue_check_timeout(q);
- goto sched_enable;
- }
-
if (q->vm && q->vm->xef) {
process_name = q->vm->xef->process_name;
pid = q->vm->xef->pid;
@@ -1381,46 +1570,29 @@ trigger_reset:
if (!wedged && (q->flags & EXEC_QUEUE_FLAG_KERNEL ||
(q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q)))) {
if (!xe_sched_invalidate_job(job, 2)) {
- clear_exec_queue_check_timeout(q);
xe_gt_reset_async(q->gt);
goto rearm;
}
}
- /* Finish cleaning up exec queue via deregister */
- set_exec_queue_banned(q);
- if (!wedged && exec_queue_registered(q) && !exec_queue_destroyed(q)) {
- set_exec_queue_extra_ref(q);
- xe_exec_queue_get(q);
- __deregister_exec_queue(guc, q);
- }
-
- /* Stop fence signaling */
- xe_hw_fence_irq_stop(q->fence_irq);
+ /* Mark all outstanding jobs as bad, thus completing them */
+ xe_sched_job_set_error(job, err);
+ drm_sched_for_each_pending_job(tmp_job, &sched->base, NULL)
+ xe_sched_job_set_error(to_xe_sched_job(tmp_job), -ECANCELED);
- /*
- * Fence state now stable, stop / start scheduler which cleans up any
- * fences that are complete
- */
- xe_sched_add_pending_job(sched, job);
xe_sched_submission_start(sched);
- xe_guc_exec_queue_trigger_cleanup(q);
-
- /* Mark all outstanding jobs as bad, thus completing them */
- spin_lock(&sched->base.job_list_lock);
- list_for_each_entry(tmp_job, &sched->base.pending_list, drm.list)
- xe_sched_job_set_error(tmp_job, !i++ ? err : -ECANCELED);
- spin_unlock(&sched->base.job_list_lock);
-
- /* Start fence signaling */
- xe_hw_fence_irq_start(q->fence_irq);
+ if (xe_exec_queue_is_multi_queue(q))
+ xe_guc_exec_queue_group_trigger_cleanup(q);
+ else
+ xe_guc_exec_queue_trigger_cleanup(q);
- return DRM_GPU_SCHED_STAT_RESET;
+ /*
+ * We want the job added back to the pending list so it gets freed; this
+ * is what DRM_GPU_SCHED_STAT_NO_HANG does.
+ */
+ return DRM_GPU_SCHED_STAT_NO_HANG;
-sched_enable:
- set_exec_queue_pending_tdr_exit(q);
- enable_scheduling(q);
rearm:
/*
* XXX: Ideally want to adjust timeout based on current execution time
@@ -1455,17 +1627,21 @@ static void __guc_exec_queue_destroy_async(struct work_struct *w)
struct xe_exec_queue *q = ge->q;
struct xe_guc *guc = exec_queue_to_guc(q);
- xe_pm_runtime_get(guc_to_xe(guc));
+ guard(xe_pm_runtime)(guc_to_xe(guc));
trace_xe_exec_queue_destroy(q);
- if (xe_exec_queue_is_lr(q))
- cancel_work_sync(&ge->lr_tdr);
+ if (xe_exec_queue_is_multi_queue_secondary(q)) {
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+
+ mutex_lock(&group->list_lock);
+ list_del(&q->multi_queue.link);
+ mutex_unlock(&group->list_lock);
+ }
+
/* Confirm no work left behind accessing device structures */
cancel_delayed_work_sync(&ge->sched.base.work_tdr);
xe_exec_queue_fini(q);
-
- xe_pm_runtime_put(guc_to_xe(guc));
}
static void guc_exec_queue_destroy_async(struct xe_exec_queue *q)
@@ -1570,9 +1746,10 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg)
{
struct xe_exec_queue *q = msg->private_data;
struct xe_guc *guc = exec_queue_to_guc(q);
+ bool idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q);
- if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) &&
- exec_queue_enabled(q)) {
+ if (!idle_skip_suspend && guc_exec_queue_allowed_to_change_state(q) &&
+ !exec_queue_suspended(q) && exec_queue_enabled(q)) {
wait_event(guc->ct.wq, vf_recovery(guc) ||
((q->guc->resume_time != RESUME_PENDING ||
xe_guc_read_stopped(guc)) && !exec_queue_pending_disable(q)));
@@ -1585,17 +1762,39 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg)
since_resume_ms;
if (wait_ms > 0 && q->guc->resume_time)
- msleep(wait_ms);
+ relaxed_ms_sleep(wait_ms);
set_exec_queue_suspended(q);
disable_scheduling(q, false);
}
} else if (q->guc->suspend_pending) {
+ if (idle_skip_suspend)
+ set_exec_queue_idle_skip_suspend(q);
set_exec_queue_suspended(q);
suspend_fence_signal(q);
}
}
+static void sched_context(struct xe_exec_queue *q)
+{
+ struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_lrc *lrc = q->lrc[0];
+ u32 action[] = {
+ XE_GUC_ACTION_SCHED_CONTEXT,
+ q->guc->id,
+ };
+
+ xe_gt_assert(guc_to_gt(guc), !xe_exec_queue_is_parallel(q));
+ xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q));
+ xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
+ xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
+
+ trace_xe_exec_queue_submit(q);
+
+ xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
+ xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
+}
+
static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
{
struct xe_exec_queue *q = msg->private_data;
@@ -1603,19 +1802,53 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
if (guc_exec_queue_allowed_to_change_state(q)) {
clear_exec_queue_suspended(q);
if (!exec_queue_enabled(q)) {
+ if (exec_queue_idle_skip_suspend(q)) {
+ struct xe_lrc *lrc = q->lrc[0];
+
+ clear_exec_queue_idle_skip_suspend(q);
+ xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
+ }
q->guc->resume_time = RESUME_PENDING;
set_exec_queue_pending_resume(q);
enable_scheduling(q);
+ } else if (exec_queue_idle_skip_suspend(q)) {
+ clear_exec_queue_idle_skip_suspend(q);
+ sched_context(q);
}
} else {
clear_exec_queue_suspended(q);
+ clear_exec_queue_idle_skip_suspend(q);
+ }
+}
+
+static void __guc_exec_queue_process_msg_set_multi_queue_priority(struct xe_sched_msg *msg)
+{
+ struct xe_exec_queue *q = msg->private_data;
+
+ if (guc_exec_queue_allowed_to_change_state(q)) {
+#define MAX_MULTI_QUEUE_CGP_SYNC_SIZE (2)
+ struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+ u32 action[MAX_MULTI_QUEUE_CGP_SYNC_SIZE];
+ int len = 0;
+
+ action[len++] = XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC;
+ action[len++] = group->primary->guc->id;
+
+ xe_gt_assert(guc_to_gt(guc), len <= MAX_MULTI_QUEUE_CGP_SYNC_SIZE);
+#undef MAX_MULTI_QUEUE_CGP_SYNC_SIZE
+
+ xe_guc_exec_queue_group_cgp_sync(guc, q, action, len);
}
+
+ kfree(msg);
}
-#define CLEANUP 1 /* Non-zero values to catch uninitialized msg */
-#define SET_SCHED_PROPS 2
-#define SUSPEND 3
-#define RESUME 4
+#define CLEANUP 1 /* Non-zero values to catch uninitialized msg */
+#define SET_SCHED_PROPS 2
+#define SUSPEND 3
+#define RESUME 4
+#define SET_MULTI_QUEUE_PRIORITY 5
#define OPCODE_MASK 0xf
#define MSG_LOCKED BIT(8)
#define MSG_HEAD BIT(9)
@@ -1639,6 +1872,9 @@ static void guc_exec_queue_process_msg(struct xe_sched_msg *msg)
case RESUME:
__guc_exec_queue_process_msg_resume(msg);
break;
+ case SET_MULTI_QUEUE_PRIORITY:
+ __guc_exec_queue_process_msg_set_multi_queue_priority(msg);
+ break;
default:
XE_WARN_ON("Unknown message type");
}
@@ -1660,6 +1896,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
{
struct xe_gpu_scheduler *sched;
struct xe_guc *guc = exec_queue_to_guc(q);
+ struct workqueue_struct *submit_wq = NULL;
struct xe_guc_exec_queue *ge;
long timeout;
int err, i;
@@ -1680,8 +1917,20 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
timeout = (q->vm && xe_vm_in_lr_mode(q->vm)) ? MAX_SCHEDULE_TIMEOUT :
msecs_to_jiffies(q->sched_props.job_timeout_ms);
+
+ /*
+ * Use primary queue's submit_wq for all secondary queues of a
+ * multi queue group. This serialization avoids any locking around
+ * CGP synchronization with GuC.
+ */
+ if (xe_exec_queue_is_multi_queue_secondary(q)) {
+ struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q);
+
+ submit_wq = primary->guc->sched.base.submit_wq;
+ }
+
err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
- NULL, xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, 64,
+ submit_wq, xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, 64,
timeout, guc_to_gt(guc)->ordered_wq, NULL,
q->name, gt_to_xe(q->gt)->drm.dev);
if (err)
@@ -1692,9 +1941,6 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
if (err)
goto err_sched;
- if (xe_exec_queue_is_lr(q))
- INIT_WORK(&q->guc->lr_tdr, xe_guc_exec_queue_lr_cleanup);
-
mutex_lock(&guc->submission_state.lock);
err = alloc_guc_id(guc, q);
@@ -1710,7 +1956,23 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
xe_exec_queue_assign_name(q, q->guc->id);
- trace_xe_exec_queue_create(q);
+ /*
+ * Maintain secondary queues of the multi queue group in a list
+ * for handling dependencies across the queues in the group.
+ */
+ if (xe_exec_queue_is_multi_queue_secondary(q)) {
+ struct xe_exec_queue_group *group = q->multi_queue.group;
+
+ INIT_LIST_HEAD(&q->multi_queue.link);
+ mutex_lock(&group->list_lock);
+ list_add_tail(&q->multi_queue.link, &group->list);
+ mutex_unlock(&group->list_lock);
+ }
+
+ if (xe_exec_queue_is_multi_queue(q))
+ trace_xe_exec_queue_create_multi_queue(q);
+ else
+ trace_xe_exec_queue_create(q);
return 0;
@@ -1842,6 +2104,27 @@ static int guc_exec_queue_set_preempt_timeout(struct xe_exec_queue *q,
return 0;
}
+static int guc_exec_queue_set_multi_queue_priority(struct xe_exec_queue *q,
+ enum xe_multi_queue_priority priority)
+{
+ struct xe_sched_msg *msg;
+
+ xe_gt_assert(guc_to_gt(exec_queue_to_guc(q)), xe_exec_queue_is_multi_queue(q));
+
+ if (q->multi_queue.priority == priority ||
+ exec_queue_killed_or_banned_or_wedged(q))
+ return 0;
+
+ msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ q->multi_queue.priority = priority;
+ guc_exec_queue_add_msg(q, msg, SET_MULTI_QUEUE_PRIORITY);
+
+ return 0;
+}
+
static int guc_exec_queue_suspend(struct xe_exec_queue *q)
{
struct xe_gpu_scheduler *sched = &q->guc->sched;
@@ -1916,6 +2199,10 @@ static void guc_exec_queue_resume(struct xe_exec_queue *q)
static bool guc_exec_queue_reset_status(struct xe_exec_queue *q)
{
+ if (xe_exec_queue_is_multi_queue_secondary(q) &&
+ guc_exec_queue_reset_status(xe_exec_queue_multi_queue_primary(q)))
+ return true;
+
return exec_queue_reset(q) || exec_queue_killed_or_banned_or_wedged(q);
}
@@ -1933,6 +2220,7 @@ static const struct xe_exec_queue_ops guc_exec_queue_ops = {
.set_priority = guc_exec_queue_set_priority,
.set_timeslice = guc_exec_queue_set_timeslice,
.set_preempt_timeout = guc_exec_queue_set_preempt_timeout,
+ .set_multi_queue_priority = guc_exec_queue_set_multi_queue_priority,
.suspend = guc_exec_queue_suspend,
.suspend_wait = guc_exec_queue_suspend_wait,
.resume = guc_exec_queue_resume,
@@ -1948,9 +2236,7 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q)
/* Clean up lost G2H + reset engine state */
if (exec_queue_registered(q)) {
- if (exec_queue_extra_ref(q) || xe_exec_queue_is_lr(q))
- xe_exec_queue_put(q);
- else if (exec_queue_destroyed(q))
+ if (exec_queue_destroyed(q))
__guc_exec_queue_destroy(guc, q);
}
if (q->guc->suspend_pending) {
@@ -1980,9 +2266,6 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q)
trace_xe_sched_job_ban(job);
ban = true;
}
- } else if (xe_exec_queue_is_lr(q) &&
- !xe_lrc_ring_is_idle(q->lrc[0])) {
- ban = true;
}
if (ban) {
@@ -2062,11 +2345,8 @@ static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc,
q->guc->id);
}
- if (pending_enable && !pending_resume &&
- !exec_queue_pending_tdr_exit(q)) {
+ if (pending_enable && !pending_resume) {
clear_exec_queue_registered(q);
- if (xe_exec_queue_is_lr(q))
- xe_exec_queue_put(q);
xe_gt_dbg(guc_to_gt(guc), "Replay REGISTER - guc_id=%d",
q->guc->id);
}
@@ -2074,7 +2354,6 @@ static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc,
if (pending_enable) {
clear_exec_queue_enabled(q);
clear_exec_queue_pending_resume(q);
- clear_exec_queue_pending_tdr_exit(q);
clear_exec_queue_pending_enable(q);
xe_gt_dbg(guc_to_gt(guc), "Replay ENABLE - guc_id=%d",
q->guc->id);
@@ -2082,11 +2361,7 @@ static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc,
if (exec_queue_destroyed(q) && exec_queue_registered(q)) {
clear_exec_queue_destroyed(q);
- if (exec_queue_extra_ref(q))
- xe_exec_queue_put(q);
- else
- q->guc->needs_cleanup = true;
- clear_exec_queue_extra_ref(q);
+ q->guc->needs_cleanup = true;
xe_gt_dbg(guc_to_gt(guc), "Replay CLEANUP - guc_id=%d",
q->guc->id);
}
@@ -2104,7 +2379,6 @@ static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc,
if (!pending_enable)
set_exec_queue_enabled(q);
clear_exec_queue_pending_disable(q);
- clear_exec_queue_check_timeout(q);
xe_gt_dbg(guc_to_gt(guc), "Replay DISABLE - guc_id=%d",
q->guc->id);
}
@@ -2139,10 +2413,7 @@ static void guc_exec_queue_pause(struct xe_guc *guc, struct xe_exec_queue *q)
/* Stop scheduling + flush any DRM scheduler operations */
xe_sched_submission_stop(sched);
- if (xe_exec_queue_is_lr(q))
- cancel_work_sync(&q->guc->lr_tdr);
- else
- cancel_delayed_work_sync(&sched->base.work_tdr);
+ cancel_delayed_work_sync(&sched->base.work_tdr);
guc_exec_queue_revert_pending_state_change(guc, q);
@@ -2182,6 +2453,22 @@ void xe_guc_submit_pause(struct xe_guc *guc)
struct xe_exec_queue *q;
unsigned long index;
+ mutex_lock(&guc->submission_state.lock);
+ xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
+ xe_sched_submission_stop(&q->guc->sched);
+ mutex_unlock(&guc->submission_state.lock);
+}
+
+/**
+ * xe_guc_submit_pause_vf - Stop further runs of submission tasks for VF.
+ * @guc: the &xe_guc struct instance whose scheduler is to be disabled
+ */
+void xe_guc_submit_pause_vf(struct xe_guc *guc)
+{
+ struct xe_exec_queue *q;
+ unsigned long index;
+
+ xe_gt_assert(guc_to_gt(guc), IS_SRIOV_VF(guc_to_xe(guc)));
xe_gt_assert(guc_to_gt(guc), vf_recovery(guc));
mutex_lock(&guc->submission_state.lock);
@@ -2254,9 +2541,11 @@ static void guc_exec_queue_unpause_prepare(struct xe_guc *guc,
{
struct xe_gpu_scheduler *sched = &q->guc->sched;
struct xe_sched_job *job = NULL;
+ struct drm_sched_job *s_job;
bool restore_replay = false;
- list_for_each_entry(job, &sched->base.pending_list, drm.list) {
+ drm_sched_for_each_pending_job(s_job, &sched->base, NULL) {
+ job = to_xe_sched_job(s_job);
restore_replay |= job->restore_replay;
if (restore_replay) {
xe_gt_dbg(guc_to_gt(guc), "Replay JOB - guc_id=%d, seqno=%d",
@@ -2272,14 +2561,15 @@ static void guc_exec_queue_unpause_prepare(struct xe_guc *guc,
}
/**
- * xe_guc_submit_unpause_prepare - Prepare unpause submission tasks on given GuC.
+ * xe_guc_submit_unpause_prepare_vf - Prepare unpause submission tasks for VF.
* @guc: the &xe_guc struct instance whose scheduler is to be prepared for unpause
*/
-void xe_guc_submit_unpause_prepare(struct xe_guc *guc)
+void xe_guc_submit_unpause_prepare_vf(struct xe_guc *guc)
{
struct xe_exec_queue *q;
unsigned long index;
+ xe_gt_assert(guc_to_gt(guc), IS_SRIOV_VF(guc_to_xe(guc)));
xe_gt_assert(guc_to_gt(guc), vf_recovery(guc));
mutex_lock(&guc->submission_state.lock);
@@ -2356,13 +2646,30 @@ void xe_guc_submit_unpause(struct xe_guc *guc)
unsigned long index;
mutex_lock(&guc->submission_state.lock);
+ xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
+ xe_sched_submission_start(&q->guc->sched);
+ mutex_unlock(&guc->submission_state.lock);
+}
+
+/**
+ * xe_guc_submit_unpause_vf - Allow further runs of submission tasks for VF.
+ * @guc: the &xe_guc struct instance whose scheduler is to be enabled
+ */
+void xe_guc_submit_unpause_vf(struct xe_guc *guc)
+{
+ struct xe_exec_queue *q;
+ unsigned long index;
+
+ xe_gt_assert(guc_to_gt(guc), IS_SRIOV_VF(guc_to_xe(guc)));
+
+ mutex_lock(&guc->submission_state.lock);
xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) {
/*
* Prevent redundant attempts to stop parallel queues, or queues
* created after resfix done.
*/
if (q->guc->id != index ||
- !READ_ONCE(q->guc->sched.base.pause_submit))
+ !drm_sched_is_stopped(&q->guc->sched.base))
continue;
guc_exec_queue_unpause(guc, q);
@@ -2431,7 +2738,11 @@ static void deregister_exec_queue(struct xe_guc *guc, struct xe_exec_queue *q)
trace_xe_exec_queue_deregister(q);
- xe_guc_ct_send_g2h_handler(&guc->ct, action, ARRAY_SIZE(action));
+ if (xe_exec_queue_is_multi_queue_secondary(q))
+ handle_deregister_done(guc, q);
+ else
+ xe_guc_ct_send_g2h_handler(&guc->ct, action,
+ ARRAY_SIZE(action));
}
static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q,
@@ -2444,13 +2755,10 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q,
q->guc->resume_time = ktime_get();
clear_exec_queue_pending_resume(q);
- clear_exec_queue_pending_tdr_exit(q);
clear_exec_queue_pending_enable(q);
smp_wmb();
wake_up_all(&guc->ct.wq);
} else {
- bool check_timeout = exec_queue_check_timeout(q);
-
xe_gt_assert(guc_to_gt(guc), runnable_state == 0);
xe_gt_assert(guc_to_gt(guc), exec_queue_pending_disable(q));
@@ -2458,11 +2766,11 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q,
suspend_fence_signal(q);
clear_exec_queue_pending_disable(q);
} else {
- if (exec_queue_banned(q) || check_timeout) {
+ if (exec_queue_banned(q)) {
smp_wmb();
wake_up_all(&guc->ct.wq);
}
- if (!check_timeout && exec_queue_destroyed(q)) {
+ if (exec_queue_destroyed(q)) {
/*
* Make sure to clear the pending_disable only
* after sampling the destroyed state. We want
@@ -2481,6 +2789,16 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q,
}
}
+static void handle_multi_queue_secondary_sched_done(struct xe_guc *guc,
+ struct xe_exec_queue *q,
+ u32 runnable_state)
+{
+ /* Take CT lock here as handle_sched_done() do send a h2g message */
+ mutex_lock(&guc->ct.lock);
+ handle_sched_done(guc, q, runnable_state);
+ mutex_unlock(&guc->ct.lock);
+}
+
int xe_guc_sched_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
{
struct xe_exec_queue *q;
@@ -2515,11 +2833,7 @@ static void handle_deregister_done(struct xe_guc *guc, struct xe_exec_queue *q)
trace_xe_exec_queue_deregister_done(q);
clear_exec_queue_registered(q);
-
- if (exec_queue_extra_ref(q) || xe_exec_queue_is_lr(q))
- xe_exec_queue_put(q);
- else
- __guc_exec_queue_destroy(guc, q);
+ __guc_exec_queue_destroy(guc, q);
}
int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
@@ -2564,8 +2878,9 @@ int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
if (unlikely(!q))
return -EPROTO;
- xe_gt_info(gt, "Engine reset: engine_class=%s, logical_mask: 0x%x, guc_id=%d",
- xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
+ xe_gt_info(gt, "Engine reset: engine_class=%s, logical_mask: 0x%x, guc_id=%d, state=0x%0x",
+ xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id,
+ atomic_read(&q->guc->state));
trace_xe_exec_queue_reset(q);
@@ -2575,9 +2890,7 @@ int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
* jobs by setting timeout of the job to the minimum value kicking
* guc_exec_queue_timedout_job.
*/
- set_exec_queue_reset(q);
- if (!exec_queue_banned(q) && !exec_queue_check_timeout(q))
- xe_guc_exec_queue_trigger_cleanup(q);
+ xe_guc_exec_queue_reset_trigger_cleanup(q);
return 0;
}
@@ -2645,20 +2958,18 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
* See bspec 54047 and 72187 for details.
*/
if (type != XE_GUC_CAT_ERR_TYPE_INVALID)
- xe_gt_dbg(gt,
- "Engine memory CAT error [%u]: class=%s, logical_mask: 0x%x, guc_id=%d",
- type, xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
+ xe_gt_info(gt,
+ "Engine memory CAT error [%u]: class=%s, logical_mask: 0x%x, guc_id=%d",
+ type, xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
else
- xe_gt_dbg(gt,
- "Engine memory CAT error: class=%s, logical_mask: 0x%x, guc_id=%d",
- xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
+ xe_gt_info(gt,
+ "Engine memory CAT error: class=%s, logical_mask: 0x%x, guc_id=%d",
+ xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
trace_xe_exec_queue_memory_cat_error(q);
/* Treat the same as engine reset */
- set_exec_queue_reset(q);
- if (!exec_queue_banned(q) && !exec_queue_check_timeout(q))
- xe_guc_exec_queue_trigger_cleanup(q);
+ xe_guc_exec_queue_reset_trigger_cleanup(q);
return 0;
}
@@ -2685,6 +2996,73 @@ int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 le
return 0;
}
+int xe_guc_exec_queue_cgp_context_error_handler(struct xe_guc *guc, u32 *msg,
+ u32 len)
+{
+ struct xe_gt *gt = guc_to_gt(guc);
+ struct xe_device *xe = guc_to_xe(guc);
+ struct xe_exec_queue *q;
+ u32 guc_id = msg[2];
+
+ if (unlikely(len != XE_GUC_EXEC_QUEUE_CGP_CONTEXT_ERROR_LEN)) {
+ drm_err(&xe->drm, "Invalid length %u", len);
+ return -EPROTO;
+ }
+
+ q = g2h_exec_queue_lookup(guc, guc_id);
+ if (unlikely(!q))
+ return -EPROTO;
+
+ xe_gt_dbg(gt,
+ "CGP context error: [%s] err=0x%x, q0_id=0x%x LRCA=0x%x guc_id=0x%x",
+ msg[0] & 1 ? "uc" : "kmd", msg[1], msg[2], msg[3], msg[4]);
+
+ trace_xe_exec_queue_cgp_context_error(q);
+
+ /* Treat the same as engine reset */
+ xe_guc_exec_queue_reset_trigger_cleanup(q);
+
+ return 0;
+}
+
+/**
+ * xe_guc_exec_queue_cgp_sync_done_handler - CGP synchronization done handler
+ * @guc: guc
+ * @msg: message indicating CGP sync done
+ * @len: length of message
+ *
+ * Set multi queue group's sync_pending flag to false and wakeup anyone waiting
+ * for CGP synchronization to complete.
+ *
+ * Return: 0 on success, -EPROTO for malformed messages.
+ */
+int xe_guc_exec_queue_cgp_sync_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
+{
+ struct xe_device *xe = guc_to_xe(guc);
+ struct xe_exec_queue *q;
+ u32 guc_id = msg[0];
+
+ if (unlikely(len < 1)) {
+ drm_err(&xe->drm, "Invalid CGP_SYNC_DONE length %u", len);
+ return -EPROTO;
+ }
+
+ q = g2h_exec_queue_lookup(guc, guc_id);
+ if (unlikely(!q))
+ return -EPROTO;
+
+ if (!xe_exec_queue_is_multi_queue_primary(q)) {
+ drm_err(&xe->drm, "Unexpected CGP_SYNC_DONE response");
+ return -EPROTO;
+ }
+
+ /* Wakeup the serialized cgp update wait */
+ WRITE_ONCE(q->multi_queue.group->sync_pending, false);
+ xe_guc_ct_wake_waiters(&guc->ct);
+
+ return 0;
+}
+
static void
guc_exec_queue_wq_snapshot_capture(struct xe_exec_queue *q,
struct xe_guc_submit_exec_queue_snapshot *snapshot)
@@ -2784,30 +3162,12 @@ xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q)
if (snapshot->parallel_execution)
guc_exec_queue_wq_snapshot_capture(q, snapshot);
- spin_lock(&sched->base.job_list_lock);
- snapshot->pending_list_size = list_count_nodes(&sched->base.pending_list);
- snapshot->pending_list = kmalloc_array(snapshot->pending_list_size,
- sizeof(struct pending_list_snapshot),
- GFP_ATOMIC);
-
- if (snapshot->pending_list) {
- struct xe_sched_job *job_iter;
-
- i = 0;
- list_for_each_entry(job_iter, &sched->base.pending_list, drm.list) {
- snapshot->pending_list[i].seqno =
- xe_sched_job_seqno(job_iter);
- snapshot->pending_list[i].fence =
- dma_fence_is_signaled(job_iter->fence) ? 1 : 0;
- snapshot->pending_list[i].finished =
- dma_fence_is_signaled(&job_iter->drm.s_fence->finished)
- ? 1 : 0;
- i++;
- }
+ if (xe_exec_queue_is_multi_queue(q)) {
+ snapshot->multi_queue.valid = true;
+ snapshot->multi_queue.primary = xe_exec_queue_multi_queue_primary(q)->guc->id;
+ snapshot->multi_queue.pos = q->multi_queue.pos;
}
- spin_unlock(&sched->base.job_list_lock);
-
return snapshot;
}
@@ -2866,12 +3226,10 @@ xe_guc_exec_queue_snapshot_print(struct xe_guc_submit_exec_queue_snapshot *snaps
if (snapshot->parallel_execution)
guc_exec_queue_wq_snapshot_print(snapshot, p);
- for (i = 0; snapshot->pending_list && i < snapshot->pending_list_size;
- i++)
- drm_printf(p, "\tJob: seqno=%d, fence=%d, finished=%d\n",
- snapshot->pending_list[i].seqno,
- snapshot->pending_list[i].fence,
- snapshot->pending_list[i].finished);
+ if (snapshot->multi_queue.valid) {
+ drm_printf(p, "\tMulti queue primary GuC ID: %d\n", snapshot->multi_queue.primary);
+ drm_printf(p, "\tMulti queue position: %d\n", snapshot->multi_queue.pos);
+ }
}
/**
@@ -2894,7 +3252,6 @@ void xe_guc_exec_queue_snapshot_free(struct xe_guc_submit_exec_queue_snapshot *s
xe_lrc_snapshot_free(snapshot->lrc[i]);
kfree(snapshot->lrc);
}
- kfree(snapshot->pending_list);
kfree(snapshot);
}
@@ -2957,6 +3314,27 @@ void xe_guc_submit_print(struct xe_guc *guc, struct drm_printer *p)
}
/**
+ * xe_guc_has_registered_mlrc_queues - check whether there are any MLRC queues
+ * registered with the GuC
+ * @guc: GuC.
+ *
+ * Return: true if any MLRC queue is registered with the GuC, false otherwise.
+ */
+bool xe_guc_has_registered_mlrc_queues(struct xe_guc *guc)
+{
+ struct xe_exec_queue *q;
+ unsigned long index;
+
+ guard(mutex)(&guc->submission_state.lock);
+
+ xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
+ if (q->width > 1)
+ return true;
+
+ return false;
+}
+
+/**
* xe_guc_contexts_hwsp_rebase - Re-compute GGTT references within all
* exec queues registered to given GuC.
* @guc: the &xe_guc struct instance
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.h b/drivers/gpu/drm/xe/xe_guc_submit.h
index b49a2748ec46..b3839a90c142 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.h
+++ b/drivers/gpu/drm/xe/xe_guc_submit.h
@@ -21,9 +21,11 @@ void xe_guc_submit_reset_wait(struct xe_guc *guc);
void xe_guc_submit_stop(struct xe_guc *guc);
int xe_guc_submit_start(struct xe_guc *guc);
void xe_guc_submit_pause(struct xe_guc *guc);
-void xe_guc_submit_unpause(struct xe_guc *guc);
-void xe_guc_submit_unpause_prepare(struct xe_guc *guc);
void xe_guc_submit_pause_abort(struct xe_guc *guc);
+void xe_guc_submit_pause_vf(struct xe_guc *guc);
+void xe_guc_submit_unpause(struct xe_guc *guc);
+void xe_guc_submit_unpause_vf(struct xe_guc *guc);
+void xe_guc_submit_unpause_prepare_vf(struct xe_guc *guc);
void xe_guc_submit_wedge(struct xe_guc *guc);
int xe_guc_read_stopped(struct xe_guc *guc);
@@ -34,6 +36,9 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
u32 len);
int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 len);
int xe_guc_error_capture_handler(struct xe_guc *guc, u32 *msg, u32 len);
+int xe_guc_exec_queue_cgp_sync_done_handler(struct xe_guc *guc, u32 *msg, u32 len);
+int xe_guc_exec_queue_cgp_context_error_handler(struct xe_guc *guc, u32 *msg,
+ u32 len);
struct xe_guc_submit_exec_queue_snapshot *
xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q);
@@ -47,6 +52,8 @@ xe_guc_exec_queue_snapshot_free(struct xe_guc_submit_exec_queue_snapshot *snapsh
void xe_guc_submit_print(struct xe_guc *guc, struct drm_printer *p);
void xe_guc_register_vf_exec_queue(struct xe_exec_queue *q, int ctx_type);
+bool xe_guc_has_registered_mlrc_queues(struct xe_guc *guc);
+
int xe_guc_contexts_hwsp_rebase(struct xe_guc *guc, void *scratch);
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_submit_types.h b/drivers/gpu/drm/xe/xe_guc_submit_types.h
index dc7456c34583..5ccc5f959bb3 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_submit_types.h
@@ -61,12 +61,6 @@ struct guc_submit_parallel_scratch {
u32 wq[WQ_SIZE / sizeof(u32)];
};
-struct pending_list_snapshot {
- u32 seqno;
- bool fence;
- bool finished;
-};
-
/**
* struct xe_guc_submit_exec_queue_snapshot - Snapshot for devcoredump
*/
@@ -135,10 +129,18 @@ struct xe_guc_submit_exec_queue_snapshot {
u32 wq[WQ_SIZE / sizeof(u32)];
} parallel;
- /** @pending_list_size: Size of the pending list snapshot array */
- int pending_list_size;
- /** @pending_list: snapshot of the pending list info */
- struct pending_list_snapshot *pending_list;
+ /** @multi_queue: snapshot of the multi queue information */
+ struct {
+ /**
+ * @multi_queue.primary: GuC id of the primary exec queue
+ * of the multi queue group.
+ */
+ u32 primary;
+ /** @multi_queue.pos: Position of the exec queue within the multi queue group */
+ u8 pos;
+ /** @valid: The exec queue is part of a multi queue group */
+ bool valid;
+ } multi_queue;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c
index a80175c7c478..774467befbb9 100644
--- a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c
+++ b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c
@@ -13,6 +13,7 @@
#include "xe_guc_tlb_inval.h"
#include "xe_force_wake.h"
#include "xe_mmio.h"
+#include "xe_sa.h"
#include "xe_tlb_inval.h"
#include "regs/xe_guc_regs.h"
@@ -34,9 +35,12 @@ static int send_tlb_inval(struct xe_guc *guc, const u32 *action, int len)
G2H_LEN_DW_TLB_INVALIDATE, 1);
}
-#define MAKE_INVAL_OP(type) ((type << XE_GUC_TLB_INVAL_TYPE_SHIFT) | \
+#define MAKE_INVAL_OP_FLUSH(type, flush_cache) ((type << XE_GUC_TLB_INVAL_TYPE_SHIFT) | \
XE_GUC_TLB_INVAL_MODE_HEAVY << XE_GUC_TLB_INVAL_MODE_SHIFT | \
- XE_GUC_TLB_INVAL_FLUSH_CACHE)
+ (flush_cache ? \
+ XE_GUC_TLB_INVAL_FLUSH_CACHE : 0))
+
+#define MAKE_INVAL_OP(type) MAKE_INVAL_OP_FLUSH(type, true)
static int send_tlb_inval_all(struct xe_tlb_inval *tlb_inval, u32 seqno)
{
@@ -71,12 +75,11 @@ static int send_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval, u32 seqno)
return send_tlb_inval(guc, action, ARRAY_SIZE(action));
} else if (xe_device_uc_enabled(xe) && !xe_device_wedged(xe)) {
struct xe_mmio *mmio = &gt->mmio;
- unsigned int fw_ref;
if (IS_SRIOV_VF(xe))
return -ECANCELED;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
if (xe->info.platform == XE_PVC || GRAPHICS_VER(xe) >= 20) {
xe_mmio_write32(mmio, PVC_GUC_TLB_INV_DESC1,
PVC_GUC_TLB_INV_DESC1_INVALIDATE);
@@ -86,12 +89,28 @@ static int send_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval, u32 seqno)
xe_mmio_write32(mmio, GUC_TLB_INV_CR,
GUC_TLB_INV_CR_INVALIDATE);
}
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
return -ECANCELED;
}
+static int send_page_reclaim(struct xe_guc *guc, u32 seqno,
+ u64 gpu_addr)
+{
+ struct xe_gt *gt = guc_to_gt(guc);
+ u32 action[] = {
+ XE_GUC_ACTION_PAGE_RECLAMATION,
+ seqno,
+ lower_32_bits(gpu_addr),
+ upper_32_bits(gpu_addr),
+ };
+
+ xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_ISSUED_COUNT, 1);
+
+ return xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
+ G2H_LEN_DW_PAGE_RECLAMATION, 1);
+}
+
/*
* Ensure that roundup_pow_of_two(length) doesn't overflow.
* Note that roundup_pow_of_two() operates on unsigned long,
@@ -100,20 +119,21 @@ static int send_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval, u32 seqno)
#define MAX_RANGE_TLB_INVALIDATION_LENGTH (rounddown_pow_of_two(ULONG_MAX))
static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno,
- u64 start, u64 end, u32 asid)
+ u64 start, u64 end, u32 asid,
+ struct drm_suballoc *prl_sa)
{
#define MAX_TLB_INVALIDATION_LEN 7
struct xe_guc *guc = tlb_inval->private;
struct xe_gt *gt = guc_to_gt(guc);
u32 action[MAX_TLB_INVALIDATION_LEN];
u64 length = end - start;
- int len = 0;
+ int len = 0, err;
if (guc_to_xe(guc)->info.force_execlist)
return -ECANCELED;
action[len++] = XE_GUC_ACTION_TLB_INVALIDATION;
- action[len++] = seqno;
+ action[len++] = !prl_sa ? seqno : TLB_INVALIDATION_SEQNO_INVALID;
if (!gt_to_xe(gt)->info.has_range_tlb_inval ||
length > MAX_RANGE_TLB_INVALIDATION_LENGTH) {
action[len++] = MAKE_INVAL_OP(XE_GUC_TLB_INVAL_FULL);
@@ -154,7 +174,8 @@ static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno,
ilog2(SZ_2M) + 1)));
xe_gt_assert(gt, IS_ALIGNED(start, length));
- action[len++] = MAKE_INVAL_OP(XE_GUC_TLB_INVAL_PAGE_SELECTIVE);
+ /* Flush on NULL case, Media is not required to modify flush due to no PPC so NOP */
+ action[len++] = MAKE_INVAL_OP_FLUSH(XE_GUC_TLB_INVAL_PAGE_SELECTIVE, !prl_sa);
action[len++] = asid;
action[len++] = lower_32_bits(start);
action[len++] = upper_32_bits(start);
@@ -163,7 +184,10 @@ static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno,
xe_gt_assert(gt, len <= MAX_TLB_INVALIDATION_LEN);
- return send_tlb_inval(guc, action, len);
+ err = send_tlb_inval(guc, action, len);
+ if (!err && prl_sa)
+ err = send_page_reclaim(guc, seqno, xe_sa_bo_gpu_addr(prl_sa));
+ return err;
}
static bool tlb_inval_initialized(struct xe_tlb_inval *tlb_inval)
diff --git a/drivers/gpu/drm/xe/xe_heci_gsc.c b/drivers/gpu/drm/xe/xe_heci_gsc.c
index 2b3d49dd394c..c1f15313f92e 100644
--- a/drivers/gpu/drm/xe/xe_heci_gsc.c
+++ b/drivers/gpu/drm/xe/xe_heci_gsc.c
@@ -11,7 +11,6 @@
#include <drm/drm_print.h>
#include "xe_device_types.h"
-#include "xe_drv.h"
#include "xe_heci_gsc.h"
#include "regs/xe_gsc_regs.h"
#include "xe_platform_types.h"
@@ -223,7 +222,7 @@ void xe_heci_gsc_irq_handler(struct xe_device *xe, u32 iir)
if (xe->heci_gsc.irq < 0)
return;
- ret = generic_handle_irq(xe->heci_gsc.irq);
+ ret = generic_handle_irq_safe(xe->heci_gsc.irq);
if (ret)
drm_err_ratelimited(&xe->drm, "error handling GSC irq: %d\n", ret);
}
@@ -243,7 +242,7 @@ void xe_heci_csc_irq_handler(struct xe_device *xe, u32 iir)
if (xe->heci_gsc.irq < 0)
return;
- ret = generic_handle_irq(xe->heci_gsc.irq);
+ ret = generic_handle_irq_safe(xe->heci_gsc.irq);
if (ret)
drm_err_ratelimited(&xe->drm, "error handling GSC irq: %d\n", ret);
}
diff --git a/drivers/gpu/drm/xe/xe_huc.c b/drivers/gpu/drm/xe/xe_huc.c
index 0a70c8924582..57afe21444b1 100644
--- a/drivers/gpu/drm/xe/xe_huc.c
+++ b/drivers/gpu/drm/xe/xe_huc.c
@@ -12,7 +12,6 @@
#include "abi/gsc_pxp_commands_abi.h"
#include "regs/xe_gsc_regs.h"
#include "regs/xe_guc_regs.h"
-#include "xe_assert.h"
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_force_wake.h"
@@ -300,19 +299,16 @@ void xe_huc_sanitize(struct xe_huc *huc)
void xe_huc_print_info(struct xe_huc *huc, struct drm_printer *p)
{
struct xe_gt *gt = huc_to_gt(huc);
- unsigned int fw_ref;
xe_uc_fw_print(&huc->fw, p);
if (!xe_uc_fw_is_enabled(&huc->fw))
return;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return;
drm_printf(p, "\nHuC status: 0x%08x\n",
xe_mmio_read32(&gt->mmio, HUC_KERNEL_LOAD_INFO));
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
diff --git a/drivers/gpu/drm/xe/xe_huc_debugfs.c b/drivers/gpu/drm/xe/xe_huc_debugfs.c
index 3a888a40188b..80829967b3d7 100644
--- a/drivers/gpu/drm/xe/xe_huc_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_huc_debugfs.c
@@ -7,11 +7,10 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
-#include "xe_device.h"
-#include "xe_gt.h"
+#include "xe_gt_types.h"
#include "xe_huc.h"
-#include "xe_macros.h"
#include "xe_pm.h"
static struct xe_gt *
@@ -37,9 +36,8 @@ static int huc_info(struct seq_file *m, void *data)
struct xe_device *xe = huc_to_xe(huc);
struct drm_printer p = drm_seq_file_printer(m);
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
xe_huc_print_info(huc, &p);
- xe_pm_runtime_put(xe);
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c
index 6a9e2a4272dd..4d3ee5226e3a 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine.c
@@ -33,7 +33,6 @@
#include "xe_hw_fence.h"
#include "xe_irq.h"
#include "xe_lrc.h"
-#include "xe_macros.h"
#include "xe_mmio.h"
#include "xe_reg_sr.h"
#include "xe_reg_whitelist.h"
diff --git a/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c b/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c
index 640950172088..3c65becb39ad 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c
@@ -7,7 +7,7 @@
#include <linux/kobject.h>
#include <linux/sysfs.h>
-#include "xe_device.h"
+#include "xe_device_types.h"
#include "xe_gt.h"
#include "xe_hw_engine_class_sysfs.h"
#include "xe_pm.h"
@@ -43,16 +43,14 @@ static ssize_t xe_hw_engine_class_sysfs_attr_show(struct kobject *kobj,
{
struct xe_device *xe = kobj_to_xe(kobj);
struct kobj_attribute *kattr;
- ssize_t ret = -EIO;
kattr = container_of(attr, struct kobj_attribute, attr);
if (kattr->show) {
- xe_pm_runtime_get(xe);
- ret = kattr->show(kobj, kattr, buf);
- xe_pm_runtime_put(xe);
+ guard(xe_pm_runtime)(xe);
+ return kattr->show(kobj, kattr, buf);
}
- return ret;
+ return -EIO;
}
static ssize_t xe_hw_engine_class_sysfs_attr_store(struct kobject *kobj,
@@ -62,16 +60,14 @@ static ssize_t xe_hw_engine_class_sysfs_attr_store(struct kobject *kobj,
{
struct xe_device *xe = kobj_to_xe(kobj);
struct kobj_attribute *kattr;
- ssize_t ret = -EIO;
kattr = container_of(attr, struct kobj_attribute, attr);
if (kattr->store) {
- xe_pm_runtime_get(xe);
- ret = kattr->store(kobj, kattr, buf, count);
- xe_pm_runtime_put(xe);
+ guard(xe_pm_runtime)(xe);
+ return kattr->store(kobj, kattr, buf, count);
}
- return ret;
+ return -EIO;
}
static const struct sysfs_ops xe_hw_engine_class_sysfs_ops = {
diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group.c b/drivers/gpu/drm/xe/xe_hw_engine_group.c
index fa4db5f23342..2ef33dfbe3a2 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine_group.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine_group.c
@@ -6,10 +6,12 @@
#include <drm/drm_managed.h>
#include "xe_assert.h"
-#include "xe_device.h"
+#include "xe_device_types.h"
#include "xe_exec_queue.h"
#include "xe_gt.h"
+#include "xe_gt_stats.h"
#include "xe_hw_engine_group.h"
+#include "xe_sync.h"
#include "xe_vm.h"
static void
@@ -20,7 +22,8 @@ hw_engine_group_resume_lr_jobs_func(struct work_struct *w)
int err;
enum xe_hw_engine_group_execution_mode previous_mode;
- err = xe_hw_engine_group_get_mode(group, EXEC_MODE_LR, &previous_mode);
+ err = xe_hw_engine_group_get_mode(group, EXEC_MODE_LR, &previous_mode,
+ NULL, 0);
if (err)
return;
@@ -188,23 +191,39 @@ void xe_hw_engine_group_resume_faulting_lr_jobs(struct xe_hw_engine_group *group
/**
* xe_hw_engine_group_suspend_faulting_lr_jobs() - Suspend the faulting LR jobs of this group
* @group: The hw engine group
+ * @has_deps: dma-fence job triggering suspend has dependencies
*
* Return: 0 on success, negative error code on error.
*/
-static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group *group)
+static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group *group,
+ bool has_deps)
{
int err;
struct xe_exec_queue *q;
+ struct xe_gt *gt = NULL;
bool need_resume = false;
+ ktime_t start = xe_gt_stats_ktime_get();
lockdep_assert_held_write(&group->mode_sem);
list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
+ bool idle_skip_suspend;
+
if (!xe_vm_in_fault_mode(q->vm))
continue;
- need_resume = true;
+ idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q);
+ if (!idle_skip_suspend && has_deps)
+ return -EAGAIN;
+
+ xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, 1);
+ if (idle_skip_suspend)
+ xe_gt_stats_incr(q->gt,
+ XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT, 1);
+
+ need_resume |= !idle_skip_suspend;
q->ops->suspend(q);
+ gt = q->gt;
}
list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
@@ -216,6 +235,12 @@ static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group
return err;
}
+ if (gt) {
+ xe_gt_stats_incr(gt,
+ XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US,
+ xe_gt_stats_ktime_us_delta(start));
+ }
+
if (need_resume)
xe_hw_engine_group_resume_faulting_lr_jobs(group);
@@ -236,7 +261,9 @@ static int xe_hw_engine_group_wait_for_dma_fence_jobs(struct xe_hw_engine_group
{
long timeout;
struct xe_exec_queue *q;
+ struct xe_gt *gt = NULL;
struct dma_fence *fence;
+ ktime_t start = xe_gt_stats_ktime_get();
lockdep_assert_held_write(&group->mode_sem);
@@ -244,18 +271,26 @@ static int xe_hw_engine_group_wait_for_dma_fence_jobs(struct xe_hw_engine_group
if (xe_vm_in_lr_mode(q->vm))
continue;
+ xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT, 1);
fence = xe_exec_queue_last_fence_get_for_resume(q, q->vm);
timeout = dma_fence_wait(fence, false);
dma_fence_put(fence);
+ gt = q->gt;
if (timeout < 0)
return -ETIME;
}
+ if (gt) {
+ xe_gt_stats_incr(gt,
+ XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US,
+ xe_gt_stats_ktime_us_delta(start));
+ }
+
return 0;
}
-static int switch_mode(struct xe_hw_engine_group *group)
+static int switch_mode(struct xe_hw_engine_group *group, bool has_deps)
{
int err = 0;
enum xe_hw_engine_group_execution_mode new_mode;
@@ -265,7 +300,8 @@ static int switch_mode(struct xe_hw_engine_group *group)
switch (group->cur_mode) {
case EXEC_MODE_LR:
new_mode = EXEC_MODE_DMA_FENCE;
- err = xe_hw_engine_group_suspend_faulting_lr_jobs(group);
+ err = xe_hw_engine_group_suspend_faulting_lr_jobs(group,
+ has_deps);
break;
case EXEC_MODE_DMA_FENCE:
new_mode = EXEC_MODE_LR;
@@ -281,19 +317,36 @@ static int switch_mode(struct xe_hw_engine_group *group)
return 0;
}
+static int wait_syncs(struct xe_sync_entry *syncs, int num_syncs)
+{
+ int err, i;
+
+ for (i = 0; i < num_syncs; ++i) {
+ err = xe_sync_entry_wait(syncs + i);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
/**
* xe_hw_engine_group_get_mode() - Get the group to execute in the new mode
* @group: The hw engine group
* @new_mode: The new execution mode
* @previous_mode: Pointer to the previous mode provided for use by caller
+ * @syncs: Syncs from exec IOCTL
+ * @num_syncs: Number of syncs from exec IOCTL
*
* Return: 0 if successful, -EINTR if locking failed.
*/
int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group,
enum xe_hw_engine_group_execution_mode new_mode,
- enum xe_hw_engine_group_execution_mode *previous_mode)
+ enum xe_hw_engine_group_execution_mode *previous_mode,
+ struct xe_sync_entry *syncs, int num_syncs)
__acquires(&group->mode_sem)
{
+ bool has_deps = !!num_syncs;
int err = down_read_interruptible(&group->mode_sem);
if (err)
@@ -303,15 +356,25 @@ __acquires(&group->mode_sem)
if (new_mode != group->cur_mode) {
up_read(&group->mode_sem);
+retry:
err = down_write_killable(&group->mode_sem);
if (err)
return err;
if (new_mode != group->cur_mode) {
- err = switch_mode(group);
+ err = switch_mode(group, has_deps);
if (err) {
up_write(&group->mode_sem);
- return err;
+
+ if (err != -EAGAIN)
+ return err;
+
+ err = wait_syncs(syncs, num_syncs);
+ if (err)
+ return err;
+
+ has_deps = false;
+ goto retry;
}
}
downgrade_write(&group->mode_sem);
diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group.h b/drivers/gpu/drm/xe/xe_hw_engine_group.h
index 797ee81acbf2..8b17ccd30b70 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine_group.h
+++ b/drivers/gpu/drm/xe/xe_hw_engine_group.h
@@ -11,6 +11,7 @@
struct drm_device;
struct xe_exec_queue;
struct xe_gt;
+struct xe_sync_entry;
int xe_hw_engine_setup_groups(struct xe_gt *gt);
@@ -19,7 +20,8 @@ void xe_hw_engine_group_del_exec_queue(struct xe_hw_engine_group *group, struct
int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group,
enum xe_hw_engine_group_execution_mode new_mode,
- enum xe_hw_engine_group_execution_mode *previous_mode);
+ enum xe_hw_engine_group_execution_mode *previous_mode,
+ struct xe_sync_entry *syncs, int num_syncs);
void xe_hw_engine_group_put(struct xe_hw_engine_group *group);
enum xe_hw_engine_group_execution_mode
diff --git a/drivers/gpu/drm/xe/xe_hw_fence.c b/drivers/gpu/drm/xe/xe_hw_fence.c
index f6057456e460..ae8ed15b64c5 100644
--- a/drivers/gpu/drm/xe/xe_hw_fence.c
+++ b/drivers/gpu/drm/xe/xe_hw_fence.c
@@ -8,9 +8,7 @@
#include <linux/device.h>
#include <linux/slab.h>
-#include "xe_bo.h"
-#include "xe_device.h"
-#include "xe_gt.h"
+#include "xe_device_types.h"
#include "xe_hw_engine.h"
#include "xe_macros.h"
#include "xe_map.h"
@@ -108,22 +106,6 @@ void xe_hw_fence_irq_run(struct xe_hw_fence_irq *irq)
irq_work_queue(&irq->work);
}
-void xe_hw_fence_irq_stop(struct xe_hw_fence_irq *irq)
-{
- spin_lock_irq(&irq->lock);
- irq->enabled = false;
- spin_unlock_irq(&irq->lock);
-}
-
-void xe_hw_fence_irq_start(struct xe_hw_fence_irq *irq)
-{
- spin_lock_irq(&irq->lock);
- irq->enabled = true;
- spin_unlock_irq(&irq->lock);
-
- irq_work_queue(&irq->work);
-}
-
void xe_hw_fence_ctx_init(struct xe_hw_fence_ctx *ctx, struct xe_gt *gt,
struct xe_hw_fence_irq *irq, const char *name)
{
diff --git a/drivers/gpu/drm/xe/xe_hw_fence.h b/drivers/gpu/drm/xe/xe_hw_fence.h
index f13a1c4982c7..599492c13f80 100644
--- a/drivers/gpu/drm/xe/xe_hw_fence.h
+++ b/drivers/gpu/drm/xe/xe_hw_fence.h
@@ -17,8 +17,6 @@ void xe_hw_fence_module_exit(void);
void xe_hw_fence_irq_init(struct xe_hw_fence_irq *irq);
void xe_hw_fence_irq_finish(struct xe_hw_fence_irq *irq);
void xe_hw_fence_irq_run(struct xe_hw_fence_irq *irq);
-void xe_hw_fence_irq_stop(struct xe_hw_fence_irq *irq);
-void xe_hw_fence_irq_start(struct xe_hw_fence_irq *irq);
void xe_hw_fence_ctx_init(struct xe_hw_fence_ctx *ctx, struct xe_gt *gt,
struct xe_hw_fence_irq *irq, const char *name);
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index 97879daeefc1..baf277955b33 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -39,10 +39,16 @@ enum xe_hwmon_reg_operation {
REG_READ64,
};
+#define MAX_VRAM_CHANNELS (16)
+
enum xe_hwmon_channel {
CHANNEL_CARD,
CHANNEL_PKG,
CHANNEL_VRAM,
+ CHANNEL_MCTRL,
+ CHANNEL_PCIE,
+ CHANNEL_VRAM_N,
+ CHANNEL_VRAM_N_MAX = CHANNEL_VRAM_N + MAX_VRAM_CHANNELS,
CHANNEL_MAX,
};
@@ -53,6 +59,15 @@ enum xe_fan_channel {
FAN_MAX,
};
+enum xe_temp_limit {
+ TEMP_LIMIT_PKG_SHUTDOWN,
+ TEMP_LIMIT_PKG_CRIT,
+ TEMP_LIMIT_MEM_SHUTDOWN,
+ TEMP_LIMIT_PKG_MAX,
+ TEMP_LIMIT_MEM_CRIT,
+ TEMP_LIMIT_MAX
+};
+
/* Attribute index for powerX_xxx_interval sysfs entries */
enum sensor_attr_power {
SENSOR_INDEX_PSYS_PL1,
@@ -91,6 +106,12 @@ enum sensor_attr_power {
*/
#define PL_WRITE_MBX_TIMEOUT_MS (1)
+/* Index of memory controller in READ_THERMAL_DATA output */
+#define TEMP_INDEX_MCTRL 2
+
+/* Maximum characters in hwmon label name */
+#define MAX_LABEL_SIZE 16
+
/**
* struct xe_hwmon_energy_info - to accumulate energy
*/
@@ -112,6 +133,24 @@ struct xe_hwmon_fan_info {
};
/**
+ * struct xe_hwmon_thermal_info - to store temperature data
+ */
+struct xe_hwmon_thermal_info {
+ union {
+ /** @limit: temperatures limits */
+ u8 limit[TEMP_LIMIT_MAX];
+ /** @data: temperature limits in dwords */
+ u32 data[DIV_ROUND_UP(TEMP_LIMIT_MAX, sizeof(u32))];
+ };
+ /** @count: no of temperature sensors available for the platform */
+ u8 count;
+ /** @value: signed value from each sensor */
+ s8 value[U8_MAX];
+ /** @vram_label: vram label names */
+ char vram_label[MAX_VRAM_CHANNELS][MAX_LABEL_SIZE];
+};
+
+/**
* struct xe_hwmon - xe hwmon data structure
*/
struct xe_hwmon {
@@ -137,7 +176,8 @@ struct xe_hwmon {
u32 pl1_on_boot[CHANNEL_MAX];
/** @pl2_on_boot: power limit PL2 on boot */
u32 pl2_on_boot[CHANNEL_MAX];
-
+ /** @temp: Temperature info */
+ struct xe_hwmon_thermal_info temp;
};
static int xe_hwmon_pcode_read_power_limit(const struct xe_hwmon *hwmon, u32 attr, int channel,
@@ -224,6 +264,8 @@ static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg
return BMG_PACKAGE_TEMPERATURE;
else if (channel == CHANNEL_VRAM)
return BMG_VRAM_TEMPERATURE;
+ else if (in_range(channel, CHANNEL_VRAM_N, CHANNEL_VRAM_N_MAX))
+ return BMG_VRAM_TEMPERATURE_N(channel - CHANNEL_VRAM_N);
} else if (xe->info.platform == XE_DG2) {
if (channel == CHANNEL_PKG)
return PCU_CR_PACKAGE_TEMPERATURE;
@@ -502,7 +544,7 @@ xe_hwmon_power_max_interval_show(struct device *dev, struct device_attribute *at
int ret = 0;
- xe_pm_runtime_get(hwmon->xe);
+ guard(xe_pm_runtime)(hwmon->xe);
mutex_lock(&hwmon->hwmon_lock);
@@ -521,8 +563,6 @@ xe_hwmon_power_max_interval_show(struct device *dev, struct device_attribute *at
mutex_unlock(&hwmon->hwmon_lock);
- xe_pm_runtime_put(hwmon->xe);
-
x = REG_FIELD_GET(PWR_LIM_TIME_X, reg_val);
y = REG_FIELD_GET(PWR_LIM_TIME_Y, reg_val);
@@ -604,7 +644,7 @@ xe_hwmon_power_max_interval_store(struct device *dev, struct device_attribute *a
rxy = REG_FIELD_PREP(PWR_LIM_TIME_X, x) |
REG_FIELD_PREP(PWR_LIM_TIME_Y, y);
- xe_pm_runtime_get(hwmon->xe);
+ guard(xe_pm_runtime)(hwmon->xe);
mutex_lock(&hwmon->hwmon_lock);
@@ -616,8 +656,6 @@ xe_hwmon_power_max_interval_store(struct device *dev, struct device_attribute *a
mutex_unlock(&hwmon->hwmon_lock);
- xe_pm_runtime_put(hwmon->xe);
-
return count;
}
@@ -681,8 +719,29 @@ static const struct attribute_group *hwmon_groups[] = {
};
static const struct hwmon_channel_info * const hwmon_info[] = {
- HWMON_CHANNEL_INFO(temp, HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_INPUT | HWMON_T_LABEL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL |
+ HWMON_T_MAX,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL),
HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_LABEL | HWMON_P_CRIT |
HWMON_P_CAP,
HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_LABEL | HWMON_P_CAP),
@@ -693,6 +752,75 @@ static const struct hwmon_channel_info * const hwmon_info[] = {
NULL
};
+static int xe_hwmon_pcode_read_thermal_info(struct xe_hwmon *hwmon)
+{
+ struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ u32 config = 0;
+ int ret;
+
+ ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_LIMITS, 0),
+ &hwmon->temp.data[0], &hwmon->temp.data[1]);
+ if (ret)
+ return ret;
+
+ drm_dbg(&hwmon->xe->drm, "thermal info read val 0x%x val1 0x%x\n",
+ hwmon->temp.data[0], hwmon->temp.data[1]);
+
+ ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_CONFIG, 0),
+ &config, NULL);
+ if (ret)
+ return ret;
+
+ drm_dbg(&hwmon->xe->drm, "thermal config count 0x%x\n", config);
+ hwmon->temp.count = REG_FIELD_GET(TEMP_MASK, config);
+
+ return ret;
+}
+
+static int get_mc_temp(struct xe_hwmon *hwmon, long *val)
+{
+ struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ u32 *dword = (u32 *)hwmon->temp.value;
+ s32 average = 0;
+ int ret, i;
+
+ for (i = 0; i < DIV_ROUND_UP(TEMP_LIMIT_MAX, sizeof(u32)); i++) {
+ ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_DATA, i),
+ (dword + i), NULL);
+ if (ret)
+ return ret;
+ drm_dbg(&hwmon->xe->drm, "thermal data for group %d val 0x%x\n", i, dword[i]);
+ }
+
+ for (i = TEMP_INDEX_MCTRL; i < hwmon->temp.count - 1; i++)
+ average += hwmon->temp.value[i];
+
+ average /= (hwmon->temp.count - TEMP_INDEX_MCTRL - 1);
+ *val = average * MILLIDEGREE_PER_DEGREE;
+ return 0;
+}
+
+static int get_pcie_temp(struct xe_hwmon *hwmon, long *val)
+{
+ struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ u32 data = 0;
+ int ret;
+
+ ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_DATA,
+ PCIE_SENSOR_GROUP_ID), &data, NULL);
+ if (ret)
+ return ret;
+
+ /* Sensor offset is different for G21 */
+ if (hwmon->xe->info.subplatform != XE_SUBPLATFORM_BATTLEMAGE_G21)
+ data = REG_FIELD_GET(PCIE_SENSOR_MASK, data);
+
+ data = REG_FIELD_GET(TEMP_MASK, data);
+ *val = (s8)data * MILLIDEGREE_PER_DEGREE;
+
+ return 0;
+}
+
/* I1 is exposed as power_crit or as curr_crit depending on bit 31 */
static int xe_hwmon_pcode_read_i1(const struct xe_hwmon *hwmon, u32 *uval)
{
@@ -787,13 +915,77 @@ static void xe_hwmon_get_voltage(struct xe_hwmon *hwmon, int channel, long *valu
*value = DIV_ROUND_CLOSEST(REG_FIELD_GET(VOLTAGE_MASK, reg_val) * 2500, SF_VOLTAGE);
}
+static inline bool is_vram_ch_available(struct xe_hwmon *hwmon, int channel)
+{
+ struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
+ int vram_id = channel - CHANNEL_VRAM_N;
+ struct xe_reg vram_reg;
+
+ vram_reg = xe_hwmon_get_reg(hwmon, REG_TEMP, channel);
+ if (!xe_reg_is_valid(vram_reg) || !xe_mmio_read32(mmio, vram_reg))
+ return false;
+
+ /* Create label only for available vram channel */
+ sprintf(hwmon->temp.vram_label[vram_id], "vram_ch_%d", vram_id);
+ return true;
+}
+
static umode_t
xe_hwmon_temp_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
{
switch (attr) {
+ case hwmon_temp_emergency:
+ switch (channel) {
+ case CHANNEL_PKG:
+ return hwmon->temp.limit[TEMP_LIMIT_PKG_SHUTDOWN] ? 0444 : 0;
+ case CHANNEL_VRAM:
+ return hwmon->temp.limit[TEMP_LIMIT_MEM_SHUTDOWN] ? 0444 : 0;
+ case CHANNEL_MCTRL:
+ case CHANNEL_PCIE:
+ return hwmon->temp.count ? 0444 : 0;
+ case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
+ return (is_vram_ch_available(hwmon, channel) &&
+ hwmon->temp.limit[TEMP_LIMIT_MEM_SHUTDOWN]) ? 0444 : 0;
+ default:
+ return 0;
+ }
+ case hwmon_temp_crit:
+ switch (channel) {
+ case CHANNEL_PKG:
+ return hwmon->temp.limit[TEMP_LIMIT_PKG_CRIT] ? 0444 : 0;
+ case CHANNEL_VRAM:
+ return hwmon->temp.limit[TEMP_LIMIT_MEM_CRIT] ? 0444 : 0;
+ case CHANNEL_MCTRL:
+ case CHANNEL_PCIE:
+ return hwmon->temp.count ? 0444 : 0;
+ case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
+ return (is_vram_ch_available(hwmon, channel) &&
+ hwmon->temp.limit[TEMP_LIMIT_MEM_CRIT]) ? 0444 : 0;
+ default:
+ return 0;
+ }
+ case hwmon_temp_max:
+ switch (channel) {
+ case CHANNEL_PKG:
+ return hwmon->temp.limit[TEMP_LIMIT_PKG_MAX] ? 0444 : 0;
+ default:
+ return 0;
+ }
case hwmon_temp_input:
case hwmon_temp_label:
- return xe_reg_is_valid(xe_hwmon_get_reg(hwmon, REG_TEMP, channel)) ? 0444 : 0;
+ switch (channel) {
+ case CHANNEL_PKG:
+ case CHANNEL_VRAM:
+ return xe_reg_is_valid(xe_hwmon_get_reg(hwmon, REG_TEMP,
+ channel)) ? 0444 : 0;
+ case CHANNEL_MCTRL:
+ case CHANNEL_PCIE:
+ return hwmon->temp.count ? 0444 : 0;
+ case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
+ return is_vram_ch_available(hwmon, channel) ? 0444 : 0;
+ default:
+ return 0;
+ }
default:
return 0;
}
@@ -807,11 +999,67 @@ xe_hwmon_temp_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
switch (attr) {
case hwmon_temp_input:
- reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_TEMP, channel));
+ switch (channel) {
+ case CHANNEL_PKG:
+ case CHANNEL_VRAM:
+ reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_TEMP, channel));
- /* HW register value is in degrees Celsius, convert to millidegrees. */
- *val = REG_FIELD_GET(TEMP_MASK, reg_val) * MILLIDEGREE_PER_DEGREE;
- return 0;
+ /* HW register value is in degrees Celsius, convert to millidegrees. */
+ *val = REG_FIELD_GET(TEMP_MASK, reg_val) * MILLIDEGREE_PER_DEGREE;
+ return 0;
+ case CHANNEL_MCTRL:
+ return get_mc_temp(hwmon, val);
+ case CHANNEL_PCIE:
+ return get_pcie_temp(hwmon, val);
+ case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
+ reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_TEMP, channel));
+ /*
+ * This temperature format is 24 bit [31:8] signed integer and 8 bit
+ * [7:0] fraction.
+ */
+ *val = (s32)(REG_FIELD_GET(TEMP_MASK_VRAM_N, reg_val)) *
+ (REG_FIELD_GET(TEMP_SIGN_MASK, reg_val) ? -1 : 1) *
+ MILLIDEGREE_PER_DEGREE;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ case hwmon_temp_emergency:
+ switch (channel) {
+ case CHANNEL_PKG:
+ case CHANNEL_MCTRL:
+ case CHANNEL_PCIE:
+ *val = hwmon->temp.limit[TEMP_LIMIT_PKG_SHUTDOWN] * MILLIDEGREE_PER_DEGREE;
+ return 0;
+ case CHANNEL_VRAM:
+ case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
+ *val = hwmon->temp.limit[TEMP_LIMIT_MEM_SHUTDOWN] * MILLIDEGREE_PER_DEGREE;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ case hwmon_temp_crit:
+ switch (channel) {
+ case CHANNEL_PKG:
+ case CHANNEL_MCTRL:
+ case CHANNEL_PCIE:
+ *val = hwmon->temp.limit[TEMP_LIMIT_PKG_CRIT] * MILLIDEGREE_PER_DEGREE;
+ return 0;
+ case CHANNEL_VRAM:
+ case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
+ *val = hwmon->temp.limit[TEMP_LIMIT_MEM_CRIT] * MILLIDEGREE_PER_DEGREE;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ case hwmon_temp_max:
+ switch (channel) {
+ case CHANNEL_PKG:
+ *val = hwmon->temp.limit[TEMP_LIMIT_PKG_MAX] * MILLIDEGREE_PER_DEGREE;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
default:
return -EOPNOTSUPP;
}
@@ -1124,37 +1372,25 @@ xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
struct xe_hwmon *hwmon = dev_get_drvdata(dev);
- int ret;
- xe_pm_runtime_get(hwmon->xe);
+ guard(xe_pm_runtime)(hwmon->xe);
switch (type) {
case hwmon_temp:
- ret = xe_hwmon_temp_read(hwmon, attr, channel, val);
- break;
+ return xe_hwmon_temp_read(hwmon, attr, channel, val);
case hwmon_power:
- ret = xe_hwmon_power_read(hwmon, attr, channel, val);
- break;
+ return xe_hwmon_power_read(hwmon, attr, channel, val);
case hwmon_curr:
- ret = xe_hwmon_curr_read(hwmon, attr, channel, val);
- break;
+ return xe_hwmon_curr_read(hwmon, attr, channel, val);
case hwmon_in:
- ret = xe_hwmon_in_read(hwmon, attr, channel, val);
- break;
+ return xe_hwmon_in_read(hwmon, attr, channel, val);
case hwmon_energy:
- ret = xe_hwmon_energy_read(hwmon, attr, channel, val);
- break;
+ return xe_hwmon_energy_read(hwmon, attr, channel, val);
case hwmon_fan:
- ret = xe_hwmon_fan_read(hwmon, attr, channel, val);
- break;
+ return xe_hwmon_fan_read(hwmon, attr, channel, val);
default:
- ret = -EOPNOTSUPP;
- break;
+ return -EOPNOTSUPP;
}
-
- xe_pm_runtime_put(hwmon->xe);
-
- return ret;
}
static int
@@ -1162,37 +1398,37 @@ xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long val)
{
struct xe_hwmon *hwmon = dev_get_drvdata(dev);
- int ret;
- xe_pm_runtime_get(hwmon->xe);
+ guard(xe_pm_runtime)(hwmon->xe);
switch (type) {
case hwmon_power:
- ret = xe_hwmon_power_write(hwmon, attr, channel, val);
- break;
+ return xe_hwmon_power_write(hwmon, attr, channel, val);
case hwmon_curr:
- ret = xe_hwmon_curr_write(hwmon, attr, channel, val);
- break;
+ return xe_hwmon_curr_write(hwmon, attr, channel, val);
default:
- ret = -EOPNOTSUPP;
- break;
+ return -EOPNOTSUPP;
}
-
- xe_pm_runtime_put(hwmon->xe);
-
- return ret;
}
static int xe_hwmon_read_label(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, const char **str)
{
+ struct xe_hwmon *hwmon = dev_get_drvdata(dev);
+
switch (type) {
case hwmon_temp:
if (channel == CHANNEL_PKG)
*str = "pkg";
else if (channel == CHANNEL_VRAM)
*str = "vram";
+ else if (channel == CHANNEL_MCTRL)
+ *str = "mctrl";
+ else if (channel == CHANNEL_PCIE)
+ *str = "pcie";
+ else if (in_range(channel, CHANNEL_VRAM_N, CHANNEL_VRAM_N_MAX))
+ *str = hwmon->temp.vram_label[channel - CHANNEL_VRAM_N];
return 0;
case hwmon_power:
case hwmon_energy:
@@ -1287,6 +1523,9 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
for (channel = 0; channel < FAN_MAX; channel++)
if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
+
+ if (hwmon->xe->info.has_mbx_thermal_info && xe_hwmon_pcode_read_thermal_info(hwmon))
+ drm_warn(&hwmon->xe->drm, "Thermal mailbox not supported by card firmware\n");
}
int xe_hwmon_register(struct xe_device *xe)
diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
index 0b5452be0c87..1e1fb72e49bf 100644
--- a/drivers/gpu/drm/xe/xe_i2c.c
+++ b/drivers/gpu/drm/xe/xe_i2c.c
@@ -5,6 +5,7 @@
* Copyright (C) 2025 Intel Corporation.
*/
+#include <drm/drm_print.h>
#include <linux/array_size.h>
#include <linux/container_of.h>
#include <linux/device.h>
@@ -26,11 +27,11 @@
#include "regs/xe_i2c_regs.h"
#include "regs/xe_irq_regs.h"
-#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_i2c.h"
#include "xe_mmio.h"
-#include "xe_platform_types.h"
+#include "xe_sriov.h"
+#include "xe_survivability_mode.h"
/**
* DOC: Xe I2C devices
@@ -213,11 +214,13 @@ static const struct irq_domain_ops xe_i2c_irq_ops = {
.map = xe_i2c_irq_map,
};
-static int xe_i2c_create_irq(struct xe_i2c *i2c)
+static int xe_i2c_create_irq(struct xe_device *xe)
{
+ struct xe_i2c *i2c = xe->i2c;
struct irq_domain *domain;
- if (!(i2c->ep.capabilities & XE_I2C_EP_CAP_IRQ))
+ if (!(i2c->ep.capabilities & XE_I2C_EP_CAP_IRQ) ||
+ xe_survivability_mode_is_boot_enabled(xe))
return 0;
domain = irq_domain_create_linear(dev_fwnode(i2c->drm_dev), 1, &xe_i2c_irq_ops, NULL);
@@ -319,7 +322,7 @@ int xe_i2c_probe(struct xe_device *xe)
struct xe_i2c *i2c;
int ret;
- if (xe->info.platform != XE_BATTLEMAGE)
+ if (!xe->info.has_i2c)
return 0;
if (IS_SRIOV_VF(xe))
@@ -351,7 +354,7 @@ int xe_i2c_probe(struct xe_device *xe)
if (ret)
return ret;
- ret = xe_i2c_create_irq(i2c);
+ ret = xe_i2c_create_irq(xe);
if (ret)
goto err_unregister_notifier;
diff --git a/drivers/gpu/drm/xe/xe_irq.c b/drivers/gpu/drm/xe/xe_irq.c
index 024e13e606ec..7560a45f7f64 100644
--- a/drivers/gpu/drm/xe/xe_irq.c
+++ b/drivers/gpu/drm/xe/xe_irq.c
@@ -10,7 +10,6 @@
#include <drm/drm_managed.h>
#include "display/xe_display.h"
-#include "regs/xe_guc_regs.h"
#include "regs/xe_irq_regs.h"
#include "xe_device.h"
#include "xe_drv.h"
@@ -21,6 +20,7 @@
#include "xe_hw_error.h"
#include "xe_i2c.h"
#include "xe_memirq.h"
+#include "xe_mert.h"
#include "xe_mmio.h"
#include "xe_pxp.h"
#include "xe_sriov.h"
@@ -525,6 +525,7 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg)
xe_heci_csc_irq_handler(xe, master_ctl);
xe_display_irq_handler(xe, master_ctl);
xe_i2c_irq_handler(xe, master_ctl);
+ xe_mert_irq_handler(xe, master_ctl);
gu_misc_iir = gu_misc_irq_ack(xe, master_ctl);
}
}
diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw_types.h b/drivers/gpu/drm/xe/xe_late_bind_fw_types.h
index 0f5da89ce98b..2a8a985c37e7 100644
--- a/drivers/gpu/drm/xe/xe_late_bind_fw_types.h
+++ b/drivers/gpu/drm/xe/xe_late_bind_fw_types.h
@@ -15,10 +15,12 @@
#define XE_LB_MAX_PAYLOAD_SIZE SZ_4K
/**
- * xe_late_bind_fw_id - enum to determine late binding fw index
+ * enum xe_late_bind_fw_id - enum to determine late binding fw index
*/
enum xe_late_bind_fw_id {
+ /** @XE_LB_FW_FAN_CONTROL: Fan control */
XE_LB_FW_FAN_CONTROL = 0,
+ /** @XE_LB_FW_MAX_ID: Number of IDs */
XE_LB_FW_MAX_ID
};
diff --git a/drivers/gpu/drm/xe/xe_lmtt.c b/drivers/gpu/drm/xe/xe_lmtt.c
index 4dc1de482eee..2077e1ef8b43 100644
--- a/drivers/gpu/drm/xe/xe_lmtt.c
+++ b/drivers/gpu/drm/xe/xe_lmtt.c
@@ -8,15 +8,18 @@
#include <drm/drm_managed.h>
#include "regs/xe_gt_regs.h"
+#include "regs/xe_mert_regs.h"
#include "xe_assert.h"
#include "xe_bo.h"
#include "xe_tlb_inval.h"
#include "xe_lmtt.h"
#include "xe_map.h"
+#include "xe_mert.h"
#include "xe_mmio.h"
#include "xe_res_cursor.h"
#include "xe_sriov.h"
+#include "xe_tile.h"
#include "xe_tile_sriov_printk.h"
/**
@@ -196,16 +199,22 @@ static void lmtt_setup_dir_ptr(struct xe_lmtt *lmtt)
struct xe_device *xe = tile_to_xe(tile);
dma_addr_t offset = xe_bo_main_addr(lmtt->pd->bo, XE_PAGE_SIZE);
struct xe_gt *gt;
+ u32 config;
u8 id;
lmtt_debug(lmtt, "DIR offset %pad\n", &offset);
lmtt_assert(lmtt, xe_bo_is_vram(lmtt->pd->bo));
lmtt_assert(lmtt, IS_ALIGNED(offset, SZ_64K));
+ config = LMEM_EN | REG_FIELD_PREP(LMTT_DIR_PTR, offset / SZ_64K);
+
for_each_gt_on_tile(gt, tile, id)
xe_mmio_write32(&gt->mmio,
GRAPHICS_VER(xe) >= 20 ? XE2_LMEM_CFG : LMEM_CFG,
- LMEM_EN | REG_FIELD_PREP(LMTT_DIR_PTR, offset / SZ_64K));
+ config);
+
+ if (xe_device_has_mert(xe) && xe_tile_is_root(tile))
+ xe_mmio_write32(&tile->mmio, MERT_LMEM_CFG, config);
}
/**
@@ -262,19 +271,29 @@ static int lmtt_invalidate_hw(struct xe_lmtt *lmtt)
* @lmtt: the &xe_lmtt to invalidate
*
* Send requests to all GuCs on this tile to invalidate all TLBs.
+ * If the platform has a standalone MERT, also invalidate MERT's TLB.
*
* This function should be called only when running as a PF driver.
*/
void xe_lmtt_invalidate_hw(struct xe_lmtt *lmtt)
{
+ struct xe_tile *tile = lmtt_to_tile(lmtt);
+ struct xe_device *xe = lmtt_to_xe(lmtt);
int err;
- lmtt_assert(lmtt, IS_SRIOV_PF(lmtt_to_xe(lmtt)));
+ lmtt_assert(lmtt, IS_SRIOV_PF(xe));
err = lmtt_invalidate_hw(lmtt);
if (err)
- xe_tile_sriov_err(lmtt_to_tile(lmtt), "LMTT invalidation failed (%pe)",
+ xe_tile_sriov_err(tile, "LMTT invalidation failed (%pe)",
ERR_PTR(err));
+
+ if (xe_device_has_mert(xe) && xe_tile_is_root(tile)) {
+ err = xe_mert_invalidate_lmtt(xe);
+ if (err)
+ xe_tile_sriov_err(tile, "MERT LMTT invalidation failed (%pe)",
+ ERR_PTR(err));
+ }
}
static void lmtt_write_pte(struct xe_lmtt *lmtt, struct xe_lmtt_pt *pt,
diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c
index b5083c99dd50..f4f31bc240d9 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -44,6 +44,11 @@
#define LRC_INDIRECT_CTX_BO_SIZE SZ_4K
#define LRC_INDIRECT_RING_STATE_SIZE SZ_4K
+#define LRC_PRIORITY GENMASK_ULL(10, 9)
+#define LRC_PRIORITY_LOW 0
+#define LRC_PRIORITY_NORMAL 1
+#define LRC_PRIORITY_HIGH 2
+
/*
* Layout of the LRC and associated data allocated as
* lrc->bo:
@@ -91,13 +96,19 @@ gt_engine_needs_indirect_ctx(struct xe_gt *gt, enum xe_engine_class class)
return false;
}
-size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class)
+/**
+ * xe_gt_lrc_hang_replay_size() - Hang replay size
+ * @gt: The GT
+ * @class: Hardware engine class
+ *
+ * Determine size of GPU hang replay state for a GT and hardware engine class.
+ *
+ * Return: Size of GPU hang replay size
+ */
+size_t xe_gt_lrc_hang_replay_size(struct xe_gt *gt, enum xe_engine_class class)
{
struct xe_device *xe = gt_to_xe(gt);
- size_t size;
-
- /* Per-process HW status page (PPHWSP) */
- size = LRC_PPHWSP_SIZE;
+ size_t size = 0;
/* Engine context image */
switch (class) {
@@ -123,11 +134,18 @@ size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class)
size += 1 * SZ_4K;
}
+ return size;
+}
+
+size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class)
+{
+ size_t size = xe_gt_lrc_hang_replay_size(gt, class);
+
/* Add indirect ring state page */
if (xe_gt_has_indirect_ring_state(gt))
size += LRC_INDIRECT_RING_STATE_SIZE;
- return size;
+ return size + LRC_PPHWSP_SIZE;
}
/*
@@ -839,7 +857,7 @@ u32 xe_lrc_ctx_timestamp_udw_ggtt_addr(struct xe_lrc *lrc)
*
* Returns: ctx timestamp value
*/
-u64 xe_lrc_ctx_timestamp(struct xe_lrc *lrc)
+static u64 xe_lrc_ctx_timestamp(struct xe_lrc *lrc)
{
struct xe_device *xe = lrc_to_xe(lrc);
struct iosys_map map;
@@ -1050,6 +1068,9 @@ static ssize_t setup_utilization_wa(struct xe_lrc *lrc,
{
u32 *cmd = batch;
+ if (IS_SRIOV_VF(gt_to_xe(lrc->gt)))
+ return 0;
+
if (xe_gt_WARN_ON(lrc->gt, max_len < 12))
return -ENOSPC;
@@ -1386,8 +1407,33 @@ setup_indirect_ctx(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
return 0;
}
+static u8 xe_multi_queue_prio_to_lrc(struct xe_lrc *lrc, enum xe_multi_queue_priority priority)
+{
+ struct xe_device *xe = gt_to_xe(lrc->gt);
+
+ xe_assert(xe, (priority >= XE_MULTI_QUEUE_PRIORITY_LOW &&
+ priority <= XE_MULTI_QUEUE_PRIORITY_HIGH));
+
+ /* xe_multi_queue_priority is directly mapped to LRC priority values */
+ return priority;
+}
+
+/**
+ * xe_lrc_set_multi_queue_priority() - Set multi queue priority in LRC
+ * @lrc: Logical Ring Context
+ * @priority: Multi queue priority of the exec queue
+ *
+ * Convert @priority to LRC multi queue priority and update the @lrc descriptor
+ */
+void xe_lrc_set_multi_queue_priority(struct xe_lrc *lrc, enum xe_multi_queue_priority priority)
+{
+ lrc->desc &= ~LRC_PRIORITY;
+ lrc->desc |= FIELD_PREP(LRC_PRIORITY, xe_multi_queue_prio_to_lrc(lrc, priority));
+}
+
static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
- struct xe_vm *vm, u32 ring_size, u16 msix_vec,
+ struct xe_vm *vm, void *replay_state, u32 ring_size,
+ u16 msix_vec,
u32 init_flags)
{
struct xe_gt *gt = hwe->gt;
@@ -1402,6 +1448,7 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
kref_init(&lrc->refcount);
lrc->gt = gt;
+ lrc->replay_size = xe_gt_lrc_hang_replay_size(gt, hwe->class);
lrc->size = lrc_size;
lrc->flags = 0;
lrc->ring.size = ring_size;
@@ -1438,11 +1485,14 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
* scratch.
*/
map = __xe_lrc_pphwsp_map(lrc);
- if (gt->default_lrc[hwe->class]) {
+ if (gt->default_lrc[hwe->class] || replay_state) {
xe_map_memset(xe, &map, 0, 0, LRC_PPHWSP_SIZE); /* PPHWSP */
xe_map_memcpy_to(xe, &map, LRC_PPHWSP_SIZE,
gt->default_lrc[hwe->class] + LRC_PPHWSP_SIZE,
lrc_size - LRC_PPHWSP_SIZE);
+ if (replay_state)
+ xe_map_memcpy_to(xe, &map, LRC_PPHWSP_SIZE,
+ replay_state, lrc->replay_size);
} else {
void *init_data = empty_lrc_data(hwe);
@@ -1550,6 +1600,7 @@ err_lrc_finish:
* xe_lrc_create - Create a LRC
* @hwe: Hardware Engine
* @vm: The VM (address space)
+ * @replay_state: GPU hang replay state
* @ring_size: LRC ring size
* @msix_vec: MSI-X interrupt vector (for platforms that support it)
* @flags: LRC initialization flags
@@ -1560,7 +1611,7 @@ err_lrc_finish:
* upon failure.
*/
struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
- u32 ring_size, u16 msix_vec, u32 flags)
+ void *replay_state, u32 ring_size, u16 msix_vec, u32 flags)
{
struct xe_lrc *lrc;
int err;
@@ -1569,7 +1620,7 @@ struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
if (!lrc)
return ERR_PTR(-ENOMEM);
- err = xe_lrc_init(lrc, hwe, vm, ring_size, msix_vec, flags);
+ err = xe_lrc_init(lrc, hwe, vm, replay_state, ring_size, msix_vec, flags);
if (err) {
kfree(lrc);
return ERR_PTR(err);
@@ -2235,6 +2286,8 @@ struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc)
snapshot->lrc_bo = xe_bo_get(lrc->bo);
snapshot->lrc_offset = xe_lrc_pphwsp_offset(lrc);
snapshot->lrc_size = lrc->size;
+ snapshot->replay_offset = 0;
+ snapshot->replay_size = lrc->replay_size;
snapshot->lrc_snapshot = NULL;
snapshot->ctx_timestamp = lower_32_bits(xe_lrc_ctx_timestamp(lrc));
snapshot->ctx_job_timestamp = xe_lrc_ctx_job_timestamp(lrc);
@@ -2305,6 +2358,9 @@ void xe_lrc_snapshot_print(struct xe_lrc_snapshot *snapshot, struct drm_printer
}
drm_printf(p, "\n\t[HWCTX].length: 0x%lx\n", snapshot->lrc_size - LRC_PPHWSP_SIZE);
+ drm_printf(p, "\n\t[HWCTX].replay_offset: 0x%lx\n", snapshot->replay_offset);
+ drm_printf(p, "\n\t[HWCTX].replay_length: 0x%lx\n", snapshot->replay_size);
+
drm_puts(p, "\t[HWCTX].data: ");
for (; i < snapshot->lrc_size; i += sizeof(u32)) {
u32 *val = snapshot->lrc_snapshot + i;
@@ -2353,35 +2409,31 @@ static int get_ctx_timestamp(struct xe_lrc *lrc, u32 engine_id, u64 *reg_ctx_ts)
}
/**
- * xe_lrc_update_timestamp() - Update ctx timestamp
+ * xe_lrc_timestamp() - Current ctx timestamp
* @lrc: Pointer to the lrc.
- * @old_ts: Old timestamp value
*
- * Populate @old_ts current saved ctx timestamp, read new ctx timestamp and
- * update saved value. With support for active contexts, the calculation may be
- * slightly racy, so follow a read-again logic to ensure that the context is
- * still active before returning the right timestamp.
+ * Return latest ctx timestamp. With support for active contexts, the
+ * calculation may bb slightly racy, so follow a read-again logic to ensure that
+ * the context is still active before returning the right timestamp.
*
* Returns: New ctx timestamp value
*/
-u64 xe_lrc_update_timestamp(struct xe_lrc *lrc, u64 *old_ts)
+u64 xe_lrc_timestamp(struct xe_lrc *lrc)
{
- u64 lrc_ts, reg_ts;
+ u64 lrc_ts, reg_ts, new_ts;
u32 engine_id;
- *old_ts = lrc->ctx_timestamp;
-
lrc_ts = xe_lrc_ctx_timestamp(lrc);
/* CTX_TIMESTAMP mmio read is invalid on VF, so return the LRC value */
if (IS_SRIOV_VF(lrc_to_xe(lrc))) {
- lrc->ctx_timestamp = lrc_ts;
+ new_ts = lrc_ts;
goto done;
}
if (lrc_ts == CONTEXT_ACTIVE) {
engine_id = xe_lrc_engine_id(lrc);
if (!get_ctx_timestamp(lrc, engine_id, &reg_ts))
- lrc->ctx_timestamp = reg_ts;
+ new_ts = reg_ts;
/* read lrc again to ensure context is still active */
lrc_ts = xe_lrc_ctx_timestamp(lrc);
@@ -2392,9 +2444,27 @@ u64 xe_lrc_update_timestamp(struct xe_lrc *lrc, u64 *old_ts)
* be a separate if condition.
*/
if (lrc_ts != CONTEXT_ACTIVE)
- lrc->ctx_timestamp = lrc_ts;
+ new_ts = lrc_ts;
done:
+ return new_ts;
+}
+
+/**
+ * xe_lrc_update_timestamp() - Update ctx timestamp
+ * @lrc: Pointer to the lrc.
+ * @old_ts: Old timestamp value
+ *
+ * Populate @old_ts current saved ctx timestamp, read new ctx timestamp and
+ * update saved value.
+ *
+ * Returns: New ctx timestamp value
+ */
+u64 xe_lrc_update_timestamp(struct xe_lrc *lrc, u64 *old_ts)
+{
+ *old_ts = lrc->ctx_timestamp;
+ lrc->ctx_timestamp = xe_lrc_timestamp(lrc);
+
trace_xe_lrc_update_timestamp(lrc, *old_ts);
return lrc->ctx_timestamp;
diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h
index 2fb628da5c43..c307a3fd9ea2 100644
--- a/drivers/gpu/drm/xe/xe_lrc.h
+++ b/drivers/gpu/drm/xe/xe_lrc.h
@@ -13,6 +13,7 @@ struct drm_printer;
struct xe_bb;
struct xe_device;
struct xe_exec_queue;
+enum xe_multi_queue_priority;
enum xe_engine_class;
struct xe_gt;
struct xe_hw_engine;
@@ -23,6 +24,7 @@ struct xe_lrc_snapshot {
struct xe_bo *lrc_bo;
void *lrc_snapshot;
unsigned long lrc_size, lrc_offset;
+ unsigned long replay_size, replay_offset;
u32 context_desc;
u32 ring_addr;
@@ -49,7 +51,7 @@ struct xe_lrc_snapshot {
#define XE_LRC_CREATE_USER_CTX BIT(2)
struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
- u32 ring_size, u16 msix_vec, u32 flags);
+ void *replay_state, u32 ring_size, u16 msix_vec, u32 flags);
void xe_lrc_destroy(struct kref *ref);
/**
@@ -86,6 +88,7 @@ static inline size_t xe_lrc_ring_size(void)
return SZ_16K;
}
+size_t xe_gt_lrc_hang_replay_size(struct xe_gt *gt, enum xe_engine_class class);
size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class);
u32 xe_lrc_pphwsp_offset(struct xe_lrc *lrc);
u32 xe_lrc_regs_offset(struct xe_lrc *lrc);
@@ -133,6 +136,8 @@ void xe_lrc_dump_default(struct drm_printer *p,
u32 *xe_lrc_emit_hwe_state_instructions(struct xe_exec_queue *q, u32 *cs);
+void xe_lrc_set_multi_queue_priority(struct xe_lrc *lrc, enum xe_multi_queue_priority priority);
+
struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc);
void xe_lrc_snapshot_capture_delayed(struct xe_lrc_snapshot *snapshot);
void xe_lrc_snapshot_print(struct xe_lrc_snapshot *snapshot, struct drm_printer *p);
@@ -140,7 +145,6 @@ void xe_lrc_snapshot_free(struct xe_lrc_snapshot *snapshot);
u32 xe_lrc_ctx_timestamp_ggtt_addr(struct xe_lrc *lrc);
u32 xe_lrc_ctx_timestamp_udw_ggtt_addr(struct xe_lrc *lrc);
-u64 xe_lrc_ctx_timestamp(struct xe_lrc *lrc);
u32 xe_lrc_ctx_job_timestamp_ggtt_addr(struct xe_lrc *lrc);
u32 xe_lrc_ctx_job_timestamp(struct xe_lrc *lrc);
int xe_lrc_setup_wa_bb_with_scratch(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
@@ -160,4 +164,6 @@ int xe_lrc_setup_wa_bb_with_scratch(struct xe_lrc *lrc, struct xe_hw_engine *hwe
*/
u64 xe_lrc_update_timestamp(struct xe_lrc *lrc, u64 *old_ts);
+u64 xe_lrc_timestamp(struct xe_lrc *lrc);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_lrc_types.h b/drivers/gpu/drm/xe/xe_lrc_types.h
index e9883706e004..a4373d280c39 100644
--- a/drivers/gpu/drm/xe/xe_lrc_types.h
+++ b/drivers/gpu/drm/xe/xe_lrc_types.h
@@ -25,6 +25,9 @@ struct xe_lrc {
/** @size: size of the lrc and optional indirect ring state */
u32 size;
+ /** @replay_size: Size LRC needed for replaying a hang */
+ u32 replay_size;
+
/** @gt: gt which this LRC belongs to */
struct xe_gt *gt;
diff --git a/drivers/gpu/drm/xe/xe_memirq.c b/drivers/gpu/drm/xe/xe_memirq.c
index b0c7ce0a5d1e..811e07136efb 100644
--- a/drivers/gpu/drm/xe/xe_memirq.c
+++ b/drivers/gpu/drm/xe/xe_memirq.c
@@ -7,7 +7,6 @@
#include "regs/xe_guc_regs.h"
#include "regs/xe_irq_regs.h"
-#include "regs/xe_regs.h"
#include "xe_assert.h"
#include "xe_bo.h"
@@ -16,7 +15,6 @@
#include "xe_gt.h"
#include "xe_guc.h"
#include "xe_hw_engine.h"
-#include "xe_map.h"
#include "xe_memirq.h"
#include "xe_tile_printk.h"
diff --git a/drivers/gpu/drm/xe/xe_mert.c b/drivers/gpu/drm/xe/xe_mert.c
new file mode 100644
index 000000000000..f637df95418b
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_mert.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright(c) 2025, Intel Corporation. All rights reserved.
+ */
+
+#include "regs/xe_irq_regs.h"
+#include "regs/xe_mert_regs.h"
+
+#include "xe_device.h"
+#include "xe_mert.h"
+#include "xe_mmio.h"
+#include "xe_sriov_printk.h"
+#include "xe_tile.h"
+
+/**
+ * xe_mert_init_early() - Initialize MERT data
+ * @xe: the &xe_device with MERT to init
+ */
+void xe_mert_init_early(struct xe_device *xe)
+{
+ struct xe_tile *tile = xe_device_get_root_tile(xe);
+ struct xe_mert *mert = &tile->mert;
+
+ spin_lock_init(&mert->lock);
+ init_completion(&mert->tlb_inv_done);
+}
+
+/**
+ * xe_mert_invalidate_lmtt() - Invalidate MERT LMTT
+ * @xe: the &xe_device with MERT
+ *
+ * Trigger invalidation of the MERT LMTT and wait for completion.
+ *
+ * Return: 0 on success or -ETIMEDOUT in case of a timeout.
+ */
+int xe_mert_invalidate_lmtt(struct xe_device *xe)
+{
+ struct xe_tile *tile = xe_device_get_root_tile(xe);
+ struct xe_mert *mert = &tile->mert;
+ const long timeout = HZ / 4;
+ unsigned long flags;
+
+ xe_assert(xe, xe_device_has_mert(xe));
+
+ spin_lock_irqsave(&mert->lock, flags);
+ if (!mert->tlb_inv_triggered) {
+ mert->tlb_inv_triggered = true;
+ reinit_completion(&mert->tlb_inv_done);
+ xe_mmio_write32(&tile->mmio, MERT_TLB_INV_DESC_A, MERT_TLB_INV_DESC_A_VALID);
+ }
+ spin_unlock_irqrestore(&mert->lock, flags);
+
+ if (!wait_for_completion_timeout(&mert->tlb_inv_done, timeout))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static void mert_handle_cat_error(struct xe_device *xe)
+{
+ struct xe_tile *tile = xe_device_get_root_tile(xe);
+ u32 reg_val, vfid, code;
+
+ reg_val = xe_mmio_read32(&tile->mmio, MERT_TLB_CT_INTR_ERR_ID_PORT);
+ if (!reg_val)
+ return;
+ xe_mmio_write32(&tile->mmio, MERT_TLB_CT_INTR_ERR_ID_PORT, 0);
+
+ vfid = FIELD_GET(CATERR_VFID, reg_val);
+ code = FIELD_GET(CATERR_CODES, reg_val);
+
+ switch (code) {
+ case CATERR_NO_ERROR:
+ break;
+ case CATERR_UNMAPPED_GGTT:
+ xe_sriov_err(xe, "MERT: CAT_ERR: Access to an unmapped GGTT!\n");
+ xe_device_declare_wedged(xe);
+ break;
+ case CATERR_LMTT_FAULT:
+ xe_sriov_dbg(xe, "MERT: CAT_ERR: VF%u LMTT fault!\n", vfid);
+ /* XXX: track/report malicious VF activity */
+ break;
+ default:
+ xe_sriov_err(xe, "MERT: Unexpected CAT_ERR code=%#x!\n", code);
+ xe_device_declare_wedged(xe);
+ break;
+ }
+}
+
+/**
+ * xe_mert_irq_handler - Handler for MERT interrupts
+ * @xe: the &xe_device
+ * @master_ctl: interrupt register
+ *
+ * Handle interrupts generated by MERT.
+ */
+void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl)
+{
+ struct xe_tile *tile = xe_device_get_root_tile(xe);
+ struct xe_mert *mert = &tile->mert;
+ unsigned long flags;
+ u32 reg_val;
+
+ if (!(master_ctl & SOC_H2DMEMINT_IRQ))
+ return;
+
+ mert_handle_cat_error(xe);
+
+ spin_lock_irqsave(&mert->lock, flags);
+ if (mert->tlb_inv_triggered) {
+ reg_val = xe_mmio_read32(&tile->mmio, MERT_TLB_INV_DESC_A);
+ if (!(reg_val & MERT_TLB_INV_DESC_A_VALID)) {
+ mert->tlb_inv_triggered = false;
+ complete_all(&mert->tlb_inv_done);
+ }
+ }
+ spin_unlock_irqrestore(&mert->lock, flags);
+}
diff --git a/drivers/gpu/drm/xe/xe_mert.h b/drivers/gpu/drm/xe/xe_mert.h
new file mode 100644
index 000000000000..fc977203692d
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_mert.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright(c) 2025, Intel Corporation. All rights reserved.
+ */
+
+#ifndef _XE_MERT_H_
+#define _XE_MERT_H_
+
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+struct xe_device;
+
+/**
+ * struct xe_mert - MERT related data
+ */
+struct xe_mert {
+ /** @lock: protects the TLB invalidation status */
+ spinlock_t lock;
+ /** @tlb_inv_triggered: indicates if TLB invalidation was triggered */
+ bool tlb_inv_triggered;
+ /** @tlb_inv_done: completion of TLB invalidation */
+ struct completion tlb_inv_done;
+};
+
+#ifdef CONFIG_PCI_IOV
+void xe_mert_init_early(struct xe_device *xe);
+int xe_mert_invalidate_lmtt(struct xe_device *xe);
+void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl);
+#else
+static inline void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl) { }
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c
index 2184af413b91..6e202428aac2 100644
--- a/drivers/gpu/drm/xe/xe_migrate.c
+++ b/drivers/gpu/drm/xe/xe_migrate.c
@@ -34,6 +34,8 @@
#include "xe_res_cursor.h"
#include "xe_sa.h"
#include "xe_sched_job.h"
+#include "xe_sriov_vf_ccs.h"
+#include "xe_svm.h"
#include "xe_sync.h"
#include "xe_trace_bo.h"
#include "xe_validation.h"
@@ -470,7 +472,8 @@ int xe_migrate_init(struct xe_migrate *m)
EXEC_QUEUE_FLAG_KERNEL |
EXEC_QUEUE_FLAG_PERMANENT |
EXEC_QUEUE_FLAG_HIGH_PRIORITY |
- EXEC_QUEUE_FLAG_MIGRATE, 0);
+ EXEC_QUEUE_FLAG_MIGRATE |
+ EXEC_QUEUE_FLAG_LOW_LATENCY, 0);
} else {
m->q = xe_exec_queue_create_class(xe, primary_gt, vm,
XE_ENGINE_CLASS_COPY,
@@ -1103,12 +1106,16 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
u32 batch_size, batch_size_allocated;
struct xe_device *xe = gt_to_xe(gt);
struct xe_res_cursor src_it, ccs_it;
+ struct xe_sriov_vf_ccs_ctx *ctx;
+ struct xe_sa_manager *bb_pool;
u64 size = xe_bo_size(src_bo);
struct xe_bb *bb = NULL;
u64 src_L0, src_L0_ofs;
u32 src_L0_pt;
int err;
+ ctx = &xe->sriov.vf.ccs.contexts[read_write];
+
xe_res_first_sg(xe_bo_sg(src_bo), 0, size, &src_it);
xe_res_first_sg(xe_bo_sg(src_bo), xe_bo_ccs_pages_start(src_bo),
@@ -1141,11 +1148,15 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
size -= src_L0;
}
+ bb_pool = ctx->mem.ccs_bb_pool;
+ guard(mutex) (xe_sa_bo_swap_guard(bb_pool));
+ xe_sa_bo_swap_shadow(bb_pool);
+
bb = xe_bb_ccs_new(gt, batch_size, read_write);
if (IS_ERR(bb)) {
drm_err(&xe->drm, "BB allocation failed.\n");
err = PTR_ERR(bb);
- goto err_ret;
+ return err;
}
batch_size_allocated = batch_size;
@@ -1194,10 +1205,52 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
xe_assert(xe, (batch_size_allocated == bb->len));
src_bo->bb_ccs[read_write] = bb;
+ xe_sriov_vf_ccs_rw_update_bb_addr(ctx);
+ xe_sa_bo_sync_shadow(bb->bo);
return 0;
+}
-err_ret:
- return err;
+/**
+ * xe_migrate_ccs_rw_copy_clear() - Clear the CCS read/write batch buffer
+ * content.
+ * @src_bo: The buffer object @src is currently bound to.
+ * @read_write : Creates BB commands for CCS read/write.
+ *
+ * Directly clearing the BB lacks atomicity and can lead to undefined
+ * behavior if the vCPU is halted mid-operation during the clearing
+ * process. To avoid this issue, we use a shadow buffer object approach.
+ *
+ * First swap the SA BO address with the shadow BO, perform the clearing
+ * operation on the BB, update the shadow BO in the ring buffer, then
+ * sync the shadow and the actual buffer to maintain consistency.
+ *
+ * Returns: None.
+ */
+void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
+ enum xe_sriov_vf_ccs_rw_ctxs read_write)
+{
+ struct xe_bb *bb = src_bo->bb_ccs[read_write];
+ struct xe_device *xe = xe_bo_device(src_bo);
+ struct xe_sriov_vf_ccs_ctx *ctx;
+ struct xe_sa_manager *bb_pool;
+ u32 *cs;
+
+ xe_assert(xe, IS_SRIOV_VF(xe));
+
+ ctx = &xe->sriov.vf.ccs.contexts[read_write];
+ bb_pool = ctx->mem.ccs_bb_pool;
+
+ guard(mutex) (xe_sa_bo_swap_guard(bb_pool));
+ xe_sa_bo_swap_shadow(bb_pool);
+
+ cs = xe_sa_bo_cpu_addr(bb->bo);
+ memset(cs, MI_NOOP, bb->len * sizeof(u32));
+ xe_sriov_vf_ccs_rw_update_bb_addr(ctx);
+
+ xe_sa_bo_sync_shadow(bb->bo);
+
+ xe_bb_free(bb, NULL);
+ src_bo->bb_ccs[read_write] = NULL;
}
/**
@@ -1997,7 +2050,8 @@ static void build_pt_update_batch_sram(struct xe_migrate *m,
u64 pte;
xe_tile_assert(m->tile, sram_addr[i].proto ==
- DRM_INTERCONNECT_SYSTEM);
+ DRM_INTERCONNECT_SYSTEM ||
+ sram_addr[i].proto == XE_INTERCONNECT_P2P);
xe_tile_assert(m->tile, addr);
xe_tile_assert(m->tile, PAGE_ALIGNED(addr));
@@ -2062,6 +2116,7 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m,
unsigned long sram_offset,
struct drm_pagemap_addr *sram_addr,
u64 vram_addr,
+ struct dma_fence *deps,
const enum xe_migrate_copy_dir dir)
{
struct xe_gt *gt = m->tile->primary_gt;
@@ -2150,6 +2205,14 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m,
xe_sched_job_add_migrate_flush(job, MI_INVALIDATE_TLB);
+ if (deps && !dma_fence_is_signaled(deps)) {
+ dma_fence_get(deps);
+ err = drm_sched_job_add_dependency(&job->drm, deps);
+ if (err)
+ dma_fence_wait(deps, false);
+ err = 0;
+ }
+
mutex_lock(&m->job_mutex);
xe_sched_job_arm(job);
fence = dma_fence_get(&job->drm.s_fence->finished);
@@ -2175,6 +2238,8 @@ err:
* @npages: Number of pages to migrate.
* @src_addr: Array of DMA information (source of migrate)
* @dst_addr: Device physical address of VRAM (destination of migrate)
+ * @deps: struct dma_fence representing the dependencies that need
+ * to be signaled before migration.
*
* Copy from an array dma addresses to a VRAM device physical address
*
@@ -2184,10 +2249,11 @@ err:
struct dma_fence *xe_migrate_to_vram(struct xe_migrate *m,
unsigned long npages,
struct drm_pagemap_addr *src_addr,
- u64 dst_addr)
+ u64 dst_addr,
+ struct dma_fence *deps)
{
return xe_migrate_vram(m, npages * PAGE_SIZE, 0, src_addr, dst_addr,
- XE_MIGRATE_COPY_TO_VRAM);
+ deps, XE_MIGRATE_COPY_TO_VRAM);
}
/**
@@ -2196,6 +2262,8 @@ struct dma_fence *xe_migrate_to_vram(struct xe_migrate *m,
* @npages: Number of pages to migrate.
* @src_addr: Device physical address of VRAM (source of migrate)
* @dst_addr: Array of DMA information (destination of migrate)
+ * @deps: struct dma_fence representing the dependencies that need
+ * to be signaled before migration.
*
* Copy from a VRAM device physical address to an array dma addresses
*
@@ -2205,10 +2273,11 @@ struct dma_fence *xe_migrate_to_vram(struct xe_migrate *m,
struct dma_fence *xe_migrate_from_vram(struct xe_migrate *m,
unsigned long npages,
u64 src_addr,
- struct drm_pagemap_addr *dst_addr)
+ struct drm_pagemap_addr *dst_addr,
+ struct dma_fence *deps)
{
return xe_migrate_vram(m, npages * PAGE_SIZE, 0, dst_addr, src_addr,
- XE_MIGRATE_COPY_TO_SRAM);
+ deps, XE_MIGRATE_COPY_TO_SRAM);
}
static void xe_migrate_dma_unmap(struct xe_device *xe,
@@ -2384,7 +2453,7 @@ int xe_migrate_access_memory(struct xe_migrate *m, struct xe_bo *bo,
__fence = xe_migrate_vram(m, current_bytes,
(unsigned long)buf & ~PAGE_MASK,
&pagemap_addr[current_page],
- vram_addr, write ?
+ vram_addr, NULL, write ?
XE_MIGRATE_COPY_TO_VRAM :
XE_MIGRATE_COPY_TO_SRAM);
if (IS_ERR(__fence)) {
@@ -2430,7 +2499,7 @@ void xe_migrate_job_lock(struct xe_migrate *m, struct xe_exec_queue *q)
if (is_migrate)
mutex_lock(&m->job_mutex);
else
- xe_vm_assert_held(q->vm); /* User queues VM's should be locked */
+ xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */
}
/**
@@ -2448,7 +2517,7 @@ void xe_migrate_job_unlock(struct xe_migrate *m, struct xe_exec_queue *q)
if (is_migrate)
mutex_unlock(&m->job_mutex);
else
- xe_vm_assert_held(q->vm); /* User queues VM's should be locked */
+ xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */
}
#if IS_ENABLED(CONFIG_PROVE_LOCKING)
diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h
index 260e298e5dd7..1522afb37dcf 100644
--- a/drivers/gpu/drm/xe/xe_migrate.h
+++ b/drivers/gpu/drm/xe/xe_migrate.h
@@ -116,12 +116,14 @@ int xe_migrate_init(struct xe_migrate *m);
struct dma_fence *xe_migrate_to_vram(struct xe_migrate *m,
unsigned long npages,
struct drm_pagemap_addr *src_addr,
- u64 dst_addr);
+ u64 dst_addr,
+ struct dma_fence *deps);
struct dma_fence *xe_migrate_from_vram(struct xe_migrate *m,
unsigned long npages,
u64 src_addr,
- struct drm_pagemap_addr *dst_addr);
+ struct drm_pagemap_addr *dst_addr,
+ struct dma_fence *deps);
struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
struct xe_bo *src_bo,
@@ -134,6 +136,9 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
struct xe_bo *src_bo,
enum xe_sriov_vf_ccs_rw_ctxs read_write);
+void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
+ enum xe_sriov_vf_ccs_rw_ctxs read_write);
+
struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate);
struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate);
struct dma_fence *xe_migrate_vram_copy_chunk(struct xe_bo *vram_bo, u64 vram_offset,
diff --git a/drivers/gpu/drm/xe/xe_mmio.c b/drivers/gpu/drm/xe/xe_mmio.c
index 350dca1f0925..bcb6674b7dac 100644
--- a/drivers/gpu/drm/xe/xe_mmio.c
+++ b/drivers/gpu/drm/xe/xe_mmio.c
@@ -14,12 +14,8 @@
#include <drm/drm_print.h>
#include "regs/xe_bars.h"
-#include "regs/xe_regs.h"
#include "xe_device.h"
-#include "xe_gt.h"
-#include "xe_gt_printk.h"
#include "xe_gt_sriov_vf.h"
-#include "xe_macros.h"
#include "xe_sriov.h"
#include "xe_trace.h"
#include "xe_wa.h"
diff --git a/drivers/gpu/drm/xe/xe_mocs.c b/drivers/gpu/drm/xe/xe_mocs.c
index 6613d3b48a84..54822497c21e 100644
--- a/drivers/gpu/drm/xe/xe_mocs.c
+++ b/drivers/gpu/drm/xe/xe_mocs.c
@@ -6,7 +6,6 @@
#include "xe_mocs.h"
#include "regs/xe_gt_regs.h"
-#include "xe_bo.h"
#include "xe_device.h"
#include "xe_exec_queue.h"
#include "xe_force_wake.h"
@@ -17,7 +16,6 @@
#include "xe_platform_types.h"
#include "xe_pm.h"
#include "xe_sriov.h"
-#include "xe_step_types.h"
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
#define mocs_dbg xe_gt_dbg
@@ -811,26 +809,20 @@ int xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p)
struct xe_device *xe = gt_to_xe(gt);
enum xe_force_wake_domains domain;
struct xe_mocs_info table;
- unsigned int fw_ref, flags;
- int err = 0;
+ unsigned int flags;
flags = get_mocs_settings(xe, &table);
domain = flags & HAS_LNCF_MOCS ? XE_FORCEWAKE_ALL : XE_FW_GT;
- xe_pm_runtime_get_noresume(xe);
- fw_ref = xe_force_wake_get(gt_to_fw(gt), domain);
- if (!xe_force_wake_ref_has_domain(fw_ref, domain)) {
- err = -ETIMEDOUT;
- goto err_fw;
- }
+ guard(xe_pm_runtime_noresume)(xe);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), domain);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, domain))
+ return -ETIMEDOUT;
table.ops->dump(&table, flags, gt, p);
-err_fw:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
- xe_pm_runtime_put(xe);
- return err;
+ return 0;
}
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c
index d08338fc3bc1..a0048f64ed12 100644
--- a/drivers/gpu/drm/xe/xe_module.c
+++ b/drivers/gpu/drm/xe/xe_module.c
@@ -10,6 +10,7 @@
#include <drm/drm_module.h>
+#include "xe_device_types.h"
#include "xe_drv.h"
#include "xe_configfs.h"
#include "xe_hw_fence.h"
@@ -29,7 +30,8 @@
#define DEFAULT_FORCE_PROBE CONFIG_DRM_XE_FORCE_PROBE
#define DEFAULT_MAX_VFS ~0
#define DEFAULT_MAX_VFS_STR "unlimited"
-#define DEFAULT_WEDGED_MODE 1
+#define DEFAULT_WEDGED_MODE XE_WEDGED_MODE_DEFAULT
+#define DEFAULT_WEDGED_MODE_STR XE_WEDGED_MODE_DEFAULT_STR
#define DEFAULT_SVM_NOTIFIER_SIZE 512
struct xe_modparam xe_modparam = {
@@ -88,10 +90,10 @@ MODULE_PARM_DESC(max_vfs,
"[default=" DEFAULT_MAX_VFS_STR "])");
#endif
-module_param_named_unsafe(wedged_mode, xe_modparam.wedged_mode, int, 0600);
+module_param_named_unsafe(wedged_mode, xe_modparam.wedged_mode, uint, 0600);
MODULE_PARM_DESC(wedged_mode,
- "Module's default policy for the wedged mode (0=never, 1=upon-critical-errors, 2=upon-any-hang "
- "[default=" __stringify(DEFAULT_WEDGED_MODE) "])");
+ "Module's default policy for the wedged mode (0=never, 1=upon-critical-error, 2=upon-any-hang-no-reset "
+ "[default=" DEFAULT_WEDGED_MODE_STR "])");
static int xe_check_nomodeset(void)
{
diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h
index 5a3bfea8b7b4..1c75f38ca393 100644
--- a/drivers/gpu/drm/xe/xe_module.h
+++ b/drivers/gpu/drm/xe/xe_module.h
@@ -21,7 +21,7 @@ struct xe_modparam {
#ifdef CONFIG_PCI_IOV
unsigned int max_vfs;
#endif
- int wedged_mode;
+ unsigned int wedged_mode;
u32 svm_notifier_size;
};
diff --git a/drivers/gpu/drm/xe/xe_nvm.c b/drivers/gpu/drm/xe/xe_nvm.c
index 33f4ac82fc80..437375046517 100644
--- a/drivers/gpu/drm/xe/xe_nvm.c
+++ b/drivers/gpu/drm/xe/xe_nvm.c
@@ -6,10 +6,10 @@
#include <linux/intel_dg_nvm_aux.h>
#include <linux/pci.h>
-#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_mmio.h"
#include "xe_nvm.h"
+#include "xe_pcode_api.h"
#include "regs/xe_gsc_regs.h"
#include "xe_sriov.h"
@@ -45,39 +45,50 @@ static bool xe_nvm_non_posted_erase(struct xe_device *xe)
{
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
- if (xe->info.platform != XE_BATTLEMAGE)
+ switch (xe->info.platform) {
+ case XE_CRESCENTISLAND:
+ case XE_BATTLEMAGE:
+ return !(xe_mmio_read32(mmio, XE_REG(GEN12_CNTL_PROTECTED_NVM_REG)) &
+ NVM_NON_POSTED_ERASE_CHICKEN_BIT);
+ default:
return false;
- return !(xe_mmio_read32(mmio, XE_REG(GEN12_CNTL_PROTECTED_NVM_REG)) &
- NVM_NON_POSTED_ERASE_CHICKEN_BIT);
+ }
}
static bool xe_nvm_writable_override(struct xe_device *xe)
{
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
bool writable_override;
- resource_size_t base;
+ struct xe_reg reg;
+ u32 test_bit;
switch (xe->info.platform) {
+ case XE_CRESCENTISLAND:
+ reg = PCODE_SCRATCH(0);
+ test_bit = FDO_MODE;
+ break;
case XE_BATTLEMAGE:
- base = DG2_GSC_HECI2_BASE;
+ reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE);
+ test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
break;
case XE_PVC:
- base = PVC_GSC_HECI2_BASE;
+ reg = HECI_FWSTS2(PVC_GSC_HECI2_BASE);
+ test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
break;
case XE_DG2:
- base = DG2_GSC_HECI2_BASE;
+ reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE);
+ test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
break;
case XE_DG1:
- base = DG1_GSC_HECI2_BASE;
+ reg = HECI_FWSTS2(DG1_GSC_HECI2_BASE);
+ test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
break;
default:
drm_err(&xe->drm, "Unknown platform\n");
return true;
}
- writable_override =
- !(xe_mmio_read32(mmio, HECI_FWSTS2(base)) &
- HECI_FW_STATUS_2_NVM_ACCESS_MODE);
+ writable_override = !(xe_mmio_read32(mmio, reg) & test_bit);
if (writable_override)
drm_info(&xe->drm, "NVM access overridden by jumper\n");
return writable_override;
diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c
index 890c363282ae..abf87fe0b345 100644
--- a/drivers/gpu/drm/xe/xe_oa.c
+++ b/drivers/gpu/drm/xe/xe_oa.c
@@ -1105,11 +1105,12 @@ static int xe_oa_enable_metric_set(struct xe_oa_stream *stream)
oag_buf_size_select(stream) |
oag_configure_mmio_trigger(stream, true));
- xe_mmio_write32(mmio, __oa_regs(stream)->oa_ctx_ctrl, stream->periodic ?
- (OAG_OAGLBCTXCTRL_COUNTER_RESUME |
+ xe_mmio_write32(mmio, __oa_regs(stream)->oa_ctx_ctrl,
+ OAG_OAGLBCTXCTRL_COUNTER_RESUME |
+ (stream->periodic ?
OAG_OAGLBCTXCTRL_TIMER_ENABLE |
REG_FIELD_PREP(OAG_OAGLBCTXCTRL_TIMER_PERIOD_MASK,
- stream->period_exponent)) : 0);
+ stream->period_exponent) : 0));
/*
* Initialize Super Queue Internal Cnt Register
@@ -1254,6 +1255,9 @@ static int xe_oa_set_no_preempt(struct xe_oa *oa, u64 value,
static int xe_oa_set_prop_num_syncs(struct xe_oa *oa, u64 value,
struct xe_oa_open_param *param)
{
+ if (XE_IOCTL_DBG(oa->xe, value > DRM_XE_MAX_SYNCS))
+ return -EINVAL;
+
param->num_syncs = value;
return 0;
}
@@ -1343,7 +1347,7 @@ static int xe_oa_user_ext_set_property(struct xe_oa *oa, enum xe_oa_user_extn_fr
ARRAY_SIZE(xe_oa_set_property_funcs_config));
if (XE_IOCTL_DBG(oa->xe, ext.property >= ARRAY_SIZE(xe_oa_set_property_funcs_open)) ||
- XE_IOCTL_DBG(oa->xe, ext.pad))
+ XE_IOCTL_DBG(oa->xe, !ext.property) || XE_IOCTL_DBG(oa->xe, ext.pad))
return -EINVAL;
idx = array_index_nospec(ext.property, ARRAY_SIZE(xe_oa_set_property_funcs_open));
@@ -1937,6 +1941,7 @@ static bool oa_unit_supports_oa_format(struct xe_oa_open_param *param, int type)
type == DRM_XE_OA_FMT_TYPE_OAC || type == DRM_XE_OA_FMT_TYPE_PEC;
case DRM_XE_OA_UNIT_TYPE_OAM:
case DRM_XE_OA_UNIT_TYPE_OAM_SAG:
+ case DRM_XE_OA_UNIT_TYPE_MERT:
return type == DRM_XE_OA_FMT_TYPE_OAM || type == DRM_XE_OA_FMT_TYPE_OAM_MPEC;
default:
return false;
@@ -1962,10 +1967,6 @@ static int xe_oa_assign_hwe(struct xe_oa *oa, struct xe_oa_open_param *param)
enum xe_hw_engine_id id;
int ret = 0;
- /* If not provided, OA unit defaults to OA unit 0 as per uapi */
- if (!param->oa_unit)
- param->oa_unit = &xe_root_mmio_gt(oa->xe)->oa.oa_unit[0];
-
/* When we have an exec_q, get hwe from the exec_q */
if (param->exec_q) {
param->hwe = xe_gt_hw_engine(param->exec_q->gt, param->exec_q->class,
@@ -2031,7 +2032,15 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f
if (ret)
return ret;
+ /* If not provided, OA unit defaults to OA unit 0 as per uapi */
+ if (!param.oa_unit)
+ param.oa_unit = &xe_root_mmio_gt(oa->xe)->oa.oa_unit[0];
+
if (param.exec_queue_id > 0) {
+ /* An exec_queue is only needed for OAR/OAC functionality on OAG */
+ if (XE_IOCTL_DBG(oa->xe, param.oa_unit->type != DRM_XE_OA_UNIT_TYPE_OAG))
+ return -EINVAL;
+
param.exec_q = xe_exec_queue_lookup(xef, param.exec_queue_id);
if (XE_IOCTL_DBG(oa->xe, !param.exec_q))
return -ENOENT;
@@ -2220,6 +2229,8 @@ static const struct xe_mmio_range xe2_oa_mux_regs[] = {
{ .start = 0xE18C, .end = 0xE18C }, /* SAMPLER_MODE */
{ .start = 0xE590, .end = 0xE590 }, /* TDL_LSC_LAT_MEASURE_TDL_GFX */
{ .start = 0x13000, .end = 0x137FC }, /* PES_0_PESL0 - PES_63_UPPER_PESL3 */
+ { .start = 0x145194, .end = 0x145194 }, /* SYS_MEM_LAT_MEASURE */
+ { .start = 0x145340, .end = 0x14537C }, /* MERTSS_PES_0 - MERTSS_PES_7 */
{},
};
@@ -2511,7 +2522,12 @@ int xe_oa_register(struct xe_device *xe)
static u32 num_oa_units_per_gt(struct xe_gt *gt)
{
if (xe_gt_is_main_type(gt) || GRAPHICS_VER(gt_to_xe(gt)) < 20)
- return 1;
+ /*
+ * Mert OA unit belongs to the SoC, not a gt, so should be accessed using
+ * xe_root_tile_mmio(). However, for all known platforms this is the same as
+ * accessing via xe_root_mmio_gt()->mmio.
+ */
+ return xe_device_has_mert(gt_to_xe(gt)) ? 2 : 1;
else if (!IS_DGFX(gt_to_xe(gt)))
return XE_OAM_UNIT_SCMI_0 + 1; /* SAG + SCMI_0 */
else
@@ -2566,40 +2582,57 @@ static u32 __hwe_oa_unit(struct xe_hw_engine *hwe)
static struct xe_oa_regs __oam_regs(u32 base)
{
return (struct xe_oa_regs) {
- base,
- OAM_HEAD_POINTER(base),
- OAM_TAIL_POINTER(base),
- OAM_BUFFER(base),
- OAM_CONTEXT_CONTROL(base),
- OAM_CONTROL(base),
- OAM_DEBUG(base),
- OAM_STATUS(base),
- OAM_CONTROL_COUNTER_SEL_MASK,
+ .base = base,
+ .oa_head_ptr = OAM_HEAD_POINTER(base),
+ .oa_tail_ptr = OAM_TAIL_POINTER(base),
+ .oa_buffer = OAM_BUFFER(base),
+ .oa_ctx_ctrl = OAM_CONTEXT_CONTROL(base),
+ .oa_ctrl = OAM_CONTROL(base),
+ .oa_debug = OAM_DEBUG(base),
+ .oa_status = OAM_STATUS(base),
+ .oa_mmio_trg = OAM_MMIO_TRG(base),
+ .oa_ctrl_counter_select_mask = OAM_CONTROL_COUNTER_SEL_MASK,
};
}
static struct xe_oa_regs __oag_regs(void)
{
return (struct xe_oa_regs) {
- 0,
- OAG_OAHEADPTR,
- OAG_OATAILPTR,
- OAG_OABUFFER,
- OAG_OAGLBCTXCTRL,
- OAG_OACONTROL,
- OAG_OA_DEBUG,
- OAG_OASTATUS,
- OAG_OACONTROL_OA_COUNTER_SEL_MASK,
+ .base = 0,
+ .oa_head_ptr = OAG_OAHEADPTR,
+ .oa_tail_ptr = OAG_OATAILPTR,
+ .oa_buffer = OAG_OABUFFER,
+ .oa_ctx_ctrl = OAG_OAGLBCTXCTRL,
+ .oa_ctrl = OAG_OACONTROL,
+ .oa_debug = OAG_OA_DEBUG,
+ .oa_status = OAG_OASTATUS,
+ .oa_mmio_trg = OAG_MMIOTRIGGER,
+ .oa_ctrl_counter_select_mask = OAG_OACONTROL_OA_COUNTER_SEL_MASK,
+ };
+}
+
+static struct xe_oa_regs __oamert_regs(void)
+{
+ return (struct xe_oa_regs) {
+ .base = 0,
+ .oa_head_ptr = OAMERT_HEAD_POINTER,
+ .oa_tail_ptr = OAMERT_TAIL_POINTER,
+ .oa_buffer = OAMERT_BUFFER,
+ .oa_ctx_ctrl = OAMERT_CONTEXT_CONTROL,
+ .oa_ctrl = OAMERT_CONTROL,
+ .oa_debug = OAMERT_DEBUG,
+ .oa_status = OAMERT_STATUS,
+ .oa_mmio_trg = OAMERT_MMIO_TRG,
+ .oa_ctrl_counter_select_mask = OAM_CONTROL_COUNTER_SEL_MASK,
};
}
static void __xe_oa_init_oa_units(struct xe_gt *gt)
{
- /* Actual address is MEDIA_GT_GSI_OFFSET + oam_base_addr[i] */
const u32 oam_base_addr[] = {
- [XE_OAM_UNIT_SAG] = 0x13000,
- [XE_OAM_UNIT_SCMI_0] = 0x14000,
- [XE_OAM_UNIT_SCMI_1] = 0x14800,
+ [XE_OAM_UNIT_SAG] = XE_OAM_SAG_BASE,
+ [XE_OAM_UNIT_SCMI_0] = XE_OAM_SCMI_0_BASE,
+ [XE_OAM_UNIT_SCMI_1] = XE_OAM_SCMI_1_BASE,
};
int i, num_units = gt->oa.num_oa_units;
@@ -2607,8 +2640,15 @@ static void __xe_oa_init_oa_units(struct xe_gt *gt)
struct xe_oa_unit *u = &gt->oa.oa_unit[i];
if (xe_gt_is_main_type(gt)) {
- u->regs = __oag_regs();
- u->type = DRM_XE_OA_UNIT_TYPE_OAG;
+ if (!i) {
+ u->regs = __oag_regs();
+ u->type = DRM_XE_OA_UNIT_TYPE_OAG;
+ } else {
+ xe_gt_assert(gt, xe_device_has_mert(gt_to_xe(gt)));
+ xe_gt_assert(gt, gt == xe_root_mmio_gt(gt_to_xe(gt)));
+ u->regs = __oamert_regs();
+ u->type = DRM_XE_OA_UNIT_TYPE_MERT;
+ }
} else {
xe_gt_assert(gt, GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270);
u->regs = __oam_regs(oam_base_addr[i]);
diff --git a/drivers/gpu/drm/xe/xe_oa_types.h b/drivers/gpu/drm/xe/xe_oa_types.h
index cf080f412189..08cc8d7c2215 100644
--- a/drivers/gpu/drm/xe/xe_oa_types.h
+++ b/drivers/gpu/drm/xe/xe_oa_types.h
@@ -87,6 +87,7 @@ struct xe_oa_regs {
struct xe_reg oa_ctrl;
struct xe_reg oa_debug;
struct xe_reg oa_status;
+ struct xe_reg oa_mmio_trg;
u32 oa_ctrl_counter_select_mask;
};
diff --git a/drivers/gpu/drm/xe/xe_page_reclaim.c b/drivers/gpu/drm/xe/xe_page_reclaim.c
new file mode 100644
index 000000000000..e13c71a89da2
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_page_reclaim.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/kref.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#include "xe_page_reclaim.h"
+
+#include "xe_gt_stats.h"
+#include "xe_macros.h"
+#include "xe_pat.h"
+#include "xe_sa.h"
+#include "xe_tlb_inval_types.h"
+
+/**
+ * xe_page_reclaim_skip() - Decide whether PRL should be skipped for a VMA
+ * @tile: Tile owning the VMA
+ * @vma: VMA under consideration
+ *
+ * PPC flushing may be handled by HW for specific PAT encodings.
+ * Skip PPC flushing/Page Reclaim for scenarios below due to redundant
+ * flushes.
+ * - pat_index is transient display (1)
+ *
+ * Return: true when page reclamation is unnecessary, false otherwise.
+ */
+bool xe_page_reclaim_skip(struct xe_tile *tile, struct xe_vma *vma)
+{
+ u8 l3_policy;
+
+ l3_policy = xe_pat_index_get_l3_policy(tile->xe, vma->attr.pat_index);
+
+ /*
+ * - l3_policy: 0=WB, 1=XD ("WB - Transient Display"), 3=UC
+ * Transient display flushes is taken care by HW, l3_policy = 1.
+ *
+ * HW will sequence these transient flushes at various sync points so
+ * any event of page reclamation will hit these sync points before
+ * page reclamation could execute.
+ */
+ return (l3_policy == XE_L3_POLICY_XD);
+}
+
+/**
+ * xe_page_reclaim_create_prl_bo() - Back a PRL with a suballocated GGTT BO
+ * @tlb_inval: TLB invalidation frontend associated with the request
+ * @prl: page reclaim list data that bo will copy from
+ * @fence: tlb invalidation fence that page reclaim action is paired to
+ *
+ * Suballocates a 4K BO out of the tile reclaim pool, copies the PRL CPU
+ * copy into the BO and queues the buffer for release when @fence signals.
+ *
+ * Return: struct drm_suballoc pointer on success or ERR_PTR on failure.
+ */
+struct drm_suballoc *xe_page_reclaim_create_prl_bo(struct xe_tlb_inval *tlb_inval,
+ struct xe_page_reclaim_list *prl,
+ struct xe_tlb_inval_fence *fence)
+{
+ struct xe_gt *gt = container_of(tlb_inval, struct xe_gt, tlb_inval);
+ struct xe_tile *tile = gt_to_tile(gt);
+ /* (+1) for NULL page_reclaim_entry to indicate end of list */
+ int prl_size = min(prl->num_entries + 1, XE_PAGE_RECLAIM_MAX_ENTRIES) *
+ sizeof(struct xe_guc_page_reclaim_entry);
+ struct drm_suballoc *prl_sa;
+
+ /* Maximum size of PRL is 1 4K-page */
+ prl_sa = __xe_sa_bo_new(tile->mem.reclaim_pool,
+ prl_size, GFP_ATOMIC);
+ if (IS_ERR(prl_sa))
+ return prl_sa;
+
+ memcpy(xe_sa_bo_cpu_addr(prl_sa), prl->entries,
+ prl_size);
+ xe_sa_bo_flush_write(prl_sa);
+ /* Queue up sa_bo_free on tlb invalidation fence signal */
+ xe_sa_bo_free(prl_sa, &fence->base);
+
+ return prl_sa;
+}
+
+/**
+ * xe_page_reclaim_list_invalidate() - Mark a PRL as invalid
+ * @prl: Page reclaim list to reset
+ *
+ * Clears the entries pointer and marks the list as invalid so
+ * future use knows PRL is unusable. It is expected that the entries
+ * have already been released.
+ */
+void xe_page_reclaim_list_invalidate(struct xe_page_reclaim_list *prl)
+{
+ xe_page_reclaim_entries_put(prl->entries);
+ prl->entries = NULL;
+ prl->num_entries = XE_PAGE_RECLAIM_INVALID_LIST;
+}
+
+/**
+ * xe_page_reclaim_list_init() - Initialize a page reclaim list
+ * @prl: Page reclaim list to initialize
+ *
+ * NULLs both values in list to prepare on initalization.
+ */
+void xe_page_reclaim_list_init(struct xe_page_reclaim_list *prl)
+{
+ prl->entries = NULL;
+ prl->num_entries = 0;
+}
+
+/**
+ * xe_page_reclaim_list_alloc_entries() - Allocate page reclaim list entries
+ * @prl: Page reclaim list to allocate entries for
+ *
+ * Allocate one 4K page for the PRL entries, otherwise assign prl->entries to NULL.
+ */
+int xe_page_reclaim_list_alloc_entries(struct xe_page_reclaim_list *prl)
+{
+ struct page *page;
+
+ if (XE_WARN_ON(prl->entries))
+ return 0;
+
+ page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (page) {
+ prl->entries = page_address(page);
+ prl->num_entries = 0;
+ }
+
+ return page ? 0 : -ENOMEM;
+}
diff --git a/drivers/gpu/drm/xe/xe_page_reclaim.h b/drivers/gpu/drm/xe/xe_page_reclaim.h
new file mode 100644
index 000000000000..3dd103e37beb
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_page_reclaim.h
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_PAGE_RECLAIM_H_
+#define _XE_PAGE_RECLAIM_H_
+
+#include <linux/kref.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <linux/bits.h>
+
+#define XE_PAGE_RECLAIM_MAX_ENTRIES 512
+#define XE_PAGE_RECLAIM_LIST_MAX_SIZE SZ_4K
+
+struct xe_tlb_inval;
+struct xe_tlb_inval_fence;
+struct xe_tile;
+struct xe_gt;
+struct xe_vma;
+
+struct xe_guc_page_reclaim_entry {
+ u64 qw;
+/* valid reclaim entry bit */
+#define XE_PAGE_RECLAIM_VALID BIT_ULL(0)
+/*
+ * offset order of page size to be reclaimed
+ * page_size = 1 << (XE_PTE_SHIFT + reclamation_size)
+ */
+#define XE_PAGE_RECLAIM_SIZE GENMASK_ULL(6, 1)
+#define XE_PAGE_RECLAIM_RSVD_0 GENMASK_ULL(11, 7)
+/* lower 20 bits of the physical address */
+#define XE_PAGE_RECLAIM_ADDR_LO GENMASK_ULL(31, 12)
+/* upper 20 bits of the physical address */
+#define XE_PAGE_RECLAIM_ADDR_HI GENMASK_ULL(51, 32)
+#define XE_PAGE_RECLAIM_RSVD_1 GENMASK_ULL(63, 52)
+} __packed;
+
+struct xe_page_reclaim_list {
+ /** @entries: array of page reclaim entries, page allocated */
+ struct xe_guc_page_reclaim_entry *entries;
+ /** @num_entries: number of entries */
+ int num_entries;
+#define XE_PAGE_RECLAIM_INVALID_LIST -1
+};
+
+/**
+ * xe_page_reclaim_list_is_new() - Check if PRL is new allocation
+ * @prl: Pointer to page reclaim list
+ *
+ * PRL indicates it hasn't been allocated through both values being NULL
+ */
+static inline bool xe_page_reclaim_list_is_new(struct xe_page_reclaim_list *prl)
+{
+ return !prl->entries && prl->num_entries == 0;
+}
+
+/**
+ * xe_page_reclaim_list_valid() - Check if the page reclaim list is valid
+ * @prl: Pointer to page reclaim list
+ *
+ * PRL uses the XE_PAGE_RECLAIM_INVALID_LIST to indicate that a PRL
+ * is unusable.
+ */
+static inline bool xe_page_reclaim_list_valid(struct xe_page_reclaim_list *prl)
+{
+ return !xe_page_reclaim_list_is_new(prl) &&
+ prl->num_entries != XE_PAGE_RECLAIM_INVALID_LIST;
+}
+
+bool xe_page_reclaim_skip(struct xe_tile *tile, struct xe_vma *vma);
+struct drm_suballoc *xe_page_reclaim_create_prl_bo(struct xe_tlb_inval *tlb_inval,
+ struct xe_page_reclaim_list *prl,
+ struct xe_tlb_inval_fence *fence);
+void xe_page_reclaim_list_invalidate(struct xe_page_reclaim_list *prl);
+
+/**
+ * xe_page_reclaim_list_abort() - Invalidate a PRL and log an abort reason
+ * @gt: GT owning the page reclaim request
+ * @prl: Page reclaim list to invalidate
+ * @fmt: format string for the log message with args
+ *
+ * Abort page reclaim process by invalidating PRL and doing any relevant logging.
+ */
+#define xe_page_reclaim_list_abort(gt, prl, fmt, ...) \
+ do { \
+ struct xe_gt *__gt = (gt); \
+ struct xe_page_reclaim_list *__prl = (prl); \
+ \
+ xe_page_reclaim_list_invalidate(__prl); \
+ xe_gt_stats_incr(__gt, XE_GT_STATS_ID_PRL_ABORTED_COUNT, 1); \
+ vm_dbg(&gt_to_xe(__gt)->drm, "PRL aborted: " fmt, ##__VA_ARGS__); \
+ } while (0)
+
+void xe_page_reclaim_list_init(struct xe_page_reclaim_list *prl);
+int xe_page_reclaim_list_alloc_entries(struct xe_page_reclaim_list *prl);
+/**
+ * xe_page_reclaim_entries_get() - Increment the reference count of page reclaim entries.
+ * @entries: Pointer to the array of page reclaim entries.
+ *
+ * This function increments the reference count of the backing page.
+ */
+static inline void xe_page_reclaim_entries_get(struct xe_guc_page_reclaim_entry *entries)
+{
+ if (entries)
+ get_page(virt_to_page(entries));
+}
+
+/**
+ * xe_page_reclaim_entries_put() - Decrement the reference count of page reclaim entries.
+ * @entries: Pointer to the array of page reclaim entries.
+ *
+ * This function decrements the reference count of the backing page
+ * and frees it if the count reaches zero.
+ */
+static inline void xe_page_reclaim_entries_put(struct xe_guc_page_reclaim_entry *entries)
+{
+ if (entries)
+ put_page(virt_to_page(entries));
+}
+
+#endif /* _XE_PAGE_RECLAIM_H_ */
diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
index afb06598b6e1..6bee53d6ffc3 100644
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -223,22 +223,22 @@ static bool xe_pagefault_queue_pop(struct xe_pagefault_queue *pf_queue,
static void xe_pagefault_print(struct xe_pagefault *pf)
{
- xe_gt_dbg(pf->gt, "\n\tASID: %d\n"
- "\tFaulted Address: 0x%08x%08x\n"
- "\tFaultType: %d\n"
- "\tAccessType: %d\n"
- "\tFaultLevel: %d\n"
- "\tEngineClass: %d %s\n"
- "\tEngineInstance: %d\n",
- pf->consumer.asid,
- upper_32_bits(pf->consumer.page_addr),
- lower_32_bits(pf->consumer.page_addr),
- pf->consumer.fault_type,
- pf->consumer.access_type,
- pf->consumer.fault_level,
- pf->consumer.engine_class,
- xe_hw_engine_class_to_str(pf->consumer.engine_class),
- pf->consumer.engine_instance);
+ xe_gt_info(pf->gt, "\n\tASID: %d\n"
+ "\tFaulted Address: 0x%08x%08x\n"
+ "\tFaultType: %d\n"
+ "\tAccessType: %d\n"
+ "\tFaultLevel: %d\n"
+ "\tEngineClass: %d %s\n"
+ "\tEngineInstance: %d\n",
+ pf->consumer.asid,
+ upper_32_bits(pf->consumer.page_addr),
+ lower_32_bits(pf->consumer.page_addr),
+ pf->consumer.fault_type,
+ pf->consumer.access_type,
+ pf->consumer.fault_level,
+ pf->consumer.engine_class,
+ xe_hw_engine_class_to_str(pf->consumer.engine_class),
+ pf->consumer.engine_instance);
}
static void xe_pagefault_queue_work(struct work_struct *w)
@@ -260,8 +260,8 @@ static void xe_pagefault_queue_work(struct work_struct *w)
err = xe_pagefault_service(&pf);
if (err) {
xe_pagefault_print(&pf);
- xe_gt_dbg(pf.gt, "Fault response: Unsuccessful %pe\n",
- ERR_PTR(err));
+ xe_gt_info(pf.gt, "Fault response: Unsuccessful %pe\n",
+ ERR_PTR(err));
}
pf.producer.ops->ack_fault(&pf, err);
diff --git a/drivers/gpu/drm/xe/xe_pat.c b/drivers/gpu/drm/xe/xe_pat.c
index 68171cceea18..14d0dce5190a 100644
--- a/drivers/gpu/drm/xe/xe_pat.c
+++ b/drivers/gpu/drm/xe/xe_pat.c
@@ -9,6 +9,7 @@
#include <generated/xe_wa_oob.h>
+#include "regs/xe_gt_regs.h"
#include "regs/xe_reg_defs.h"
#include "xe_assert.h"
#include "xe_device.h"
@@ -50,8 +51,37 @@
#define XELP_PAT_WC REG_FIELD_PREP(XELP_MEM_TYPE_MASK, 1)
#define XELP_PAT_UC REG_FIELD_PREP(XELP_MEM_TYPE_MASK, 0)
+#define PAT_LABEL_LEN 20
+
static const char *XELP_MEM_TYPE_STR_MAP[] = { "UC", "WC", "WT", "WB" };
+static void xe_pat_index_label(char *label, size_t len, int index)
+{
+ snprintf(label, len, "PAT[%2d] ", index);
+}
+
+static void xelp_pat_entry_dump(struct drm_printer *p, int index, u32 pat)
+{
+ u8 mem_type = REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat);
+
+ drm_printf(p, "PAT[%2d] = %s (%#8x)\n", index,
+ XELP_MEM_TYPE_STR_MAP[mem_type], pat);
+}
+
+static void xehpc_pat_entry_dump(struct drm_printer *p, int index, u32 pat)
+{
+ drm_printf(p, "PAT[%2d] = [ %u, %u ] (%#8x)\n", index,
+ REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat),
+ REG_FIELD_GET(XEHPC_CLOS_LEVEL_MASK, pat), pat);
+}
+
+static void xelpg_pat_entry_dump(struct drm_printer *p, int index, u32 pat)
+{
+ drm_printf(p, "PAT[%2d] = [ %u, %u ] (%#8x)\n", index,
+ REG_FIELD_GET(XELPG_L4_POLICY_MASK, pat),
+ REG_FIELD_GET(XELPG_INDEX_COH_MODE_MASK, pat), pat);
+}
+
struct xe_pat_ops {
void (*program_graphics)(struct xe_gt *gt, const struct xe_pat_table_entry table[],
int n_entries);
@@ -102,9 +132,10 @@ static const struct xe_pat_table_entry xelpg_pat_table[] = {
* in the table.
*
* Note: There is an implicit assumption in the driver that compression and
- * coh_1way+ are mutually exclusive. If this is ever not true then userptr
- * and imported dma-buf from external device will have uncleared ccs state. See
- * also xe_bo_needs_ccs_pages().
+ * coh_1way+ are mutually exclusive for platforms prior to Xe3. Starting
+ * with Xe3, compression can be combined with coherency. If using compression
+ * with coherency, userptr and imported dma-buf from external device will
+ * have uncleared ccs state. See also xe_bo_needs_ccs_pages().
*/
#define XE2_PAT(no_promote, comp_en, l3clos, l3_policy, l4_policy, __coh_mode) \
{ \
@@ -114,8 +145,7 @@ static const struct xe_pat_table_entry xelpg_pat_table[] = {
REG_FIELD_PREP(XE2_L3_POLICY, l3_policy) | \
REG_FIELD_PREP(XE2_L4_POLICY, l4_policy) | \
REG_FIELD_PREP(XE2_COH_MODE, __coh_mode), \
- .coh_mode = (BUILD_BUG_ON_ZERO(__coh_mode && comp_en) || __coh_mode) ? \
- XE_COH_AT_LEAST_1WAY : XE_COH_NONE, \
+ .coh_mode = __coh_mode ? XE_COH_AT_LEAST_1WAY : XE_COH_NONE, \
.valid = 1 \
}
@@ -151,6 +181,38 @@ static const struct xe_pat_table_entry xe2_pat_table[] = {
[31] = XE2_PAT( 0, 0, 3, 0, 3, 3 ),
};
+static const struct xe_pat_table_entry xe3_lpg_pat_table[] = {
+ [ 0] = XE2_PAT( 0, 0, 0, 0, 3, 0 ),
+ [ 1] = XE2_PAT( 0, 0, 0, 0, 3, 2 ),
+ [ 2] = XE2_PAT( 0, 0, 0, 0, 3, 3 ),
+ [ 3] = XE2_PAT( 0, 0, 0, 3, 3, 0 ),
+ [ 4] = XE2_PAT( 0, 0, 0, 3, 0, 2 ),
+ [ 5] = XE2_PAT( 0, 0, 0, 3, 3, 2 ),
+ [ 6] = XE2_PAT( 1, 0, 0, 1, 3, 0 ),
+ [ 7] = XE2_PAT( 0, 0, 0, 3, 0, 3 ),
+ [ 8] = XE2_PAT( 0, 0, 0, 3, 0, 0 ),
+ [ 9] = XE2_PAT( 0, 1, 0, 0, 3, 0 ),
+ [10] = XE2_PAT( 0, 1, 0, 3, 0, 0 ),
+ [11] = XE2_PAT( 1, 1, 0, 1, 3, 0 ),
+ [12] = XE2_PAT( 0, 1, 0, 3, 3, 0 ),
+ [13] = XE2_PAT( 0, 0, 0, 0, 0, 0 ),
+ [14] = XE2_PAT( 0, 1, 0, 0, 0, 0 ),
+ [15] = XE2_PAT( 1, 1, 0, 1, 1, 0 ),
+ [16] = XE2_PAT( 0, 1, 0, 0, 3, 2 ),
+ /* 17..19 are reserved; leave set to all 0's */
+ [20] = XE2_PAT( 0, 0, 1, 0, 3, 0 ),
+ [21] = XE2_PAT( 0, 1, 1, 0, 3, 0 ),
+ [22] = XE2_PAT( 0, 0, 1, 0, 3, 2 ),
+ [23] = XE2_PAT( 0, 0, 1, 0, 3, 3 ),
+ [24] = XE2_PAT( 0, 0, 2, 0, 3, 0 ),
+ [25] = XE2_PAT( 0, 1, 2, 0, 3, 0 ),
+ [26] = XE2_PAT( 0, 0, 2, 0, 3, 2 ),
+ [27] = XE2_PAT( 0, 0, 2, 0, 3, 3 ),
+ [28] = XE2_PAT( 0, 0, 3, 0, 3, 0 ),
+ [29] = XE2_PAT( 0, 1, 3, 0, 3, 0 ),
+ [30] = XE2_PAT( 0, 0, 3, 0, 3, 2 ),
+ [31] = XE2_PAT( 0, 0, 3, 0, 3, 3 ),
+};
/* Special PAT values programmed outside the main table */
static const struct xe_pat_table_entry xe2_pat_ats = XE2_PAT( 0, 0, 0, 0, 3, 3 );
static const struct xe_pat_table_entry xe2_pat_pta = XE2_PAT( 0, 0, 0, 0, 3, 0 );
@@ -196,6 +258,19 @@ u16 xe_pat_index_get_coh_mode(struct xe_device *xe, u16 pat_index)
return xe->pat.table[pat_index].coh_mode;
}
+bool xe_pat_index_get_comp_en(struct xe_device *xe, u16 pat_index)
+{
+ WARN_ON(pat_index >= xe->pat.n_entries);
+ return !!(xe->pat.table[pat_index].value & XE2_COMP_EN);
+}
+
+u16 xe_pat_index_get_l3_policy(struct xe_device *xe, u16 pat_index)
+{
+ WARN_ON(pat_index >= xe->pat.n_entries);
+
+ return REG_FIELD_GET(XE2_L3_POLICY, xe->pat.table[pat_index].value);
+}
+
static void program_pat(struct xe_gt *gt, const struct xe_pat_table_entry table[],
int n_entries)
{
@@ -233,24 +308,20 @@ static void program_pat_mcr(struct xe_gt *gt, const struct xe_pat_table_entry ta
static int xelp_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
- unsigned int fw_ref;
int i;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
for (i = 0; i < xe->pat.n_entries; i++) {
u32 pat = xe_mmio_read32(&gt->mmio, XE_REG(_PAT_INDEX(i)));
- u8 mem_type = REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat);
- drm_printf(p, "PAT[%2d] = %s (%#8x)\n", i,
- XELP_MEM_TYPE_STR_MAP[mem_type], pat);
+ xelp_pat_entry_dump(p, i, pat);
}
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
return 0;
}
@@ -262,26 +333,20 @@ static const struct xe_pat_ops xelp_pat_ops = {
static int xehp_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
- unsigned int fw_ref;
int i;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
for (i = 0; i < xe->pat.n_entries; i++) {
u32 pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
- u8 mem_type;
-
- mem_type = REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat);
- drm_printf(p, "PAT[%2d] = %s (%#8x)\n", i,
- XELP_MEM_TYPE_STR_MAP[mem_type], pat);
+ xelp_pat_entry_dump(p, i, pat);
}
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
return 0;
}
@@ -293,11 +358,10 @@ static const struct xe_pat_ops xehp_pat_ops = {
static int xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
- unsigned int fw_ref;
int i;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -305,12 +369,9 @@ static int xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
for (i = 0; i < xe->pat.n_entries; i++) {
u32 pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
- drm_printf(p, "PAT[%2d] = [ %u, %u ] (%#8x)\n", i,
- REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat),
- REG_FIELD_GET(XEHPC_CLOS_LEVEL_MASK, pat), pat);
+ xehpc_pat_entry_dump(p, i, pat);
}
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
return 0;
}
@@ -322,11 +383,10 @@ static const struct xe_pat_ops xehpc_pat_ops = {
static int xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
- unsigned int fw_ref;
int i;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -339,12 +399,9 @@ static int xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
else
pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
- drm_printf(p, "PAT[%2d] = [ %u, %u ] (%#8x)\n", i,
- REG_FIELD_GET(XELPG_L4_POLICY_MASK, pat),
- REG_FIELD_GET(XELPG_INDEX_COH_MODE_MASK, pat), pat);
+ xelpg_pat_entry_dump(p, i, pat);
}
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
return 0;
}
@@ -358,15 +415,38 @@ static const struct xe_pat_ops xelpg_pat_ops = {
.dump = xelpg_dump,
};
+static void xe2_pat_entry_dump(struct drm_printer *p, const char *label, u32 pat, bool rsvd)
+{
+ drm_printf(p, "%s= [ %u, %u, %u, %u, %u, %u ] (%#8x)%s\n", label,
+ !!(pat & XE2_NO_PROMOTE),
+ !!(pat & XE2_COMP_EN),
+ REG_FIELD_GET(XE2_L3_CLOS, pat),
+ REG_FIELD_GET(XE2_L3_POLICY, pat),
+ REG_FIELD_GET(XE2_L4_POLICY, pat),
+ REG_FIELD_GET(XE2_COH_MODE, pat),
+ pat, rsvd ? " *" : "");
+}
+
+static void xe3p_xpc_pat_entry_dump(struct drm_printer *p, const char *label, u32 pat, bool rsvd)
+{
+ drm_printf(p, "%s= [ %u, %u, %u, %u, %u ] (%#8x)%s\n", label,
+ !!(pat & XE2_NO_PROMOTE),
+ REG_FIELD_GET(XE2_L3_CLOS, pat),
+ REG_FIELD_GET(XE2_L3_POLICY, pat),
+ REG_FIELD_GET(XE2_L4_POLICY, pat),
+ REG_FIELD_GET(XE2_COH_MODE, pat),
+ pat, rsvd ? " *" : "");
+}
+
static int xe2_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
- unsigned int fw_ref;
u32 pat;
int i;
+ char label[PAT_LABEL_LEN];
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
drm_printf(p, "PAT table: (* = reserved entry)\n");
@@ -377,14 +457,8 @@ static int xe2_dump(struct xe_gt *gt, struct drm_printer *p)
else
pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
- drm_printf(p, "PAT[%2d] = [ %u, %u, %u, %u, %u, %u ] (%#8x)%s\n", i,
- !!(pat & XE2_NO_PROMOTE),
- !!(pat & XE2_COMP_EN),
- REG_FIELD_GET(XE2_L3_CLOS, pat),
- REG_FIELD_GET(XE2_L3_POLICY, pat),
- REG_FIELD_GET(XE2_L4_POLICY, pat),
- REG_FIELD_GET(XE2_COH_MODE, pat),
- pat, xe->pat.table[i].valid ? "" : " *");
+ xe_pat_index_label(label, sizeof(label), i);
+ xe2_pat_entry_dump(p, label, pat, !xe->pat.table[i].valid);
}
/*
@@ -397,16 +471,8 @@ static int xe2_dump(struct xe_gt *gt, struct drm_printer *p)
pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_PTA));
drm_printf(p, "Page Table Access:\n");
- drm_printf(p, "PTA_MODE= [ %u, %u, %u, %u, %u, %u ] (%#8x)\n",
- !!(pat & XE2_NO_PROMOTE),
- !!(pat & XE2_COMP_EN),
- REG_FIELD_GET(XE2_L3_CLOS, pat),
- REG_FIELD_GET(XE2_L3_POLICY, pat),
- REG_FIELD_GET(XE2_L4_POLICY, pat),
- REG_FIELD_GET(XE2_COH_MODE, pat),
- pat);
+ xe2_pat_entry_dump(p, "PTA_MODE", pat, false);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
return 0;
}
@@ -419,12 +485,12 @@ static const struct xe_pat_ops xe2_pat_ops = {
static int xe3p_xpc_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
- unsigned int fw_ref;
u32 pat;
int i;
+ char label[PAT_LABEL_LEN];
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
drm_printf(p, "PAT table: (* = reserved entry)\n");
@@ -432,13 +498,8 @@ static int xe3p_xpc_dump(struct xe_gt *gt, struct drm_printer *p)
for (i = 0; i < xe->pat.n_entries; i++) {
pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
- drm_printf(p, "PAT[%2d] = [ %u, %u, %u, %u, %u ] (%#8x)%s\n", i,
- !!(pat & XE2_NO_PROMOTE),
- REG_FIELD_GET(XE2_L3_CLOS, pat),
- REG_FIELD_GET(XE2_L3_POLICY, pat),
- REG_FIELD_GET(XE2_L4_POLICY, pat),
- REG_FIELD_GET(XE2_COH_MODE, pat),
- pat, xe->pat.table[i].valid ? "" : " *");
+ xe_pat_index_label(label, sizeof(label), i);
+ xe3p_xpc_pat_entry_dump(p, label, pat, !xe->pat.table[i].valid);
}
/*
@@ -448,15 +509,8 @@ static int xe3p_xpc_dump(struct xe_gt *gt, struct drm_printer *p)
pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_PTA));
drm_printf(p, "Page Table Access:\n");
- drm_printf(p, "PTA_MODE= [ %u, %u, %u, %u, %u ] (%#8x)\n",
- !!(pat & XE2_NO_PROMOTE),
- REG_FIELD_GET(XE2_L3_CLOS, pat),
- REG_FIELD_GET(XE2_L3_POLICY, pat),
- REG_FIELD_GET(XE2_L4_POLICY, pat),
- REG_FIELD_GET(XE2_COH_MODE, pat),
- pat);
+ xe3p_xpc_pat_entry_dump(p, "PTA_MODE", pat, false);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
return 0;
}
@@ -468,6 +522,7 @@ static const struct xe_pat_ops xe3p_xpc_pat_ops = {
void xe_pat_init_early(struct xe_device *xe)
{
+ xe->pat.idx[XE_CACHE_WB_COMPRESSION] = XE_PAT_INVALID_IDX;
if (GRAPHICS_VERx100(xe) == 3511) {
xe->pat.ops = &xe3p_xpc_pat_ops;
xe->pat.table = xe3p_xpc_pat_table;
@@ -479,7 +534,12 @@ void xe_pat_init_early(struct xe_device *xe)
xe->pat.idx[XE_CACHE_WB] = 2;
} else if (GRAPHICS_VER(xe) == 30 || GRAPHICS_VER(xe) == 20) {
xe->pat.ops = &xe2_pat_ops;
- xe->pat.table = xe2_pat_table;
+ if (GRAPHICS_VER(xe) == 30) {
+ xe->pat.table = xe3_lpg_pat_table;
+ xe->pat.idx[XE_CACHE_WB_COMPRESSION] = 16;
+ } else {
+ xe->pat.table = xe2_pat_table;
+ }
xe->pat.pat_ats = &xe2_pat_ats;
if (IS_DGFX(xe))
xe->pat.pat_pta = &xe2_pat_pta;
@@ -578,3 +638,67 @@ int xe_pat_dump(struct xe_gt *gt, struct drm_printer *p)
return xe->pat.ops->dump(gt, p);
}
+
+/**
+ * xe_pat_dump_sw_config() - Dump the software-configured GT PAT table into a drm printer.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_pat_dump_sw_config(struct xe_gt *gt, struct drm_printer *p)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ char label[PAT_LABEL_LEN];
+
+ if (!xe->pat.table || !xe->pat.n_entries)
+ return -EOPNOTSUPP;
+
+ drm_printf(p, "PAT table:%s\n", GRAPHICS_VER(xe) >= 20 ? " (* = reserved entry)" : "");
+ for (u32 i = 0; i < xe->pat.n_entries; i++) {
+ u32 pat = xe->pat.table[i].value;
+
+ if (GRAPHICS_VERx100(xe) == 3511) {
+ xe_pat_index_label(label, sizeof(label), i);
+ xe3p_xpc_pat_entry_dump(p, label, pat, !xe->pat.table[i].valid);
+ } else if (GRAPHICS_VER(xe) == 30 || GRAPHICS_VER(xe) == 20) {
+ xe_pat_index_label(label, sizeof(label), i);
+ xe2_pat_entry_dump(p, label, pat, !xe->pat.table[i].valid);
+ } else if (xe->info.platform == XE_METEORLAKE) {
+ xelpg_pat_entry_dump(p, i, pat);
+ } else if (xe->info.platform == XE_PVC) {
+ xehpc_pat_entry_dump(p, i, pat);
+ } else if (xe->info.platform == XE_DG2 || GRAPHICS_VERx100(xe) <= 1210) {
+ xelp_pat_entry_dump(p, i, pat);
+ } else {
+ return -EOPNOTSUPP;
+ }
+ }
+
+ if (xe->pat.pat_pta) {
+ u32 pat = xe->pat.pat_pta->value;
+
+ drm_printf(p, "Page Table Access:\n");
+ xe2_pat_entry_dump(p, "PTA_MODE", pat, false);
+ }
+
+ if (xe->pat.pat_ats) {
+ u32 pat = xe->pat.pat_ats->value;
+
+ drm_printf(p, "PCIe ATS/PASID:\n");
+ xe2_pat_entry_dump(p, "PAT_ATS ", pat, false);
+ }
+
+ drm_printf(p, "Cache Level:\n");
+ drm_printf(p, "IDX[XE_CACHE_NONE] = %d\n", xe->pat.idx[XE_CACHE_NONE]);
+ drm_printf(p, "IDX[XE_CACHE_WT] = %d\n", xe->pat.idx[XE_CACHE_WT]);
+ drm_printf(p, "IDX[XE_CACHE_WB] = %d\n", xe->pat.idx[XE_CACHE_WB]);
+ if (GRAPHICS_VER(xe) >= 20) {
+ drm_printf(p, "IDX[XE_CACHE_NONE_COMPRESSION] = %d\n",
+ xe->pat.idx[XE_CACHE_NONE_COMPRESSION]);
+ drm_printf(p, "IDX[XE_CACHE_WB_COMPRESSION] = %d\n",
+ xe->pat.idx[XE_CACHE_WB_COMPRESSION]);
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_pat.h b/drivers/gpu/drm/xe/xe_pat.h
index 05dae03a5f54..c7e2a53d8cee 100644
--- a/drivers/gpu/drm/xe/xe_pat.h
+++ b/drivers/gpu/drm/xe/xe_pat.h
@@ -12,6 +12,8 @@ struct drm_printer;
struct xe_device;
struct xe_gt;
+#define XE_PAT_INVALID_IDX U16_MAX
+
/**
* struct xe_pat_table_entry - The pat_index encoding and other meta information.
*/
@@ -49,6 +51,7 @@ void xe_pat_init_early(struct xe_device *xe);
void xe_pat_init(struct xe_gt *gt);
int xe_pat_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_pat_dump_sw_config(struct xe_gt *gt, struct drm_printer *p);
/**
* xe_pat_index_get_coh_mode - Extract the coherency mode for the given
@@ -58,4 +61,24 @@ int xe_pat_dump(struct xe_gt *gt, struct drm_printer *p);
*/
u16 xe_pat_index_get_coh_mode(struct xe_device *xe, u16 pat_index);
+/**
+ * xe_pat_index_get_comp_en - Extract the compression enable flag for
+ * the given pat_index.
+ * @xe: xe device
+ * @pat_index: The pat_index to query
+ *
+ * Return: true if compression is enabled for this pat_index, false otherwise.
+ */
+bool xe_pat_index_get_comp_en(struct xe_device *xe, u16 pat_index);
+
+#define XE_L3_POLICY_WB 0 /* Write-back */
+#define XE_L3_POLICY_XD 1 /* WB - Transient Display */
+#define XE_L3_POLICY_UC 3 /* Uncached */
+/**
+ * xe_pat_index_get_l3_policy - Extract the L3 policy for the given pat_index.
+ * @xe: xe device
+ * @pat_index: The pat_index to query
+ */
+u16 xe_pat_index_get_l3_policy(struct xe_device *xe, u16 pat_index);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index 9c9ea10d994c..09189ff3da44 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -24,9 +24,9 @@
#include "xe_gt.h"
#include "xe_gt_sriov_vf.h"
#include "xe_guc.h"
-#include "xe_macros.h"
#include "xe_mmio.h"
#include "xe_module.h"
+#include "xe_pci_rebar.h"
#include "xe_pci_sriov.h"
#include "xe_pci_types.h"
#include "xe_pm.h"
@@ -108,6 +108,7 @@ static const struct xe_graphics_desc graphics_xe2 = {
static const struct xe_graphics_desc graphics_xe3p_xpc = {
XE2_GFX_FEATURES,
+ .has_indirect_ring_state = 1,
.hw_engine_mask =
GENMASK(XE_HW_ENGINE_BCS8, XE_HW_ENGINE_BCS1) |
GENMASK(XE_HW_ENGINE_CCS3, XE_HW_ENGINE_CCS0),
@@ -168,6 +169,7 @@ static const struct xe_device_desc tgl_desc = {
.pre_gmdid_media_ip = &media_ip_xem,
PLATFORM(TIGERLAKE),
.dma_mask_size = 39,
+ .has_cached_pt = true,
.has_display = true,
.has_llc = true,
.has_sriov = true,
@@ -182,6 +184,7 @@ static const struct xe_device_desc rkl_desc = {
.pre_gmdid_media_ip = &media_ip_xem,
PLATFORM(ROCKETLAKE),
.dma_mask_size = 39,
+ .has_cached_pt = true,
.has_display = true,
.has_llc = true,
.max_gt_per_tile = 1,
@@ -197,6 +200,7 @@ static const struct xe_device_desc adl_s_desc = {
.pre_gmdid_media_ip = &media_ip_xem,
PLATFORM(ALDERLAKE_S),
.dma_mask_size = 39,
+ .has_cached_pt = true,
.has_display = true,
.has_llc = true,
.has_sriov = true,
@@ -217,6 +221,7 @@ static const struct xe_device_desc adl_p_desc = {
.pre_gmdid_media_ip = &media_ip_xem,
PLATFORM(ALDERLAKE_P),
.dma_mask_size = 39,
+ .has_cached_pt = true,
.has_display = true,
.has_llc = true,
.has_sriov = true,
@@ -235,6 +240,7 @@ static const struct xe_device_desc adl_n_desc = {
.pre_gmdid_media_ip = &media_ip_xem,
PLATFORM(ALDERLAKE_N),
.dma_mask_size = 39,
+ .has_cached_pt = true,
.has_display = true,
.has_llc = true,
.has_sriov = true,
@@ -359,9 +365,13 @@ static const struct xe_device_desc bmg_desc = {
.has_fan_control = true,
.has_flat_ccs = 1,
.has_mbx_power_limits = true,
+ .has_mbx_thermal_info = true,
.has_gsc_nvm = 1,
.has_heci_cscfi = 1,
+ .has_i2c = true,
.has_late_bind = true,
+ .has_pre_prod_wa = 1,
+ .has_soc_remapper_telem = true,
.has_sriov = true,
.has_mem_copy_instr = true,
.max_gt_per_tile = 2,
@@ -381,6 +391,8 @@ static const struct xe_device_desc ptl_desc = {
.has_flat_ccs = 1,
.has_sriov = true,
.has_mem_copy_instr = true,
+ .has_pre_prod_wa = 1,
+ .has_pxp = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
.needs_shared_vf_gt_wq = true,
@@ -394,6 +406,7 @@ static const struct xe_device_desc nvls_desc = {
.has_display = true,
.has_flat_ccs = 1,
.has_mem_copy_instr = true,
+ .has_pre_prod_wa = 1,
.max_gt_per_tile = 2,
.require_force_probe = true,
.va_bits = 48,
@@ -406,7 +419,14 @@ static const struct xe_device_desc cri_desc = {
.dma_mask_size = 52,
.has_display = false,
.has_flat_ccs = false,
+ .has_gsc_nvm = 1,
+ .has_i2c = true,
.has_mbx_power_limits = true,
+ .has_mbx_thermal_info = true,
+ .has_mert = true,
+ .has_pre_prod_wa = 1,
+ .has_soc_remapper_sysctrl = true,
+ .has_soc_remapper_telem = true,
.has_sriov = true,
.max_gt_per_tile = 2,
.require_force_probe = true,
@@ -663,16 +683,24 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.vram_flags = desc->vram_flags;
xe->info.is_dgfx = desc->is_dgfx;
+ xe->info.has_cached_pt = desc->has_cached_pt;
xe->info.has_fan_control = desc->has_fan_control;
/* runtime fusing may force flat_ccs to disabled later */
xe->info.has_flat_ccs = desc->has_flat_ccs;
xe->info.has_mbx_power_limits = desc->has_mbx_power_limits;
+ xe->info.has_mbx_thermal_info = desc->has_mbx_thermal_info;
xe->info.has_gsc_nvm = desc->has_gsc_nvm;
xe->info.has_heci_gscfi = desc->has_heci_gscfi;
xe->info.has_heci_cscfi = desc->has_heci_cscfi;
+ xe->info.has_i2c = desc->has_i2c;
xe->info.has_late_bind = desc->has_late_bind;
xe->info.has_llc = desc->has_llc;
+ xe->info.has_mert = desc->has_mert;
+ xe->info.has_page_reclaim_hw_assist = desc->has_page_reclaim_hw_assist;
+ xe->info.has_pre_prod_wa = desc->has_pre_prod_wa;
xe->info.has_pxp = desc->has_pxp;
+ xe->info.has_soc_remapper_sysctrl = desc->has_soc_remapper_sysctrl;
+ xe->info.has_soc_remapper_telem = desc->has_soc_remapper_telem;
xe->info.has_sriov = xe_configfs_primary_gt_allowed(to_pci_dev(xe->drm.dev)) &&
desc->has_sriov;
xe->info.has_mem_copy_instr = desc->has_mem_copy_instr;
@@ -755,6 +783,7 @@ static struct xe_gt *alloc_primary_gt(struct xe_tile *tile,
gt->info.type = XE_GT_TYPE_MAIN;
gt->info.id = tile->id * xe->info.max_gt_per_tile;
gt->info.has_indirect_ring_state = graphics_desc->has_indirect_ring_state;
+ gt->info.multi_queue_engine_class_mask = graphics_desc->multi_queue_engine_class_mask;
gt->info.engine_mask = graphics_desc->hw_engine_mask;
/*
@@ -996,7 +1025,7 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
return err;
- xe_vram_resize_bar(xe);
+ xe_pci_rebar_resize(xe);
err = xe_device_probe_early(xe);
/*
@@ -1153,6 +1182,15 @@ static int xe_pci_runtime_suspend(struct device *dev)
struct xe_device *xe = pdev_to_xe_device(pdev);
int err;
+ /*
+ * We hold an additional reference to the runtime PM to keep PF in D0
+ * during VFs lifetime, as our VFs do not implement the PM capability.
+ * This means we should never be runtime suspending as long as VFs are
+ * enabled.
+ */
+ xe_assert(xe, !IS_SRIOV_VF(xe));
+ xe_assert(xe, !pci_num_vf(pdev));
+
err = xe_pm_runtime_suspend(xe);
if (err)
return err;
diff --git a/drivers/gpu/drm/xe/xe_pci_rebar.c b/drivers/gpu/drm/xe/xe_pci_rebar.c
new file mode 100644
index 000000000000..7e2c7079b6ff
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_pci_rebar.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <linux/pci.h>
+#include <linux/types.h>
+
+#include "regs/xe_bars.h"
+#include "xe_device_types.h"
+#include "xe_module.h"
+#include "xe_pci_rebar.h"
+#include "xe_printk.h"
+
+static void resize_bar(struct xe_device *xe, int resno, resource_size_t size)
+{
+ struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
+ int bar_size = pci_rebar_bytes_to_size(size);
+ int ret;
+
+ ret = pci_resize_resource(pdev, resno, bar_size, 0);
+ if (ret) {
+ xe_info(xe, "Failed to resize BAR%d to %dMiB (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n",
+ resno, 1 << bar_size, ERR_PTR(ret));
+ return;
+ }
+
+ xe_info(xe, "BAR%d resized to %dMiB\n", resno, 1 << bar_size);
+}
+
+/*
+ * xe_pci_rebar_resize - Resize the LMEMBAR
+ * @xe: xe device instance
+ *
+ * If vram_bar_size module param is set, attempt to set to the requested size
+ * else set to maximum possible size.
+ */
+void xe_pci_rebar_resize(struct xe_device *xe)
+{
+ int force_vram_bar_size = xe_modparam.force_vram_bar_size;
+ struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
+ struct pci_bus *root = pdev->bus;
+ resource_size_t current_size;
+ resource_size_t rebar_size;
+ struct resource *root_res;
+ int max_size, i;
+ u32 pci_cmd;
+
+ /* gather some relevant info */
+ current_size = pci_resource_len(pdev, LMEM_BAR);
+
+ if (force_vram_bar_size < 0)
+ return;
+
+ /* set to a specific size? */
+ if (force_vram_bar_size) {
+ rebar_size = pci_rebar_bytes_to_size(force_vram_bar_size *
+ (resource_size_t)SZ_1M);
+
+ if (!pci_rebar_size_supported(pdev, LMEM_BAR, rebar_size)) {
+ xe_info(xe, "Requested size %lluMiB is not supported by rebar sizes: 0x%llx. Leaving default: %lluMiB\n",
+ (u64)pci_rebar_size_to_bytes(rebar_size) >> ilog2(SZ_1M),
+ pci_rebar_get_possible_sizes(pdev, LMEM_BAR),
+ (u64)current_size >> ilog2(SZ_1M));
+ return;
+ }
+
+ rebar_size = pci_rebar_size_to_bytes(rebar_size);
+ if (rebar_size == current_size)
+ return;
+ } else {
+ max_size = pci_rebar_get_max_size(pdev, LMEM_BAR);
+ if (max_size < 0)
+ return;
+ rebar_size = pci_rebar_size_to_bytes(max_size);
+
+ /* only resize if larger than current */
+ if (rebar_size <= current_size)
+ return;
+ }
+
+ xe_info(xe, "Attempting to resize bar from %lluMiB -> %lluMiB\n",
+ (u64)current_size >> ilog2(SZ_1M), (u64)rebar_size >> ilog2(SZ_1M));
+
+ while (root->parent)
+ root = root->parent;
+
+ pci_bus_for_each_resource(root, root_res, i) {
+ if (root_res && root_res->flags & (IORESOURCE_MEM | IORESOURCE_MEM_64) &&
+ (u64)root_res->start > 0x100000000ul)
+ break;
+ }
+
+ if (!root_res) {
+ xe_info(xe, "Can't resize VRAM BAR - platform support is missing. Consider enabling 'Resizable BAR' support in your BIOS\n");
+ return;
+ }
+
+ pci_read_config_dword(pdev, PCI_COMMAND, &pci_cmd);
+ pci_write_config_dword(pdev, PCI_COMMAND, pci_cmd & ~PCI_COMMAND_MEMORY);
+
+ resize_bar(xe, LMEM_BAR, rebar_size);
+
+ pci_assign_unassigned_bus_resources(pdev->bus);
+ pci_write_config_dword(pdev, PCI_COMMAND, pci_cmd);
+}
diff --git a/drivers/gpu/drm/xe/xe_pci_rebar.h b/drivers/gpu/drm/xe/xe_pci_rebar.h
new file mode 100644
index 000000000000..8677921ac363
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_pci_rebar.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_PCI_REBAR_H_
+#define _XE_PCI_REBAR_H_
+
+struct xe_device;
+
+void xe_pci_rebar_resize(struct xe_device *xe);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_pci_sriov.c b/drivers/gpu/drm/xe/xe_pci_sriov.c
index 9ff69c4843b0..3fd22034f03e 100644
--- a/drivers/gpu/drm/xe/xe_pci_sriov.c
+++ b/drivers/gpu/drm/xe/xe_pci_sriov.c
@@ -219,7 +219,6 @@ static int pf_disable_vfs(struct xe_device *xe)
int xe_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
struct xe_device *xe = pdev_to_xe_device(pdev);
- int ret;
if (!IS_SRIOV_PF(xe))
return -ENODEV;
@@ -233,14 +232,11 @@ int xe_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
if (num_vfs && pci_num_vf(pdev))
return -EBUSY;
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
if (num_vfs > 0)
- ret = pf_enable_vfs(xe, num_vfs);
+ return pf_enable_vfs(xe, num_vfs);
else
- ret = pf_disable_vfs(xe);
- xe_pm_runtime_put(xe);
-
- return ret;
+ return pf_disable_vfs(xe);
}
/**
diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h
index 9892c063a9c5..20acc5349ee6 100644
--- a/drivers/gpu/drm/xe/xe_pci_types.h
+++ b/drivers/gpu/drm/xe/xe_pci_types.h
@@ -37,17 +37,25 @@ struct xe_device_desc {
u8 require_force_probe:1;
u8 is_dgfx:1;
+ u8 has_cached_pt:1;
u8 has_display:1;
u8 has_fan_control:1;
u8 has_flat_ccs:1;
u8 has_gsc_nvm:1;
u8 has_heci_gscfi:1;
u8 has_heci_cscfi:1;
+ u8 has_i2c:1;
u8 has_late_bind:1;
u8 has_llc:1;
u8 has_mbx_power_limits:1;
+ u8 has_mbx_thermal_info:1;
u8 has_mem_copy_instr:1;
+ u8 has_mert:1;
+ u8 has_pre_prod_wa:1;
+ u8 has_page_reclaim_hw_assist:1;
u8 has_pxp:1;
+ u8 has_soc_remapper_sysctrl:1;
+ u8 has_soc_remapper_telem:1;
u8 has_sriov:1;
u8 needs_scratch:1;
u8 skip_guc_pc:1;
@@ -58,6 +66,7 @@ struct xe_device_desc {
struct xe_graphics_desc {
u64 hw_engine_mask; /* hardware engines provided by graphics IP */
+ u16 multi_queue_engine_class_mask; /* bitmask of engine classes which support multi queue */
u8 has_asid:1;
u8 has_atomic_enable_pte_bit:1;
diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
index 70dcd6625680..85cc7478b787 100644
--- a/drivers/gpu/drm/xe/xe_pcode_api.h
+++ b/drivers/gpu/drm/xe/xe_pcode_api.h
@@ -50,6 +50,13 @@
#define READ_PL_FROM_FW 0x1
#define READ_PL_FROM_PCODE 0x0
+#define PCODE_THERMAL_INFO 0x25
+#define READ_THERMAL_LIMITS 0x0
+#define READ_THERMAL_CONFIG 0x1
+#define READ_THERMAL_DATA 0x2
+#define PCIE_SENSOR_GROUP_ID 0x2
+#define PCIE_SENSOR_MASK REG_GENMASK(31, 16)
+
#define PCODE_LATE_BINDING 0x5C
#define GET_CAPABILITY_STATUS 0x0
#define V1_FAN_SUPPORTED REG_BIT(0)
@@ -77,11 +84,13 @@
#define PCODE_SCRATCH(x) XE_REG(0x138320 + ((x) * 4))
/* PCODE_SCRATCH0 */
+#define BREADCRUMB_VERSION REG_GENMASK(31, 29)
#define AUXINFO_REG_OFFSET REG_GENMASK(17, 15)
#define OVERFLOW_REG_OFFSET REG_GENMASK(14, 12)
#define HISTORY_TRACKING REG_BIT(11)
#define OVERFLOW_SUPPORT REG_BIT(10)
#define AUXINFO_SUPPORT REG_BIT(9)
+#define FDO_MODE REG_BIT(4)
#define BOOT_STATUS REG_GENMASK(3, 1)
#define CRITICAL_FAILURE 4
#define NON_CRITICAL_FAILURE 7
diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c
index 766922530265..559cf5490ac0 100644
--- a/drivers/gpu/drm/xe/xe_pm.c
+++ b/drivers/gpu/drm/xe/xe_pm.c
@@ -260,10 +260,19 @@ int xe_pm_resume(struct xe_device *xe)
xe_irq_resume(xe);
- for_each_gt(gt, xe, id)
- xe_gt_resume(gt);
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_resume(gt);
+ if (err)
+ break;
+ }
+ /*
+ * Try to bring up display before bailing from GT resume failure,
+ * so we don't leave the user clueless with a blank screen.
+ */
xe_display_pm_resume(xe);
+ if (err)
+ goto err;
err = xe_bo_restore_late(xe);
if (err)
@@ -591,7 +600,7 @@ int xe_pm_runtime_suspend(struct xe_device *xe)
}
for_each_gt(gt, xe, id) {
- err = xe_gt_suspend(gt);
+ err = xe->d3cold.allowed ? xe_gt_suspend(gt) : xe_gt_runtime_suspend(gt);
if (err)
goto out_resume;
}
@@ -633,10 +642,10 @@ int xe_pm_runtime_resume(struct xe_device *xe)
xe_rpm_lockmap_acquire(xe);
- for_each_gt(gt, xe, id)
- xe_gt_idle_disable_c6(gt);
-
if (xe->d3cold.allowed) {
+ for_each_gt(gt, xe, id)
+ xe_gt_idle_disable_c6(gt);
+
err = xe_pcode_ready(xe, true);
if (err)
goto out;
@@ -656,10 +665,19 @@ int xe_pm_runtime_resume(struct xe_device *xe)
xe_irq_resume(xe);
- for_each_gt(gt, xe, id)
- xe_gt_resume(gt);
+ for_each_gt(gt, xe, id) {
+ err = xe->d3cold.allowed ? xe_gt_resume(gt) : xe_gt_runtime_resume(gt);
+ if (err)
+ break;
+ }
+ /*
+ * Try to bring up display before bailing from GT resume failure,
+ * so we don't leave the user clueless with a blank screen.
+ */
xe_display_pm_runtime_resume(xe);
+ if (err)
+ goto out;
if (xe->d3cold.allowed) {
err = xe_bo_restore_late(xe);
diff --git a/drivers/gpu/drm/xe/xe_pmu.c b/drivers/gpu/drm/xe/xe_pmu.c
index c63335eb69e5..0b20059dd7b3 100644
--- a/drivers/gpu/drm/xe/xe_pmu.c
+++ b/drivers/gpu/drm/xe/xe_pmu.c
@@ -425,7 +425,7 @@ static ssize_t event_attr_show(struct device *dev,
struct perf_pmu_events_attr *pmu_attr =
container_of(attr, struct perf_pmu_events_attr, attr);
- return sprintf(buf, "event=%#04llx\n", pmu_attr->id);
+ return sysfs_emit(buf, "event=%#04llx\n", pmu_attr->id);
}
#define XE_EVENT_ATTR(name_, v_, id_) \
diff --git a/drivers/gpu/drm/xe/xe_psmi.c b/drivers/gpu/drm/xe/xe_psmi.c
index 6a54e38b81ba..899b01f72ba3 100644
--- a/drivers/gpu/drm/xe/xe_psmi.c
+++ b/drivers/gpu/drm/xe/xe_psmi.c
@@ -6,7 +6,7 @@
#include <linux/debugfs.h>
#include "xe_bo.h"
-#include "xe_device.h"
+#include "xe_device_types.h"
#include "xe_configfs.h"
#include "xe_psmi.h"
diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index 884127b4d97d..6703a7049227 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -11,7 +11,9 @@
#include "xe_drm_client.h"
#include "xe_exec_queue.h"
#include "xe_gt.h"
+#include "xe_gt_stats.h"
#include "xe_migrate.h"
+#include "xe_page_reclaim.h"
#include "xe_pt_types.h"
#include "xe_pt_walk.h"
#include "xe_res_cursor.h"
@@ -1535,6 +1537,9 @@ struct xe_pt_stage_unbind_walk {
/** @modified_end: Walk range start, modified like @modified_start. */
u64 modified_end;
+ /** @prl: Backing pointer to page reclaim list in pt_update_ops */
+ struct xe_page_reclaim_list *prl;
+
/* Output */
/* @wupd: Structure to track the page-table updates we're building */
struct xe_walk_update wupd;
@@ -1572,6 +1577,69 @@ static bool xe_pt_check_kill(u64 addr, u64 next, unsigned int level,
return false;
}
+/* page_size = 2^(reclamation_size + XE_PTE_SHIFT) */
+#define COMPUTE_RECLAIM_ADDRESS_MASK(page_size) \
+({ \
+ BUILD_BUG_ON(!__builtin_constant_p(page_size)); \
+ ilog2(page_size) - XE_PTE_SHIFT; \
+})
+
+static int generate_reclaim_entry(struct xe_tile *tile,
+ struct xe_page_reclaim_list *prl,
+ u64 pte, struct xe_pt *xe_child)
+{
+ struct xe_gt *gt = tile->primary_gt;
+ struct xe_guc_page_reclaim_entry *reclaim_entries = prl->entries;
+ u64 phys_addr = pte & XE_PTE_ADDR_MASK;
+ u64 phys_page = phys_addr >> XE_PTE_SHIFT;
+ int num_entries = prl->num_entries;
+ u32 reclamation_size;
+
+ xe_tile_assert(tile, xe_child->level <= MAX_HUGEPTE_LEVEL);
+ xe_tile_assert(tile, reclaim_entries);
+ xe_tile_assert(tile, num_entries < XE_PAGE_RECLAIM_MAX_ENTRIES - 1);
+
+ if (!xe_page_reclaim_list_valid(prl))
+ return -EINVAL;
+
+ /**
+ * reclamation_size indicates the size of the page to be
+ * invalidated and flushed from non-coherent cache.
+ * Page size is computed as 2^(reclamation_size + XE_PTE_SHIFT) bytes.
+ * Only 4K, 64K (level 0), and 2M pages are supported by hardware for page reclaim
+ */
+ if (xe_child->level == 0 && !(pte & XE_PTE_PS64)) {
+ xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_4K_ENTRY_COUNT, 1);
+ reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_4K); /* reclamation_size = 0 */
+ xe_tile_assert(tile, phys_addr % SZ_4K == 0);
+ } else if (xe_child->level == 0) {
+ xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_64K_ENTRY_COUNT, 1);
+ reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_64K); /* reclamation_size = 4 */
+ xe_tile_assert(tile, phys_addr % SZ_64K == 0);
+ } else if (xe_child->level == 1 && pte & XE_PDE_PS_2M) {
+ xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_2M_ENTRY_COUNT, 1);
+ reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_2M); /* reclamation_size = 9 */
+ xe_tile_assert(tile, phys_addr % SZ_2M == 0);
+ } else {
+ xe_page_reclaim_list_abort(tile->primary_gt, prl,
+ "unsupported PTE level=%u pte=%#llx",
+ xe_child->level, pte);
+ return -EINVAL;
+ }
+
+ reclaim_entries[num_entries].qw =
+ FIELD_PREP(XE_PAGE_RECLAIM_VALID, 1) |
+ FIELD_PREP(XE_PAGE_RECLAIM_SIZE, reclamation_size) |
+ FIELD_PREP(XE_PAGE_RECLAIM_ADDR_LO, phys_page) |
+ FIELD_PREP(XE_PAGE_RECLAIM_ADDR_HI, phys_page >> 20);
+ prl->num_entries++;
+ vm_dbg(&tile_to_xe(tile)->drm,
+ "PRL add entry: level=%u pte=%#llx reclamation_size=%u prl_idx=%d\n",
+ xe_child->level, pte, reclamation_size, num_entries);
+
+ return 0;
+}
+
static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,
unsigned int level, u64 addr, u64 next,
struct xe_ptw **child,
@@ -1579,11 +1647,78 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,
struct xe_pt_walk *walk)
{
struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base);
+ struct xe_pt_stage_unbind_walk *xe_walk =
+ container_of(walk, typeof(*xe_walk), base);
+ struct xe_device *xe = tile_to_xe(xe_walk->tile);
+ pgoff_t first = xe_pt_offset(addr, xe_child->level, walk);
+ bool killed;
XE_WARN_ON(!*child);
XE_WARN_ON(!level);
+ /* Check for leaf node */
+ if (xe_walk->prl && xe_page_reclaim_list_valid(xe_walk->prl) &&
+ (!xe_child->base.children || !xe_child->base.children[first])) {
+ struct iosys_map *leaf_map = &xe_child->bo->vmap;
+ pgoff_t count = xe_pt_num_entries(addr, next, xe_child->level, walk);
- xe_pt_check_kill(addr, next, level - 1, xe_child, action, walk);
+ for (pgoff_t i = 0; i < count; i++) {
+ u64 pte = xe_map_rd(xe, leaf_map, (first + i) * sizeof(u64), u64);
+ int ret;
+
+ /*
+ * In rare scenarios, pte may not be written yet due to racy conditions.
+ * In such cases, invalidate the PRL and fallback to full PPC invalidation.
+ */
+ if (!pte) {
+ xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl,
+ "found zero pte at addr=%#llx", addr);
+ break;
+ }
+
+ /* Ensure it is a defined page */
+ xe_tile_assert(xe_walk->tile,
+ xe_child->level == 0 ||
+ (pte & (XE_PTE_PS64 | XE_PDE_PS_2M | XE_PDPE_PS_1G)));
+
+ /* An entry should be added for 64KB but contigious 4K have XE_PTE_PS64 */
+ if (pte & XE_PTE_PS64)
+ i += 15; /* Skip other 15 consecutive 4K pages in the 64K page */
+
+ /* Account for NULL terminated entry on end (-1) */
+ if (xe_walk->prl->num_entries < XE_PAGE_RECLAIM_MAX_ENTRIES - 1) {
+ ret = generate_reclaim_entry(xe_walk->tile, xe_walk->prl,
+ pte, xe_child);
+ if (ret)
+ break;
+ } else {
+ /* overflow, mark as invalid */
+ xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl,
+ "overflow while adding pte=%#llx",
+ pte);
+ break;
+ }
+ }
+ }
+
+ killed = xe_pt_check_kill(addr, next, level - 1, xe_child, action, walk);
+
+ /*
+ * Verify PRL is active and if entry is not a leaf pte (base.children conditions),
+ * there is a potential need to invalidate the PRL if any PTE (num_live) are dropped.
+ */
+ if (xe_walk->prl && level > 1 && xe_child->num_live &&
+ xe_child->base.children && xe_child->base.children[first]) {
+ bool covered = xe_pt_covers(addr, next, xe_child->level, &xe_walk->base);
+
+ /*
+ * If aborting page walk early (kill) or page walk completes the full range
+ * we need to invalidate the PRL.
+ */
+ if (killed || covered)
+ xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl,
+ "kill at level=%u addr=%#llx next=%#llx num_live=%u",
+ level, addr, next, xe_child->num_live);
+ }
return 0;
}
@@ -1654,6 +1789,8 @@ static unsigned int xe_pt_stage_unbind(struct xe_tile *tile,
{
u64 start = range ? xe_svm_range_start(range) : xe_vma_start(vma);
u64 end = range ? xe_svm_range_end(range) : xe_vma_end(vma);
+ struct xe_vm_pgtable_update_op *pt_update_op =
+ container_of(entries, struct xe_vm_pgtable_update_op, entries[0]);
struct xe_pt_stage_unbind_walk xe_walk = {
.base = {
.ops = &xe_pt_stage_unbind_ops,
@@ -1665,6 +1802,7 @@ static unsigned int xe_pt_stage_unbind(struct xe_tile *tile,
.modified_start = start,
.modified_end = end,
.wupd.entries = entries,
+ .prl = pt_update_op->prl,
};
struct xe_pt *pt = vm->pt_root[tile->id];
@@ -1897,6 +2035,7 @@ static int unbind_op_prepare(struct xe_tile *tile,
struct xe_vm_pgtable_update_ops *pt_update_ops,
struct xe_vma *vma)
{
+ struct xe_device *xe = tile_to_xe(tile);
u32 current_op = pt_update_ops->current_op;
struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
int err;
@@ -1914,6 +2053,17 @@ static int unbind_op_prepare(struct xe_tile *tile,
pt_op->vma = vma;
pt_op->bind = false;
pt_op->rebind = false;
+ /*
+ * Maintain one PRL located in pt_update_ops that all others in unbind op reference.
+ * Ensure that PRL is allocated only once, and if invalidated, remains an invalidated PRL.
+ */
+ if (xe->info.has_page_reclaim_hw_assist &&
+ xe_page_reclaim_list_is_new(&pt_update_ops->prl))
+ xe_page_reclaim_list_alloc_entries(&pt_update_ops->prl);
+
+ /* Page reclaim may not be needed due to other features, so skip the corresponding VMA */
+ pt_op->prl = (xe_page_reclaim_list_valid(&pt_update_ops->prl) &&
+ !xe_page_reclaim_skip(tile, vma)) ? &pt_update_ops->prl : NULL;
err = vma_reserve_fences(tile_to_xe(tile), vma);
if (err)
@@ -1979,6 +2129,7 @@ static int unbind_range_prepare(struct xe_vm *vm,
pt_op->vma = XE_INVALID_VMA;
pt_op->bind = false;
pt_op->rebind = false;
+ pt_op->prl = NULL;
pt_op->num_entries = xe_pt_stage_unbind(tile, vm, NULL, range,
pt_op->entries);
@@ -2096,6 +2247,7 @@ xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops)
init_llist_head(&pt_update_ops->deferred);
pt_update_ops->start = ~0x0ull;
pt_update_ops->last = 0x0ull;
+ xe_page_reclaim_list_init(&pt_update_ops->prl);
}
/**
@@ -2393,6 +2545,17 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
goto kill_vm_tile1;
}
update.ijob = ijob;
+ /*
+ * Only add page reclaim for the primary GT. Media GT does not have
+ * any PPC to flush, so enabling the PPC flush bit for media is
+ * effectively a NOP and provides no performance benefit nor
+ * interfere with primary GT.
+ */
+ if (xe_page_reclaim_list_valid(&pt_update_ops->prl)) {
+ xe_tlb_inval_job_add_page_reclaim(ijob, &pt_update_ops->prl);
+ /* Release ref from alloc, job will now handle it */
+ xe_page_reclaim_list_invalidate(&pt_update_ops->prl);
+ }
if (tile->media_gt) {
dep_scheduler = to_dep_scheduler(q, tile->media_gt);
@@ -2518,6 +2681,8 @@ void xe_pt_update_ops_fini(struct xe_tile *tile, struct xe_vma_ops *vops)
&vops->pt_update_ops[tile->id];
int i;
+ xe_page_reclaim_entries_put(pt_update_ops->prl.entries);
+
lockdep_assert_held(&vops->vm->lock);
xe_vm_assert_held(vops->vm);
diff --git a/drivers/gpu/drm/xe/xe_pt_types.h b/drivers/gpu/drm/xe/xe_pt_types.h
index 881f01e14db8..84b51d3762a4 100644
--- a/drivers/gpu/drm/xe/xe_pt_types.h
+++ b/drivers/gpu/drm/xe/xe_pt_types.h
@@ -8,6 +8,7 @@
#include <linux/types.h>
+#include "xe_page_reclaim.h"
#include "xe_pt_walk.h"
struct xe_bo;
@@ -19,6 +20,7 @@ enum xe_cache_level {
XE_CACHE_WT,
XE_CACHE_WB,
XE_CACHE_NONE_COMPRESSION, /*UC + COH_NONE + COMPRESSION */
+ XE_CACHE_WB_COMPRESSION,
__XE_CACHE_LEVEL_COUNT,
};
@@ -79,6 +81,8 @@ struct xe_vm_pgtable_update_op {
struct xe_vm_pgtable_update entries[XE_VM_MAX_LEVEL * 2 + 1];
/** @vma: VMA for operation, operation not valid if NULL */
struct xe_vma *vma;
+ /** @prl: Backing pointer to page reclaim list of pt_update_ops */
+ struct xe_page_reclaim_list *prl;
/** @num_entries: number of entries for this update operation */
u32 num_entries;
/** @bind: is a bind */
@@ -95,6 +99,8 @@ struct xe_vm_pgtable_update_ops {
struct llist_head deferred;
/** @q: exec queue for PT operations */
struct xe_exec_queue *q;
+ /** @prl: embedded page reclaim list */
+ struct xe_page_reclaim_list prl;
/** @start: start address of ops */
u64 start;
/** @last: last address of ops */
diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c
index bdbdbbf6a678..d61446bf9c19 100644
--- a/drivers/gpu/drm/xe/xe_pxp.c
+++ b/drivers/gpu/drm/xe/xe_pxp.c
@@ -15,7 +15,6 @@
#include "xe_force_wake.h"
#include "xe_guc_submit.h"
#include "xe_gsc_proxy.h"
-#include "xe_gt.h"
#include "xe_gt_types.h"
#include "xe_huc.h"
#include "xe_mmio.h"
@@ -58,10 +57,9 @@ bool xe_pxp_is_enabled(const struct xe_pxp *pxp)
static bool pxp_prerequisites_done(const struct xe_pxp *pxp)
{
struct xe_gt *gt = pxp->gt;
- unsigned int fw_ref;
bool ready;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
/*
* If force_wake fails we could falsely report the prerequisites as not
@@ -71,14 +69,12 @@ static bool pxp_prerequisites_done(const struct xe_pxp *pxp)
* PXP. Therefore, we can just log the force_wake error and not escalate
* it.
*/
- XE_WARN_ON(!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL));
+ XE_WARN_ON(!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL));
/* PXP requires both HuC authentication via GSC and GSC proxy initialized */
ready = xe_huc_is_authenticated(&gt->uc.huc, XE_HUC_AUTH_VIA_GSC) &&
xe_gsc_proxy_init_done(&gt->uc.gsc);
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
-
return ready;
}
@@ -104,13 +100,12 @@ int xe_pxp_get_readiness_status(struct xe_pxp *pxp)
xe_uc_fw_status_to_error(pxp->gt->uc.gsc.fw.status))
return -EIO;
- xe_pm_runtime_get(pxp->xe);
+ guard(xe_pm_runtime)(pxp->xe);
/* PXP requires both HuC loaded and GSC proxy initialized */
if (pxp_prerequisites_done(pxp))
ret = 1;
- xe_pm_runtime_put(pxp->xe);
return ret;
}
@@ -135,35 +130,28 @@ static void pxp_invalidate_queues(struct xe_pxp *pxp);
static int pxp_terminate_hw(struct xe_pxp *pxp)
{
struct xe_gt *gt = pxp->gt;
- unsigned int fw_ref;
int ret = 0;
drm_dbg(&pxp->xe->drm, "Terminating PXP\n");
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT)) {
- ret = -EIO;
- goto out;
- }
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT))
+ return -EIO;
/* terminate the hw session */
ret = xe_pxp_submit_session_termination(pxp, ARB_SESSION);
if (ret)
- goto out;
+ return ret;
ret = pxp_wait_for_session_state(pxp, ARB_SESSION, false);
if (ret)
- goto out;
+ return ret;
/* Trigger full HW cleanup */
xe_mmio_write32(&gt->mmio, KCR_GLOBAL_TERMINATE, 1);
/* now we can tell the GSC to clean up its own state */
- ret = xe_pxp_submit_session_invalidation(&pxp->gsc_res, ARB_SESSION);
-
-out:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
- return ret;
+ return xe_pxp_submit_session_invalidation(&pxp->gsc_res, ARB_SESSION);
}
static void mark_termination_in_progress(struct xe_pxp *pxp)
@@ -326,14 +314,12 @@ static int kcr_pxp_set_status(const struct xe_pxp *pxp, bool enable)
{
u32 val = enable ? _MASKED_BIT_ENABLE(KCR_INIT_ALLOW_DISPLAY_ME_WRITES) :
_MASKED_BIT_DISABLE(KCR_INIT_ALLOW_DISPLAY_ME_WRITES);
- unsigned int fw_ref;
- fw_ref = xe_force_wake_get(gt_to_fw(pxp->gt), XE_FW_GT);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT))
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(pxp->gt), XE_FW_GT);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT))
return -EIO;
xe_mmio_write32(&pxp->gt->mmio, KCR_INIT, val);
- xe_force_wake_put(gt_to_fw(pxp->gt), fw_ref);
return 0;
}
@@ -453,34 +439,28 @@ out:
static int __pxp_start_arb_session(struct xe_pxp *pxp)
{
int ret;
- unsigned int fw_ref;
- fw_ref = xe_force_wake_get(gt_to_fw(pxp->gt), XE_FW_GT);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT))
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(pxp->gt), XE_FW_GT);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT))
return -EIO;
- if (pxp_session_is_in_play(pxp, ARB_SESSION)) {
- ret = -EEXIST;
- goto out_force_wake;
- }
+ if (pxp_session_is_in_play(pxp, ARB_SESSION))
+ return -EEXIST;
ret = xe_pxp_submit_session_init(&pxp->gsc_res, ARB_SESSION);
if (ret) {
drm_err(&pxp->xe->drm, "Failed to init PXP arb session: %pe\n", ERR_PTR(ret));
- goto out_force_wake;
+ return ret;
}
ret = pxp_wait_for_session_state(pxp, ARB_SESSION, true);
if (ret) {
drm_err(&pxp->xe->drm, "PXP ARB session failed to go in play%pe\n", ERR_PTR(ret));
- goto out_force_wake;
+ return ret;
}
drm_dbg(&pxp->xe->drm, "PXP ARB session is active\n");
-
-out_force_wake:
- xe_force_wake_put(gt_to_fw(pxp->gt), fw_ref);
- return ret;
+ return 0;
}
/**
diff --git a/drivers/gpu/drm/xe/xe_pxp_debugfs.c b/drivers/gpu/drm/xe/xe_pxp_debugfs.c
index 525a2f6bb076..d6e2e41bc88c 100644
--- a/drivers/gpu/drm/xe/xe_pxp_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_pxp_debugfs.c
@@ -11,7 +11,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
-#include "xe_device.h"
+#include "xe_device_types.h"
#include "xe_pxp.h"
#include "xe_pxp_types.h"
#include "regs/xe_irq_regs.h"
diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c
index 1c0915e2cc16..75490683bad2 100644
--- a/drivers/gpu/drm/xe/xe_query.c
+++ b/drivers/gpu/drm/xe/xe_query.c
@@ -122,7 +122,6 @@ query_engine_cycles(struct xe_device *xe,
__ktime_func_t cpu_clock;
struct xe_hw_engine *hwe;
struct xe_gt *gt;
- unsigned int fw_ref;
if (IS_SRIOV_VF(xe))
return -EOPNOTSUPP;
@@ -158,16 +157,13 @@ query_engine_cycles(struct xe_device *xe,
if (!hwe)
return -EINVAL;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) {
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
- return -EIO;
- }
-
- hwe_read_timestamp(hwe, &resp.engine_cycles, &resp.cpu_timestamp,
- &resp.cpu_delta, cpu_clock);
+ xe_with_force_wake(fw_ref, gt_to_fw(gt), XE_FORCEWAKE_ALL) {
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
+ return -EIO;
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ hwe_read_timestamp(hwe, &resp.engine_cycles, &resp.cpu_timestamp,
+ &resp.cpu_delta, cpu_clock);
+ }
if (GRAPHICS_VER(xe) >= 20)
resp.width = 64;
@@ -342,6 +338,9 @@ static int query_config(struct xe_device *xe, struct drm_xe_device_query *query)
if (xe->info.has_usm && IS_ENABLED(CONFIG_DRM_XE_GPUSVM))
config->info[DRM_XE_QUERY_CONFIG_FLAGS] |=
DRM_XE_QUERY_CONFIG_FLAG_HAS_CPU_ADDR_MIRROR;
+ if (GRAPHICS_VER(xe) >= 20)
+ config->info[DRM_XE_QUERY_CONFIG_FLAGS] |=
+ DRM_XE_QUERY_CONFIG_FLAG_HAS_NO_COMPRESSION_HINT;
config->info[DRM_XE_QUERY_CONFIG_FLAGS] |=
DRM_XE_QUERY_CONFIG_FLAG_HAS_LOW_LATENCY;
config->info[DRM_XE_QUERY_CONFIG_MIN_ALIGNMENT] =
@@ -686,7 +685,9 @@ static int query_oa_units(struct xe_device *xe,
du->capabilities = DRM_XE_OA_CAPS_BASE | DRM_XE_OA_CAPS_SYNCS |
DRM_XE_OA_CAPS_OA_BUFFER_SIZE |
DRM_XE_OA_CAPS_WAIT_NUM_REPORTS |
- DRM_XE_OA_CAPS_OAM;
+ DRM_XE_OA_CAPS_OAM |
+ DRM_XE_OA_CAPS_OA_UNIT_GT_ID;
+ du->gt_id = u->gt->info.id;
j = 0;
for_each_hw_engine(hwe, gt, hwe_id) {
if (!xe_hw_engine_is_reserved(hwe) &&
diff --git a/drivers/gpu/drm/xe/xe_reg_sr.c b/drivers/gpu/drm/xe/xe_reg_sr.c
index fc8447a838c4..d3e13ea33123 100644
--- a/drivers/gpu/drm/xe/xe_reg_sr.c
+++ b/drivers/gpu/drm/xe/xe_reg_sr.c
@@ -13,16 +13,13 @@
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
-#include "regs/xe_engine_regs.h"
-#include "regs/xe_gt_regs.h"
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_force_wake.h"
-#include "xe_gt.h"
#include "xe_gt_mcr.h"
#include "xe_gt_printk.h"
+#include "xe_gt_types.h"
#include "xe_hw_engine_types.h"
-#include "xe_macros.h"
#include "xe_mmio.h"
#include "xe_rtp_types.h"
@@ -168,7 +165,6 @@ void xe_reg_sr_apply_mmio(struct xe_reg_sr *sr, struct xe_gt *gt)
{
struct xe_reg_sr_entry *entry;
unsigned long reg;
- unsigned int fw_ref;
if (xa_empty(&sr->xa))
return;
@@ -178,20 +174,14 @@ void xe_reg_sr_apply_mmio(struct xe_reg_sr *sr, struct xe_gt *gt)
xe_gt_dbg(gt, "Applying %s save-restore MMIOs\n", sr->name);
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
- goto err_force_wake;
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) {
+ xe_gt_err(gt, "Failed to apply, err=-ETIMEDOUT\n");
+ return;
+ }
xa_for_each(&sr->xa, reg, entry)
apply_one_mmio(gt, entry);
-
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
-
- return;
-
-err_force_wake:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
- xe_gt_err(gt, "Failed to apply, err=-ETIMEDOUT\n");
}
/**
diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c
index 7ca360b2c20d..1d36c09681aa 100644
--- a/drivers/gpu/drm/xe/xe_reg_whitelist.c
+++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c
@@ -8,7 +8,7 @@
#include "regs/xe_engine_regs.h"
#include "regs/xe_gt_regs.h"
#include "regs/xe_oa_regs.h"
-#include "regs/xe_regs.h"
+#include "xe_device.h"
#include "xe_gt_types.h"
#include "xe_gt_printk.h"
#include "xe_platform_types.h"
@@ -26,6 +26,13 @@ static bool match_not_render(const struct xe_device *xe,
return hwe->class != XE_ENGINE_CLASS_RENDER;
}
+static bool match_has_mert(const struct xe_device *xe,
+ const struct xe_gt *gt,
+ const struct xe_hw_engine *hwe)
+{
+ return xe_device_has_mert((struct xe_device *)xe);
+}
+
static const struct xe_rtp_entry_sr register_whitelist[] = {
{ XE_RTP_NAME("WaAllowPMDepthAndInvocationCountAccessFromUMD, 1408556865"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1210), ENGINE_CLASS(RENDER)),
@@ -67,28 +74,6 @@ static const struct xe_rtp_entry_sr register_whitelist[] = {
ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(WHITELIST(CSBE_DEBUG_STATUS(RENDER_RING_BASE), 0))
},
- { XE_RTP_NAME("oa_reg_render"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED),
- ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(WHITELIST(OAG_MMIOTRIGGER,
- RING_FORCE_TO_NONPRIV_ACCESS_RW),
- WHITELIST(OAG_OASTATUS,
- RING_FORCE_TO_NONPRIV_ACCESS_RD),
- WHITELIST(OAG_OAHEADPTR,
- RING_FORCE_TO_NONPRIV_ACCESS_RD |
- RING_FORCE_TO_NONPRIV_RANGE_4))
- },
- { XE_RTP_NAME("oa_reg_compute"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED),
- ENGINE_CLASS(COMPUTE)),
- XE_RTP_ACTIONS(WHITELIST(OAG_MMIOTRIGGER,
- RING_FORCE_TO_NONPRIV_ACCESS_RW),
- WHITELIST(OAG_OASTATUS,
- RING_FORCE_TO_NONPRIV_ACCESS_RD),
- WHITELIST(OAG_OAHEADPTR,
- RING_FORCE_TO_NONPRIV_ACCESS_RD |
- RING_FORCE_TO_NONPRIV_RANGE_4))
- },
{ XE_RTP_NAME("14024997852"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(WHITELIST(FF_MODE,
@@ -96,6 +81,57 @@ static const struct xe_rtp_entry_sr register_whitelist[] = {
WHITELIST(VFLSKPD,
RING_FORCE_TO_NONPRIV_ACCESS_RW))
},
+
+#define WHITELIST_OA_MMIO_TRG(trg, status, head) \
+ WHITELIST(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \
+ WHITELIST(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \
+ WHITELIST(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4)
+
+#define WHITELIST_OAG_MMIO_TRG \
+ WHITELIST_OA_MMIO_TRG(OAG_MMIOTRIGGER, OAG_OASTATUS, OAG_OAHEADPTR)
+
+#define WHITELIST_OAM_MMIO_TRG \
+ WHITELIST_OA_MMIO_TRG(OAM_MMIO_TRG(XE_OAM_SAG_BASE_ADJ), \
+ OAM_STATUS(XE_OAM_SAG_BASE_ADJ), \
+ OAM_HEAD_POINTER(XE_OAM_SAG_BASE_ADJ)), \
+ WHITELIST_OA_MMIO_TRG(OAM_MMIO_TRG(XE_OAM_SCMI_0_BASE_ADJ), \
+ OAM_STATUS(XE_OAM_SCMI_0_BASE_ADJ), \
+ OAM_HEAD_POINTER(XE_OAM_SCMI_0_BASE_ADJ)), \
+ WHITELIST_OA_MMIO_TRG(OAM_MMIO_TRG(XE_OAM_SCMI_1_BASE_ADJ), \
+ OAM_STATUS(XE_OAM_SCMI_1_BASE_ADJ), \
+ OAM_HEAD_POINTER(XE_OAM_SCMI_1_BASE_ADJ))
+
+#define WHITELIST_OA_MERT_MMIO_TRG \
+ WHITELIST_OA_MMIO_TRG(OAMERT_MMIO_TRG, OAMERT_STATUS, OAMERT_HEAD_POINTER)
+
+ { XE_RTP_NAME("oag_mmio_trg_rcs"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED),
+ ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(WHITELIST_OAG_MMIO_TRG)
+ },
+ { XE_RTP_NAME("oag_mmio_trg_ccs"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED),
+ ENGINE_CLASS(COMPUTE)),
+ XE_RTP_ACTIONS(WHITELIST_OAG_MMIO_TRG)
+ },
+ { XE_RTP_NAME("oam_mmio_trg_vcs"),
+ XE_RTP_RULES(MEDIA_VERSION_RANGE(1300, XE_RTP_END_VERSION_UNDEFINED),
+ ENGINE_CLASS(VIDEO_DECODE)),
+ XE_RTP_ACTIONS(WHITELIST_OAM_MMIO_TRG)
+ },
+ { XE_RTP_NAME("oam_mmio_trg_vecs"),
+ XE_RTP_RULES(MEDIA_VERSION_RANGE(1300, XE_RTP_END_VERSION_UNDEFINED),
+ ENGINE_CLASS(VIDEO_ENHANCE)),
+ XE_RTP_ACTIONS(WHITELIST_OAM_MMIO_TRG)
+ },
+ { XE_RTP_NAME("oa_mert_mmio_trg_ccs"),
+ XE_RTP_RULES(FUNC(match_has_mert), ENGINE_CLASS(COMPUTE)),
+ XE_RTP_ACTIONS(WHITELIST_OA_MERT_MMIO_TRG)
+ },
+ { XE_RTP_NAME("oa_mert_mmio_trg_bcs"),
+ XE_RTP_RULES(FUNC(match_has_mert), ENGINE_CLASS(COPY)),
+ XE_RTP_ACTIONS(WHITELIST_OA_MERT_MMIO_TRG)
+ },
};
static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe)
diff --git a/drivers/gpu/drm/xe/xe_ring_ops.c b/drivers/gpu/drm/xe/xe_ring_ops.c
index ac0c6dcffe15..248620b0901d 100644
--- a/drivers/gpu/drm/xe/xe_ring_ops.c
+++ b/drivers/gpu/drm/xe/xe_ring_ops.c
@@ -11,11 +11,9 @@
#include "instructions/xe_mi_commands.h"
#include "regs/xe_engine_regs.h"
#include "regs/xe_gt_regs.h"
-#include "regs/xe_lrc_layout.h"
-#include "xe_exec_queue_types.h"
-#include "xe_gt.h"
+#include "xe_exec_queue.h"
+#include "xe_gt_types.h"
#include "xe_lrc.h"
-#include "xe_macros.h"
#include "xe_sched_job.h"
#include "xe_sriov.h"
#include "xe_vm_types.h"
@@ -135,12 +133,11 @@ emit_pipe_control(u32 *dw, int i, u32 bit_group_0, u32 bit_group_1, u32 offset,
return i;
}
-static int emit_pipe_invalidate(u32 mask_flags, bool invalidate_tlb, u32 *dw,
- int i)
+static int emit_pipe_invalidate(struct xe_exec_queue *q, u32 mask_flags,
+ bool invalidate_tlb, u32 *dw, int i)
{
u32 flags0 = 0;
- u32 flags1 = PIPE_CONTROL_CS_STALL |
- PIPE_CONTROL_COMMAND_CACHE_INVALIDATE |
+ u32 flags1 = PIPE_CONTROL_COMMAND_CACHE_INVALIDATE |
PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE |
PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE |
PIPE_CONTROL_VF_CACHE_INVALIDATE |
@@ -152,6 +149,11 @@ static int emit_pipe_invalidate(u32 mask_flags, bool invalidate_tlb, u32 *dw,
if (invalidate_tlb)
flags1 |= PIPE_CONTROL_TLB_INVALIDATE;
+ if (xe_exec_queue_is_multi_queue(q))
+ flags0 |= PIPE_CONTROL0_QUEUE_DRAIN_MODE;
+ else
+ flags1 |= PIPE_CONTROL_CS_STALL;
+
flags1 &= ~mask_flags;
if (flags1 & PIPE_CONTROL_VF_CACHE_INVALIDATE)
@@ -175,54 +177,52 @@ static int emit_store_imm_ppgtt_posted(u64 addr, u64 value,
static int emit_render_cache_flush(struct xe_sched_job *job, u32 *dw, int i)
{
- struct xe_gt *gt = job->q->gt;
+ struct xe_exec_queue *q = job->q;
+ struct xe_gt *gt = q->gt;
bool lacks_render = !(gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK);
- u32 flags;
+ u32 flags0, flags1;
if (XE_GT_WA(gt, 14016712196))
i = emit_pipe_control(dw, i, 0, PIPE_CONTROL_DEPTH_CACHE_FLUSH,
LRC_PPHWSP_FLUSH_INVAL_SCRATCH_ADDR, 0);
- flags = (PIPE_CONTROL_CS_STALL |
- PIPE_CONTROL_TILE_CACHE_FLUSH |
+ flags0 = PIPE_CONTROL0_HDC_PIPELINE_FLUSH;
+ flags1 = (PIPE_CONTROL_TILE_CACHE_FLUSH |
PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH |
PIPE_CONTROL_DEPTH_CACHE_FLUSH |
PIPE_CONTROL_DC_FLUSH_ENABLE |
PIPE_CONTROL_FLUSH_ENABLE);
if (XE_GT_WA(gt, 1409600907))
- flags |= PIPE_CONTROL_DEPTH_STALL;
+ flags1 |= PIPE_CONTROL_DEPTH_STALL;
if (lacks_render)
- flags &= ~PIPE_CONTROL_3D_ARCH_FLAGS;
+ flags1 &= ~PIPE_CONTROL_3D_ARCH_FLAGS;
else if (job->q->class == XE_ENGINE_CLASS_COMPUTE)
- flags &= ~PIPE_CONTROL_3D_ENGINE_FLAGS;
-
- return emit_pipe_control(dw, i, PIPE_CONTROL0_HDC_PIPELINE_FLUSH, flags, 0, 0);
-}
+ flags1 &= ~PIPE_CONTROL_3D_ENGINE_FLAGS;
-static int emit_pipe_control_to_ring_end(struct xe_hw_engine *hwe, u32 *dw, int i)
-{
- if (hwe->class != XE_ENGINE_CLASS_RENDER)
- return i;
-
- if (XE_GT_WA(hwe->gt, 16020292621))
- i = emit_pipe_control(dw, i, 0, PIPE_CONTROL_LRI_POST_SYNC,
- RING_NOPID(hwe->mmio_base).addr, 0);
+ if (xe_exec_queue_is_multi_queue(q))
+ flags0 |= PIPE_CONTROL0_QUEUE_DRAIN_MODE;
+ else
+ flags1 |= PIPE_CONTROL_CS_STALL;
- return i;
+ return emit_pipe_control(dw, i, flags0, flags1, 0, 0);
}
-static int emit_pipe_imm_ggtt(u32 addr, u32 value, bool stall_only, u32 *dw,
- int i)
+static int emit_pipe_imm_ggtt(struct xe_exec_queue *q, u32 addr, u32 value,
+ bool stall_only, u32 *dw, int i)
{
- u32 flags = PIPE_CONTROL_CS_STALL | PIPE_CONTROL_GLOBAL_GTT_IVB |
- PIPE_CONTROL_QW_WRITE;
+ u32 flags0 = 0, flags1 = PIPE_CONTROL_GLOBAL_GTT_IVB | PIPE_CONTROL_QW_WRITE;
if (!stall_only)
- flags |= PIPE_CONTROL_FLUSH_ENABLE;
+ flags1 |= PIPE_CONTROL_FLUSH_ENABLE;
+
+ if (xe_exec_queue_is_multi_queue(q))
+ flags0 |= PIPE_CONTROL0_QUEUE_DRAIN_MODE;
+ else
+ flags1 |= PIPE_CONTROL_CS_STALL;
- return emit_pipe_control(dw, i, 0, flags, addr, value);
+ return emit_pipe_control(dw, i, flags0, flags1, addr, value);
}
static u32 get_ppgtt_flag(struct xe_sched_job *job)
@@ -233,13 +233,26 @@ static u32 get_ppgtt_flag(struct xe_sched_job *job)
return 0;
}
-static int emit_copy_timestamp(struct xe_lrc *lrc, u32 *dw, int i)
+static int emit_copy_timestamp(struct xe_device *xe, struct xe_lrc *lrc,
+ u32 *dw, int i)
{
dw[i++] = MI_STORE_REGISTER_MEM | MI_SRM_USE_GGTT | MI_SRM_ADD_CS_OFFSET;
dw[i++] = RING_CTX_TIMESTAMP(0).addr;
dw[i++] = xe_lrc_ctx_job_timestamp_ggtt_addr(lrc);
dw[i++] = 0;
+ /*
+ * Ensure CTX timestamp >= Job timestamp during VF sampling to avoid
+ * arithmetic wraparound in TDR.
+ */
+ if (IS_SRIOV_VF(xe)) {
+ dw[i++] = MI_STORE_REGISTER_MEM | MI_SRM_USE_GGTT |
+ MI_SRM_ADD_CS_OFFSET;
+ dw[i++] = RING_CTX_TIMESTAMP(0).addr;
+ dw[i++] = xe_lrc_ctx_timestamp_ggtt_addr(lrc);
+ dw[i++] = 0;
+ }
+
return i;
}
@@ -253,7 +266,7 @@ static void __emit_job_gen12_simple(struct xe_sched_job *job, struct xe_lrc *lrc
*head = lrc->ring.tail;
- i = emit_copy_timestamp(lrc, dw, i);
+ i = emit_copy_timestamp(gt_to_xe(gt), lrc, dw, i);
if (job->ring_ops_flush_tlb) {
dw[i++] = preparser_disable(true);
@@ -308,7 +321,7 @@ static void __emit_job_gen12_video(struct xe_sched_job *job, struct xe_lrc *lrc,
*head = lrc->ring.tail;
- i = emit_copy_timestamp(lrc, dw, i);
+ i = emit_copy_timestamp(xe, lrc, dw, i);
dw[i++] = preparser_disable(true);
@@ -362,7 +375,7 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
*head = lrc->ring.tail;
- i = emit_copy_timestamp(lrc, dw, i);
+ i = emit_copy_timestamp(xe, lrc, dw, i);
dw[i++] = preparser_disable(true);
if (lacks_render)
@@ -371,7 +384,7 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
mask_flags = PIPE_CONTROL_3D_ENGINE_FLAGS;
/* See __xe_pt_bind_vma() for a discussion on TLB invalidations. */
- i = emit_pipe_invalidate(mask_flags, job->ring_ops_flush_tlb, dw, i);
+ i = emit_pipe_invalidate(job->q, mask_flags, job->ring_ops_flush_tlb, dw, i);
/* hsdes: 1809175790 */
if (has_aux_ccs(xe))
@@ -391,12 +404,10 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
job->user_fence.value,
dw, i);
- i = emit_pipe_imm_ggtt(xe_lrc_seqno_ggtt_addr(lrc), seqno, lacks_render, dw, i);
+ i = emit_pipe_imm_ggtt(job->q, xe_lrc_seqno_ggtt_addr(lrc), seqno, lacks_render, dw, i);
i = emit_user_interrupt(dw, i);
- i = emit_pipe_control_to_ring_end(job->q->hwe, dw, i);
-
xe_gt_assert(gt, i <= MAX_JOB_SIZE_DW);
xe_lrc_write_ring(lrc, dw, i * sizeof(*dw));
@@ -406,12 +417,14 @@ static void emit_migration_job_gen12(struct xe_sched_job *job,
struct xe_lrc *lrc, u32 *head,
u32 seqno)
{
+ struct xe_gt *gt = job->q->gt;
+ struct xe_device *xe = gt_to_xe(gt);
u32 saddr = xe_lrc_start_seqno_ggtt_addr(lrc);
u32 dw[MAX_JOB_SIZE_DW], i = 0;
*head = lrc->ring.tail;
- i = emit_copy_timestamp(lrc, dw, i);
+ i = emit_copy_timestamp(xe, lrc, dw, i);
i = emit_store_imm_ggtt(saddr, seqno, dw, i);
diff --git a/drivers/gpu/drm/xe/xe_rtp.c b/drivers/gpu/drm/xe/xe_rtp.c
index ed509b1c8cfc..b7c26e2fb411 100644
--- a/drivers/gpu/drm/xe/xe_rtp.c
+++ b/drivers/gpu/drm/xe/xe_rtp.c
@@ -12,7 +12,6 @@
#include "xe_configfs.h"
#include "xe_gt.h"
#include "xe_gt_topology.h"
-#include "xe_macros.h"
#include "xe_reg_sr.h"
#include "xe_sriov.h"
diff --git a/drivers/gpu/drm/xe/xe_sa.c b/drivers/gpu/drm/xe/xe_sa.c
index 63a5263dcf1b..b738102575d4 100644
--- a/drivers/gpu/drm/xe/xe_sa.c
+++ b/drivers/gpu/drm/xe/xe_sa.c
@@ -10,7 +10,7 @@
#include <drm/drm_managed.h>
#include "xe_bo.h"
-#include "xe_device.h"
+#include "xe_device_types.h"
#include "xe_map.h"
static void xe_sa_bo_manager_fini(struct drm_device *drm, void *arg)
@@ -29,6 +29,7 @@ static void xe_sa_bo_manager_fini(struct drm_device *drm, void *arg)
kvfree(sa_manager->cpu_ptr);
sa_manager->bo = NULL;
+ sa_manager->shadow = NULL;
}
/**
@@ -37,12 +38,14 @@ static void xe_sa_bo_manager_fini(struct drm_device *drm, void *arg)
* @size: number of bytes to allocate
* @guard: number of bytes to exclude from suballocations
* @align: alignment for each suballocated chunk
+ * @flags: flags for suballocator
*
* Prepares the suballocation manager for suballocations.
*
* Return: a pointer to the &xe_sa_manager or an ERR_PTR on failure.
*/
-struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32 guard, u32 align)
+struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size,
+ u32 guard, u32 align, u32 flags)
{
struct xe_device *xe = tile_to_xe(tile);
struct xe_sa_manager *sa_manager;
@@ -79,6 +82,26 @@ struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u3
memset(sa_manager->cpu_ptr, 0, bo->ttm.base.size);
}
+ if (flags & XE_SA_BO_MANAGER_FLAG_SHADOW) {
+ struct xe_bo *shadow;
+
+ ret = drmm_mutex_init(&xe->drm, &sa_manager->swap_guard);
+ if (ret)
+ return ERR_PTR(ret);
+
+ shadow = xe_managed_bo_create_pin_map(xe, tile, size,
+ XE_BO_FLAG_VRAM_IF_DGFX(tile) |
+ XE_BO_FLAG_GGTT |
+ XE_BO_FLAG_GGTT_INVALIDATE |
+ XE_BO_FLAG_PINNED_NORESTORE);
+ if (IS_ERR(shadow)) {
+ drm_err(&xe->drm, "Failed to prepare %uKiB BO for SA manager (%pe)\n",
+ size / SZ_1K, shadow);
+ return ERR_CAST(shadow);
+ }
+ sa_manager->shadow = shadow;
+ }
+
drm_suballoc_manager_init(&sa_manager->base, managed_size, align);
ret = drmm_add_action_or_reset(&xe->drm, xe_sa_bo_manager_fini,
sa_manager);
@@ -89,6 +112,48 @@ struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u3
}
/**
+ * xe_sa_bo_swap_shadow() - Swap the SA BO with shadow BO.
+ * @sa_manager: the XE sub allocator manager
+ *
+ * Swaps the sub-allocator primary buffer object with shadow buffer object.
+ *
+ * Return: None.
+ */
+void xe_sa_bo_swap_shadow(struct xe_sa_manager *sa_manager)
+{
+ struct xe_device *xe = tile_to_xe(sa_manager->bo->tile);
+
+ xe_assert(xe, sa_manager->shadow);
+ lockdep_assert_held(&sa_manager->swap_guard);
+
+ swap(sa_manager->bo, sa_manager->shadow);
+ if (!sa_manager->bo->vmap.is_iomem)
+ sa_manager->cpu_ptr = sa_manager->bo->vmap.vaddr;
+}
+
+/**
+ * xe_sa_bo_sync_shadow() - Sync the SA Shadow BO with primary BO.
+ * @sa_bo: the sub-allocator buffer object.
+ *
+ * Synchronize sub-allocator shadow buffer object with primary buffer object.
+ *
+ * Return: None.
+ */
+void xe_sa_bo_sync_shadow(struct drm_suballoc *sa_bo)
+{
+ struct xe_sa_manager *sa_manager = to_xe_sa_manager(sa_bo->manager);
+ struct xe_device *xe = tile_to_xe(sa_manager->bo->tile);
+
+ xe_assert(xe, sa_manager->shadow);
+ lockdep_assert_held(&sa_manager->swap_guard);
+
+ xe_map_memcpy_to(xe, &sa_manager->shadow->vmap,
+ drm_suballoc_soffset(sa_bo),
+ xe_sa_bo_cpu_addr(sa_bo),
+ drm_suballoc_size(sa_bo));
+}
+
+/**
* __xe_sa_bo_new() - Make a suballocation but use custom gfp flags.
* @sa_manager: the &xe_sa_manager
* @size: number of bytes we want to suballocate
diff --git a/drivers/gpu/drm/xe/xe_sa.h b/drivers/gpu/drm/xe/xe_sa.h
index 1be744350836..05e9a4e00e78 100644
--- a/drivers/gpu/drm/xe/xe_sa.h
+++ b/drivers/gpu/drm/xe/xe_sa.h
@@ -14,12 +14,14 @@
struct dma_fence;
struct xe_tile;
-struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32 guard, u32 align);
+#define XE_SA_BO_MANAGER_FLAG_SHADOW BIT(0)
+struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size,
+ u32 guard, u32 align, u32 flags);
struct drm_suballoc *__xe_sa_bo_new(struct xe_sa_manager *sa_manager, u32 size, gfp_t gfp);
static inline struct xe_sa_manager *xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32 align)
{
- return __xe_sa_bo_manager_init(tile, size, SZ_4K, align);
+ return __xe_sa_bo_manager_init(tile, size, SZ_4K, align, 0);
}
/**
@@ -69,4 +71,18 @@ static inline void *xe_sa_bo_cpu_addr(struct drm_suballoc *sa)
drm_suballoc_soffset(sa);
}
+void xe_sa_bo_swap_shadow(struct xe_sa_manager *sa_manager);
+void xe_sa_bo_sync_shadow(struct drm_suballoc *sa_bo);
+
+/**
+ * xe_sa_bo_swap_guard() - Retrieve the SA BO swap guard within sub-allocator.
+ * @sa_manager: the &xe_sa_manager
+ *
+ * Return: Sub alloctor swap guard mutex.
+ */
+static inline struct mutex *xe_sa_bo_swap_guard(struct xe_sa_manager *sa_manager)
+{
+ return &sa_manager->swap_guard;
+}
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_sa_types.h b/drivers/gpu/drm/xe/xe_sa_types.h
index cb7238799dcb..1085c9c37d6b 100644
--- a/drivers/gpu/drm/xe/xe_sa_types.h
+++ b/drivers/gpu/drm/xe/xe_sa_types.h
@@ -12,6 +12,9 @@ struct xe_bo;
struct xe_sa_manager {
struct drm_suballoc_manager base;
struct xe_bo *bo;
+ struct xe_bo *shadow;
+ /** @swap_guard: Timeline guard updating @bo and @shadow */
+ struct mutex swap_guard;
void *cpu_ptr;
bool is_iomem;
};
diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c
index cb674a322113..3927666fe556 100644
--- a/drivers/gpu/drm/xe/xe_sched_job.c
+++ b/drivers/gpu/drm/xe/xe_sched_job.c
@@ -11,7 +11,7 @@
#include "xe_device.h"
#include "xe_exec_queue.h"
-#include "xe_gt.h"
+#include "xe_gt_types.h"
#include "xe_hw_engine_types.h"
#include "xe_hw_fence.h"
#include "xe_lrc.h"
@@ -110,6 +110,7 @@ struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q,
return ERR_PTR(-ENOMEM);
job->q = q;
+ job->sample_timestamp = U64_MAX;
kref_init(&job->refcount);
xe_exec_queue_get(job->q);
diff --git a/drivers/gpu/drm/xe/xe_sched_job_types.h b/drivers/gpu/drm/xe/xe_sched_job_types.h
index 7c4c54fe920a..13c2970e81a8 100644
--- a/drivers/gpu/drm/xe/xe_sched_job_types.h
+++ b/drivers/gpu/drm/xe/xe_sched_job_types.h
@@ -59,6 +59,8 @@ struct xe_sched_job {
u32 lrc_seqno;
/** @migrate_flush_flags: Additional flush flags for migration jobs */
u32 migrate_flush_flags;
+ /** @sample_timestamp: Sampling of job timestamp in TDR */
+ u64 sample_timestamp;
/** @ring_ops_flush_tlb: The ring ops need to flush TLB before payload. */
bool ring_ops_flush_tlb;
/** @ggtt: mapped in ggtt. */
diff --git a/drivers/gpu/drm/xe/xe_soc_remapper.c b/drivers/gpu/drm/xe/xe_soc_remapper.c
new file mode 100644
index 000000000000..1c391d719196
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_soc_remapper.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include "regs/xe_soc_remapper_regs.h"
+#include "xe_mmio.h"
+#include "xe_soc_remapper.h"
+
+static void xe_soc_remapper_set_region(struct xe_device *xe, struct xe_reg reg,
+ u32 mask, u32 val)
+{
+ guard(spinlock_irqsave)(&xe->soc_remapper.lock);
+ xe_mmio_rmw32(xe_root_tile_mmio(xe), reg, mask, val);
+}
+
+static void xe_soc_remapper_set_telem_region(struct xe_device *xe, u32 index)
+{
+ xe_soc_remapper_set_region(xe, SG_REMAP_INDEX1, SG_REMAP_TELEM_MASK,
+ REG_FIELD_PREP(SG_REMAP_TELEM_MASK, index));
+}
+
+static void xe_soc_remapper_set_sysctrl_region(struct xe_device *xe, u32 index)
+{
+ xe_soc_remapper_set_region(xe, SG_REMAP_INDEX1, SG_REMAP_SYSCTRL_MASK,
+ REG_FIELD_PREP(SG_REMAP_SYSCTRL_MASK, index));
+}
+
+/**
+ * xe_soc_remapper_init() - Initialize SoC remapper
+ * @xe: Pointer to xe device.
+ *
+ * Initialize SoC remapper.
+ *
+ * Return: 0 on success, error code on failure
+ */
+int xe_soc_remapper_init(struct xe_device *xe)
+{
+ bool has_soc_remapper = xe->info.has_soc_remapper_telem ||
+ xe->info.has_soc_remapper_sysctrl;
+
+ if (has_soc_remapper)
+ spin_lock_init(&xe->soc_remapper.lock);
+
+ if (xe->info.has_soc_remapper_telem)
+ xe->soc_remapper.set_telem_region = xe_soc_remapper_set_telem_region;
+
+ if (xe->info.has_soc_remapper_sysctrl)
+ xe->soc_remapper.set_sysctrl_region = xe_soc_remapper_set_sysctrl_region;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_soc_remapper.h b/drivers/gpu/drm/xe/xe_soc_remapper.h
new file mode 100644
index 000000000000..1060ad0f5abc
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_soc_remapper.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SOC_REMAPPER_H_
+#define _XE_SOC_REMAPPER_H_
+
+#include "xe_device_types.h"
+
+int xe_soc_remapper_init(struct xe_device *xe);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_packet.c b/drivers/gpu/drm/xe/xe_sriov_packet.c
index bab994696896..7a4c3de662e5 100644
--- a/drivers/gpu/drm/xe/xe_sriov_packet.c
+++ b/drivers/gpu/drm/xe/xe_sriov_packet.c
@@ -6,7 +6,6 @@
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_guc_klv_helpers.h"
-#include "xe_printk.h"
#include "xe_sriov_packet.h"
#include "xe_sriov_packet_types.h"
#include "xe_sriov_pf_helpers.h"
@@ -358,7 +357,7 @@ ssize_t xe_sriov_packet_write_single(struct xe_device *xe, unsigned int vfid,
#define MIGRATION_DESCRIPTOR_DWORDS (GUC_KLV_LEN_MIN + MIGRATION_KLV_DEVICE_DEVID_LEN + \
GUC_KLV_LEN_MIN + MIGRATION_KLV_DEVICE_REVID_LEN)
-static size_t pf_descriptor_init(struct xe_device *xe, unsigned int vfid)
+static int pf_descriptor_init(struct xe_device *xe, unsigned int vfid)
{
struct xe_sriov_packet **desc = pf_pick_descriptor(xe, vfid);
struct xe_sriov_packet *data;
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf.c b/drivers/gpu/drm/xe/xe_sriov_pf.c
index 7c779d63179f..6ce3c58e003c 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf.c
+++ b/drivers/gpu/drm/xe/xe_sriov_pf.c
@@ -111,6 +111,8 @@ int xe_sriov_pf_init_early(struct xe_device *xe)
xe_sriov_pf_service_init(xe);
+ xe_mert_init_early(xe);
+
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c
index bad751217e1e..81b377830d6d 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c
@@ -16,7 +16,6 @@
#include "xe_sriov_pf_migration.h"
#include "xe_sriov_pf_provision.h"
#include "xe_sriov_pf_service.h"
-#include "xe_sriov_printk.h"
#include "xe_tile_sriov_pf_debugfs.h"
/*
@@ -70,9 +69,8 @@ static ssize_t from_file_write_to_xe_call(struct file *file, const char __user *
if (ret < 0)
return ret;
if (yes) {
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
ret = call(xe);
- xe_pm_runtime_put(xe);
}
if (ret < 0)
return ret;
@@ -209,9 +207,8 @@ static ssize_t from_file_write_to_vf_call(struct file *file, const char __user *
if (ret < 0)
return ret;
if (yes) {
- xe_pm_runtime_get(xe);
+ guard(xe_pm_runtime)(xe);
ret = call(xe, vfid);
- xe_pm_runtime_put(xe);
}
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c b/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c
index c0b767ac735c..3d140506ba36 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c
@@ -389,16 +389,12 @@ static ssize_t xe_sriov_dev_attr_store(struct kobject *kobj, struct attribute *a
struct xe_sriov_dev_attr *vattr = to_xe_sriov_dev_attr(attr);
struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
struct xe_device *xe = vkobj->xe;
- ssize_t ret;
if (!vattr->store)
return -EPERM;
- xe_pm_runtime_get(xe);
- ret = xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, buf, count);
- xe_pm_runtime_put(xe);
-
- return ret;
+ guard(xe_pm_runtime)(xe);
+ return xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, buf, count);
}
static ssize_t xe_sriov_vf_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
@@ -423,18 +419,14 @@ static ssize_t xe_sriov_vf_attr_store(struct kobject *kobj, struct attribute *at
struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
struct xe_device *xe = vkobj->xe;
unsigned int vfid = vkobj->vfid;
- ssize_t ret;
xe_sriov_pf_assert_vfid(xe, vfid);
if (!vattr->store)
return -EPERM;
- xe_pm_runtime_get(xe);
- ret = xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, vfid, buf, count);
- xe_pm_runtime_get(xe);
-
- return ret;
+ guard(xe_pm_runtime)(xe);
+ return xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, vfid, buf, count);
}
static const struct sysfs_ops xe_sriov_dev_sysfs_ops = {
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf.c b/drivers/gpu/drm/xe/xe_sriov_vf.c
index 284ce37ca92d..29894bd081c0 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf.c
+++ b/drivers/gpu/drm/xe/xe_sriov_vf.c
@@ -6,7 +6,6 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_managed.h>
-#include "xe_gt.h"
#include "xe_gt_sriov_vf.h"
#include "xe_guc.h"
#include "xe_sriov_printk.h"
@@ -49,11 +48,13 @@
*
* As soon as Virtual GPU of the VM starts, the VF driver within receives
* the MIGRATED interrupt and schedules post-migration recovery worker.
- * That worker queries GuC for new provisioning (using MMIO communication),
+ * That worker sends `VF2GUC_RESFIX_START` action along with non-zero
+ * marker, queries GuC for new provisioning (using MMIO communication),
* and applies fixups to any non-virtualized resources used by the VF.
*
* When the VF driver is ready to continue operation on the newly connected
- * hardware, it sends `VF2GUC_NOTIFY_RESFIX_DONE` which causes it to
+ * hardware, it sends `VF2GUC_RESFIX_DONE` action along with the same
+ * marker which was sent with `VF2GUC_RESFIX_START` which causes it to
* enter the long awaited `VF_RUNNING` state, and therefore start handling
* CTB messages and scheduling workloads from the VF::
*
@@ -102,12 +103,17 @@
* | [ ] new VF provisioning [ ]
* | [ ]---------------------------> [ ]
* | | [ ]
+ * | | VF2GUC_RESFIX_START [ ]
+ * | [ ] <---------------------------[ ]
+ * | [ ] [ ]
+ * | [ ] success [ ]
+ * | [ ]---------------------------> [ ]
* | | VF driver applies post [ ]
* | | migration fixups -------[ ]
* | | | [ ]
* | | -----> [ ]
* | | [ ]
- * | | VF2GUC_NOTIFY_RESFIX_DONE [ ]
+ * | | VF2GUC_RESFIX_DONE [ ]
* | [ ] <---------------------------[ ]
* | [ ] [ ]
* | [ ] GuC sets new VF state to [ ]
@@ -118,6 +124,55 @@
* | [ ]---------------------------> [ ]
* | | |
* | | |
+ *
+ * Handling of VF double migration flow is shown below::
+ *
+ * GuC1 VF
+ * | |
+ * | [ ]<--- start fixups
+ * | VF2GUC_RESFIX_START(marker) [ ]
+ * [ ] <-------------------------------------------[ ]
+ * [ ] [ ]
+ * [ ]---\ [ ]
+ * [ ] store marker [ ]
+ * [ ]<--/ [ ]
+ * [ ] [ ]
+ * [ ] success [ ]
+ * [ ] ------------------------------------------> [ ]
+ * | [ ]
+ * | [ ]---\
+ * | [ ] do fixups
+ * | [ ]<--/
+ * | [ ]
+ * -------------- VF paused / saved ----------------
+ * :
+ *
+ * GuC2
+ * |
+ * ----------------- VF restored ------------------
+ * |
+ * [ ]
+ * [ ]---\
+ * [ ] reset marker
+ * [ ]<--/
+ * [ ]
+ * ----------------- VF resumed ------------------
+ * | [ ]
+ * | [ ]
+ * | VF2GUC_RESFIX_DONE(marker) [ ]
+ * [ ] <-------------------------------------------[ ]
+ * [ ] [ ]
+ * [ ]---\ [ ]
+ * [ ] check marker [ ]
+ * [ ] (mismatch) [ ]
+ * [ ]<--/ [ ]
+ * [ ] [ ]
+ * [ ] RESPONSE_VF_MIGRATED [ ]
+ * [ ] ------------------------------------------> [ ]
+ * | [ ]---\
+ * | [ ] reschedule fixups
+ * | [ ]<--/
+ * | |
*/
/**
@@ -170,6 +225,26 @@ void xe_sriov_vf_init_early(struct xe_device *xe)
vf_migration_init_early(xe);
}
+static int vf_migration_init_late(struct xe_device *xe)
+{
+ struct xe_gt *gt = xe_root_mmio_gt(xe);
+ struct xe_uc_fw_version guc_version;
+
+ if (!xe_sriov_vf_migration_supported(xe))
+ return 0;
+
+ xe_gt_sriov_vf_guc_versions(gt, NULL, &guc_version);
+ if (MAKE_GUC_VER_STRUCT(guc_version) < MAKE_GUC_VER(1, 27, 0)) {
+ xe_sriov_vf_migration_disable(xe,
+ "requires GuC ABI >= 1.27.0, but only %u.%u.%u found",
+ guc_version.major, guc_version.minor,
+ guc_version.patch);
+ return 0;
+ }
+
+ return xe_sriov_vf_ccs_init(xe);
+}
+
/**
* xe_sriov_vf_init_late() - SR-IOV VF late initialization functions.
* @xe: the &xe_device to initialize
@@ -180,7 +255,7 @@ void xe_sriov_vf_init_early(struct xe_device *xe)
*/
int xe_sriov_vf_init_late(struct xe_device *xe)
{
- return xe_sriov_vf_ccs_init(xe);
+ return vf_migration_init_late(xe);
}
static int sa_info_vf_ccs(struct seq_file *m, void *data)
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
index 797a4b866226..db023fb66a27 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
@@ -150,7 +150,8 @@ static int alloc_bb_pool(struct xe_tile *tile, struct xe_sriov_vf_ccs_ctx *ctx)
xe_sriov_info(xe, "Allocating %s CCS BB pool size = %lldMB\n",
ctx->ctx_id ? "Restore" : "Save", bb_pool_size / SZ_1M);
- sa_manager = xe_sa_bo_manager_init(tile, bb_pool_size, SZ_16);
+ sa_manager = __xe_sa_bo_manager_init(tile, bb_pool_size, SZ_4K, SZ_16,
+ XE_SA_BO_MANAGER_FLAG_SHADOW);
if (IS_ERR(sa_manager)) {
xe_sriov_err(xe, "Suballocator init failed with error: %pe\n",
@@ -162,9 +163,12 @@ static int alloc_bb_pool(struct xe_tile *tile, struct xe_sriov_vf_ccs_ctx *ctx)
offset = 0;
xe_map_memset(xe, &sa_manager->bo->vmap, offset, MI_NOOP,
bb_pool_size);
+ xe_map_memset(xe, &sa_manager->shadow->vmap, offset, MI_NOOP,
+ bb_pool_size);
offset = bb_pool_size - sizeof(u32);
xe_map_wr(xe, &sa_manager->bo->vmap, offset, u32, MI_BATCH_BUFFER_END);
+ xe_map_wr(xe, &sa_manager->shadow->vmap, offset, u32, MI_BATCH_BUFFER_END);
ctx->mem.ccs_bb_pool = sa_manager;
@@ -346,7 +350,7 @@ int xe_sriov_vf_ccs_init(struct xe_device *xe)
flags = EXEC_QUEUE_FLAG_KERNEL |
EXEC_QUEUE_FLAG_PERMANENT |
EXEC_QUEUE_FLAG_MIGRATE;
- q = xe_exec_queue_create_bind(xe, tile, flags, 0);
+ q = xe_exec_queue_create_bind(xe, tile, NULL, flags, 0);
if (IS_ERR(q)) {
err = PTR_ERR(q);
goto err_ret;
@@ -381,6 +385,18 @@ err_ret:
return err;
}
+#define XE_SRIOV_VF_CCS_RW_BB_ADDR_OFFSET (2 * sizeof(u32))
+void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx)
+{
+ u64 addr = xe_sa_manager_gpu_addr(ctx->mem.ccs_bb_pool);
+ struct xe_lrc *lrc = xe_exec_queue_lrc(ctx->mig_q);
+ struct xe_device *xe = gt_to_xe(ctx->mig_q->gt);
+
+ xe_device_wmb(xe);
+ xe_map_wr(xe, &lrc->bo->vmap, XE_SRIOV_VF_CCS_RW_BB_ADDR_OFFSET, u32, addr);
+ xe_device_wmb(xe);
+}
+
/**
* xe_sriov_vf_ccs_attach_bo - Insert CCS read write commands in the BO.
* @bo: the &buffer object to which batch buffer commands will be added.
@@ -441,9 +457,7 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo)
if (!bb)
continue;
- memset(bb->cs, MI_NOOP, bb->len * sizeof(u32));
- xe_bb_free(bb, NULL);
- bo->bb_ccs[ctx_id] = NULL;
+ xe_migrate_ccs_rw_copy_clear(bo, ctx_id);
}
return 0;
}
@@ -463,8 +477,7 @@ void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p)
if (!IS_VF_CCS_READY(xe))
return;
- xe_pm_runtime_get(xe);
-
+ guard(xe_pm_runtime)(xe);
for_each_ccs_rw_ctx(ctx_id) {
bb_pool = xe->sriov.vf.ccs.contexts[ctx_id].mem.ccs_bb_pool;
if (!bb_pool)
@@ -475,6 +488,4 @@ void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p)
drm_suballoc_dump_debug_info(&bb_pool->base, p, xe_sa_manager_gpu_addr(bb_pool));
drm_puts(p, "\n");
}
-
- xe_pm_runtime_put(xe);
}
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
index f8ca6efce9ee..00e58b36c510 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
@@ -20,6 +20,7 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo);
int xe_sriov_vf_ccs_register_context(struct xe_device *xe);
void xe_sriov_vf_ccs_rebase(struct xe_device *xe);
void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p);
+void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx);
static inline bool xe_sriov_vf_ccs_ready(struct xe_device *xe)
{
diff --git a/drivers/gpu/drm/xe/xe_sriov_vfio.c b/drivers/gpu/drm/xe/xe_sriov_vfio.c
index e9a7615bb5c5..3da81af97b8b 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vfio.c
+++ b/drivers/gpu/drm/xe/xe_sriov_vfio.c
@@ -21,7 +21,7 @@ EXPORT_SYMBOL_FOR_MODULES(xe_sriov_vfio_get_pf, "xe-vfio-pci");
bool xe_sriov_vfio_migration_supported(struct xe_device *xe)
{
if (!IS_SRIOV_PF(xe))
- return -EPERM;
+ return false;
return xe_sriov_pf_migration_supported(xe);
}
diff --git a/drivers/gpu/drm/xe/xe_step.c b/drivers/gpu/drm/xe/xe_step.c
index 10e88f2c9615..2860986f82f7 100644
--- a/drivers/gpu/drm/xe/xe_step.c
+++ b/drivers/gpu/drm/xe/xe_step.c
@@ -5,10 +5,11 @@
#include "xe_step.h"
+#include <drm/drm_print.h>
#include <kunit/visibility.h>
#include <linux/bitfield.h>
-#include "xe_device.h"
+#include "xe_device_types.h"
#include "xe_platform_types.h"
/*
diff --git a/drivers/gpu/drm/xe/xe_survivability_mode.c b/drivers/gpu/drm/xe/xe_survivability_mode.c
index 1662bfddd4bc..6578ffc77bd5 100644
--- a/drivers/gpu/drm/xe/xe_survivability_mode.c
+++ b/drivers/gpu/drm/xe/xe_survivability_mode.c
@@ -12,15 +12,13 @@
#include "xe_configfs.h"
#include "xe_device.h"
-#include "xe_gt.h"
#include "xe_heci_gsc.h"
#include "xe_i2c.h"
#include "xe_mmio.h"
+#include "xe_nvm.h"
#include "xe_pcode_api.h"
#include "xe_vsec.h"
-#define MAX_SCRATCH_MMIO 8
-
/**
* DOC: Survivability Mode
*
@@ -48,19 +46,43 @@
*
* Refer :ref:`xe_configfs` for more details on how to use configfs
*
- * Survivability mode is indicated by the below admin-only readable sysfs which provides additional
- * debug information::
+ * Survivability mode is indicated by the below admin-only readable sysfs entry. It
+ * provides information about the type of survivability mode (Boot/Runtime).
+ *
+ * .. code-block:: shell
+ *
+ * # cat /sys/bus/pci/devices/<device>/survivability_mode
+ * Boot
+ *
+ *
+ * Any additional debug information if present will be visible under the directory
+ * ``survivability_info``::
+ *
+ * /sys/bus/pci/devices/<device>/survivability_info/
+ * ├── aux_info0
+ * ├── aux_info1
+ * ├── aux_info2
+ * ├── aux_info3
+ * ├── aux_info4
+ * ├── capability_info
+ * ├── fdo_mode
+ * ├── postcode_trace
+ * └── postcode_trace_overflow
+ *
+ * This directory has the following attributes
+ *
+ * - ``capability_info`` : Indicates Boot status and support for additional information
*
- * /sys/bus/pci/devices/<device>/survivability_mode
+ * - ``postcode_trace``, ``postcode_trace_overflow`` : Each postcode is a 8bit value and
+ * represents a boot failure event. When a new failure event is logged by PCODE the
+ * existing postcodes are shifted left. These entries provide a history of 8 postcodes.
*
- * Capability Information:
- * Provides boot status
- * Postcode Information:
- * Provides information about the failure
- * Overflow Information
- * Provides history of previous failures
- * Auxiliary Information
- * Certain failures may have information in addition to postcode information
+ * - ``aux_info<n>`` : Some failures have additional debug information
+ *
+ * - ``fdo_mode`` : To allow recovery in scenarios where MEI itself fails, a new SPI Flash
+ * Descriptor Override (FDO) mode is added in v2 survivability breadcrumbs. This mode is enabled
+ * by PCODE and provides the ability to directly update the firmware via SPI Driver without
+ * any dependency on MEI. Xe KMD initializes the nvm aux driver if FDO mode is enabled.
*
* Runtime Survivability
* =====================
@@ -68,61 +90,77 @@
* Certain runtime firmware errors can cause the device to enter a wedged state
* (:ref:`xe-device-wedging`) requiring a firmware flash to restore normal operation.
* Runtime Survivability Mode indicates that a firmware flash is necessary to recover the device and
- * is indicated by the presence of survivability mode sysfs::
+ * is indicated by the presence of survivability mode sysfs.
+ * Survivability mode sysfs provides information about the type of survivability mode.
*
- * /sys/bus/pci/devices/<device>/survivability_mode
+ * .. code-block:: shell
*
- * Survivability mode sysfs provides information about the type of survivability mode.
+ * # cat /sys/bus/pci/devices/<device>/survivability_mode
+ * Runtime
*
* When such errors occur, userspace is notified with the drm device wedged uevent and runtime
* survivability mode. User can then initiate a firmware flash using userspace tools like fwupd
* to restore device to normal operation.
*/
-static u32 aux_history_offset(u32 reg_value)
+static const char * const reg_map[] = {
+ [CAPABILITY_INFO] = "Capability Info",
+ [POSTCODE_TRACE] = "Postcode trace",
+ [POSTCODE_TRACE_OVERFLOW] = "Postcode trace overflow",
+ [AUX_INFO0] = "Auxiliary Info 0",
+ [AUX_INFO1] = "Auxiliary Info 1",
+ [AUX_INFO2] = "Auxiliary Info 2",
+ [AUX_INFO3] = "Auxiliary Info 3",
+ [AUX_INFO4] = "Auxiliary Info 4",
+};
+
+#define FDO_INFO (MAX_SCRATCH_REG + 1)
+
+struct xe_survivability_attribute {
+ struct device_attribute attr;
+ u8 index;
+};
+
+static struct
+xe_survivability_attribute *dev_attr_to_survivability_attr(struct device_attribute *attr)
{
- return REG_FIELD_GET(AUXINFO_HISTORY_OFFSET, reg_value);
+ return container_of(attr, struct xe_survivability_attribute, attr);
}
-static void set_survivability_info(struct xe_mmio *mmio, struct xe_survivability_info *info,
- int id, char *name)
+static void set_survivability_info(struct xe_mmio *mmio, u32 *info, int id)
{
- strscpy(info[id].name, name, sizeof(info[id].name));
- info[id].reg = PCODE_SCRATCH(id).raw;
- info[id].value = xe_mmio_read32(mmio, PCODE_SCRATCH(id));
+ info[id] = xe_mmio_read32(mmio, PCODE_SCRATCH(id));
}
static void populate_survivability_info(struct xe_device *xe)
{
struct xe_survivability *survivability = &xe->survivability;
- struct xe_survivability_info *info = survivability->info;
+ u32 *info = survivability->info;
struct xe_mmio *mmio;
u32 id = 0, reg_value;
- char name[NAME_MAX];
- int index;
mmio = xe_root_tile_mmio(xe);
- set_survivability_info(mmio, info, id, "Capability Info");
- reg_value = info[id].value;
+ set_survivability_info(mmio, info, CAPABILITY_INFO);
+ reg_value = info[CAPABILITY_INFO];
+
+ survivability->version = REG_FIELD_GET(BREADCRUMB_VERSION, reg_value);
+ /* FDO mode is exposed only from version 2 */
+ if (survivability->version >= 2)
+ survivability->fdo_mode = REG_FIELD_GET(FDO_MODE, reg_value);
if (reg_value & HISTORY_TRACKING) {
- id++;
- set_survivability_info(mmio, info, id, "Postcode Info");
+ set_survivability_info(mmio, info, POSTCODE_TRACE);
- if (reg_value & OVERFLOW_SUPPORT) {
- id = REG_FIELD_GET(OVERFLOW_REG_OFFSET, reg_value);
- set_survivability_info(mmio, info, id, "Overflow Info");
- }
+ if (reg_value & OVERFLOW_SUPPORT)
+ set_survivability_info(mmio, info, POSTCODE_TRACE_OVERFLOW);
}
+ /* Traverse the linked list of aux info registers */
if (reg_value & AUXINFO_SUPPORT) {
- id = REG_FIELD_GET(AUXINFO_REG_OFFSET, reg_value);
-
- for (index = 0; id && reg_value; index++, reg_value = info[id].value,
- id = aux_history_offset(reg_value)) {
- snprintf(name, NAME_MAX, "Auxiliary Info %d", index);
- set_survivability_info(mmio, info, id, name);
- }
+ for (id = REG_FIELD_GET(AUXINFO_REG_OFFSET, reg_value);
+ id >= AUX_INFO0 && id < MAX_SCRATCH_REG;
+ id = REG_FIELD_GET(AUXINFO_HISTORY_OFFSET, info[id]))
+ set_survivability_info(mmio, info, id);
}
}
@@ -130,15 +168,14 @@ static void log_survivability_info(struct pci_dev *pdev)
{
struct xe_device *xe = pdev_to_xe_device(pdev);
struct xe_survivability *survivability = &xe->survivability;
- struct xe_survivability_info *info = survivability->info;
+ u32 *info = survivability->info;
int id;
dev_info(&pdev->dev, "Survivability Boot Status : Critical Failure (%d)\n",
survivability->boot_status);
- for (id = 0; id < MAX_SCRATCH_MMIO; id++) {
- if (info[id].reg)
- dev_info(&pdev->dev, "%s: 0x%x - 0x%x\n", info[id].name,
- info[id].reg, info[id].value);
+ for (id = 0; id < MAX_SCRATCH_REG; id++) {
+ if (info[id])
+ dev_info(&pdev->dev, "%s: 0x%x\n", reg_map[id], info[id]);
}
}
@@ -156,43 +193,103 @@ static ssize_t survivability_mode_show(struct device *dev,
struct pci_dev *pdev = to_pci_dev(dev);
struct xe_device *xe = pdev_to_xe_device(pdev);
struct xe_survivability *survivability = &xe->survivability;
- struct xe_survivability_info *info = survivability->info;
- int index = 0, count = 0;
- count += sysfs_emit_at(buff, count, "Survivability mode type: %s\n",
- survivability->type ? "Runtime" : "Boot");
+ return sysfs_emit(buff, "%s\n", survivability->type ? "Runtime" : "Boot");
+}
+
+static DEVICE_ATTR_ADMIN_RO(survivability_mode);
- if (!check_boot_failure(xe))
- return count;
+static ssize_t survivability_info_show(struct device *dev,
+ struct device_attribute *attr, char *buff)
+{
+ struct xe_survivability_attribute *sa = dev_attr_to_survivability_attr(attr);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct xe_device *xe = pdev_to_xe_device(pdev);
+ struct xe_survivability *survivability = &xe->survivability;
+ u32 *info = survivability->info;
- for (index = 0; index < MAX_SCRATCH_MMIO; index++) {
- if (info[index].reg)
- count += sysfs_emit_at(buff, count, "%s: 0x%x - 0x%x\n", info[index].name,
- info[index].reg, info[index].value);
- }
+ if (sa->index == FDO_INFO)
+ return sysfs_emit(buff, "%s\n", str_enabled_disabled(survivability->fdo_mode));
- return count;
+ return sysfs_emit(buff, "0x%x\n", info[sa->index]);
}
-static DEVICE_ATTR_ADMIN_RO(survivability_mode);
+#define SURVIVABILITY_ATTR_RO(name, _index) \
+ struct xe_survivability_attribute attr_##name = { \
+ .attr = __ATTR(name, 0400, survivability_info_show, NULL), \
+ .index = _index, \
+ }
+
+static SURVIVABILITY_ATTR_RO(capability_info, CAPABILITY_INFO);
+static SURVIVABILITY_ATTR_RO(postcode_trace, POSTCODE_TRACE);
+static SURVIVABILITY_ATTR_RO(postcode_trace_overflow, POSTCODE_TRACE_OVERFLOW);
+static SURVIVABILITY_ATTR_RO(aux_info0, AUX_INFO0);
+static SURVIVABILITY_ATTR_RO(aux_info1, AUX_INFO1);
+static SURVIVABILITY_ATTR_RO(aux_info2, AUX_INFO2);
+static SURVIVABILITY_ATTR_RO(aux_info3, AUX_INFO3);
+static SURVIVABILITY_ATTR_RO(aux_info4, AUX_INFO4);
+static SURVIVABILITY_ATTR_RO(fdo_mode, FDO_INFO);
static void xe_survivability_mode_fini(void *arg)
{
struct xe_device *xe = arg;
+ struct xe_survivability *survivability = &xe->survivability;
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
struct device *dev = &pdev->dev;
- sysfs_remove_file(&dev->kobj, &dev_attr_survivability_mode.attr);
+ if (survivability->fdo_mode)
+ xe_nvm_fini(xe);
+
+ device_remove_file(dev, &dev_attr_survivability_mode);
+}
+
+static umode_t survivability_info_attrs_visible(struct kobject *kobj, struct attribute *attr,
+ int idx)
+{
+ struct xe_device *xe = kdev_to_xe_device(kobj_to_dev(kobj));
+ struct xe_survivability *survivability = &xe->survivability;
+ u32 *info = survivability->info;
+
+ /*
+ * Last index in survivability_info_attrs is fdo mode and is applicable only in
+ * version 2 of survivability mode
+ */
+ if (idx == MAX_SCRATCH_REG && survivability->version >= 2)
+ return 0400;
+
+ if (idx < MAX_SCRATCH_REG && info[idx])
+ return 0400;
+
+ return 0;
}
+/* Attributes are ordered according to enum scratch_reg */
+static struct attribute *survivability_info_attrs[] = {
+ &attr_capability_info.attr.attr,
+ &attr_postcode_trace.attr.attr,
+ &attr_postcode_trace_overflow.attr.attr,
+ &attr_aux_info0.attr.attr,
+ &attr_aux_info1.attr.attr,
+ &attr_aux_info2.attr.attr,
+ &attr_aux_info3.attr.attr,
+ &attr_aux_info4.attr.attr,
+ &attr_fdo_mode.attr.attr,
+ NULL,
+};
+
+static const struct attribute_group survivability_info_group = {
+ .name = "survivability_info",
+ .attrs = survivability_info_attrs,
+ .is_visible = survivability_info_attrs_visible,
+};
+
static int create_survivability_sysfs(struct pci_dev *pdev)
{
struct device *dev = &pdev->dev;
struct xe_device *xe = pdev_to_xe_device(pdev);
int ret;
- /* create survivability mode sysfs */
- ret = sysfs_create_file(&dev->kobj, &dev_attr_survivability_mode.attr);
+ ret = device_create_file(dev, &dev_attr_survivability_mode);
if (ret) {
dev_warn(dev, "Failed to create survivability sysfs files\n");
return ret;
@@ -203,6 +300,12 @@ static int create_survivability_sysfs(struct pci_dev *pdev)
if (ret)
return ret;
+ if (check_boot_failure(xe)) {
+ ret = devm_device_add_group(dev, &survivability_info_group);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -217,15 +320,19 @@ static int enable_boot_survivability_mode(struct pci_dev *pdev)
if (ret)
return ret;
- /* Make sure xe_heci_gsc_init() knows about survivability mode */
+ /* Make sure xe_heci_gsc_init() and xe_i2c_probe() are aware of survivability */
survivability->mode = true;
- ret = xe_heci_gsc_init(xe);
- if (ret)
- goto err;
+ xe_heci_gsc_init(xe);
xe_vsec_init(xe);
+ if (survivability->fdo_mode) {
+ ret = xe_nvm_init(xe);
+ if (ret)
+ goto err;
+ }
+
ret = xe_i2c_probe(xe);
if (ret)
goto err;
@@ -235,29 +342,11 @@ static int enable_boot_survivability_mode(struct pci_dev *pdev)
return 0;
err:
+ dev_err(dev, "Failed to enable Survivability Mode\n");
survivability->mode = false;
return ret;
}
-static int init_survivability_mode(struct xe_device *xe)
-{
- struct xe_survivability *survivability = &xe->survivability;
- struct xe_survivability_info *info;
-
- survivability->size = MAX_SCRATCH_MMIO;
-
- info = devm_kcalloc(xe->drm.dev, survivability->size, sizeof(*info),
- GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- survivability->info = info;
-
- populate_survivability_info(xe);
-
- return 0;
-}
-
/**
* xe_survivability_mode_is_boot_enabled- check if boot survivability mode is enabled
* @xe: xe device instance
@@ -325,9 +414,7 @@ int xe_survivability_mode_runtime_enable(struct xe_device *xe)
return -EINVAL;
}
- ret = init_survivability_mode(xe);
- if (ret)
- return ret;
+ populate_survivability_info(xe);
ret = create_survivability_sysfs(pdev);
if (ret)
@@ -356,17 +443,16 @@ int xe_survivability_mode_boot_enable(struct xe_device *xe)
{
struct xe_survivability *survivability = &xe->survivability;
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
- int ret;
if (!xe_survivability_mode_is_requested(xe))
return 0;
- ret = init_survivability_mode(xe);
- if (ret)
- return ret;
+ populate_survivability_info(xe);
- /* Log breadcrumbs but do not enter survivability mode for Critical boot errors */
- if (survivability->boot_status == CRITICAL_FAILURE) {
+ /*
+ * v2 supports survivability mode for critical errors
+ */
+ if (survivability->version < 2 && survivability->boot_status == CRITICAL_FAILURE) {
log_survivability_info(pdev);
return -ENXIO;
}
diff --git a/drivers/gpu/drm/xe/xe_survivability_mode_types.h b/drivers/gpu/drm/xe/xe_survivability_mode_types.h
index cd65a5d167c9..bd5dc1c955ff 100644
--- a/drivers/gpu/drm/xe/xe_survivability_mode_types.h
+++ b/drivers/gpu/drm/xe/xe_survivability_mode_types.h
@@ -9,23 +9,29 @@
#include <linux/limits.h>
#include <linux/types.h>
+enum scratch_reg {
+ CAPABILITY_INFO,
+ POSTCODE_TRACE,
+ POSTCODE_TRACE_OVERFLOW,
+ AUX_INFO0,
+ AUX_INFO1,
+ AUX_INFO2,
+ AUX_INFO3,
+ AUX_INFO4,
+ MAX_SCRATCH_REG,
+};
+
enum xe_survivability_type {
XE_SURVIVABILITY_TYPE_BOOT,
XE_SURVIVABILITY_TYPE_RUNTIME,
};
-struct xe_survivability_info {
- char name[NAME_MAX];
- u32 reg;
- u32 value;
-};
-
/**
* struct xe_survivability: Contains survivability mode information
*/
struct xe_survivability {
- /** @info: struct that holds survivability info from scratch registers */
- struct xe_survivability_info *info;
+ /** @info: survivability debug info */
+ u32 info[MAX_SCRATCH_REG];
/** @size: number of scratch registers */
u32 size;
@@ -38,6 +44,12 @@ struct xe_survivability {
/** @type: survivability type */
enum xe_survivability_type type;
+
+ /** @fdo_mode: indicates if FDO mode is enabled */
+ bool fdo_mode;
+
+ /** @version: breadcrumb version of survivability mode */
+ u8 version;
};
#endif /* _XE_SURVIVABILITY_MODE_TYPES_H_ */
diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
index 55c5a0eb82e1..213f0334518a 100644
--- a/drivers/gpu/drm/xe/xe_svm.c
+++ b/drivers/gpu/drm/xe/xe_svm.c
@@ -3,7 +3,12 @@
* Copyright © 2024 Intel Corporation
*/
+#include <linux/pci-p2pdma.h>
+
#include <drm/drm_drv.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_pagemap.h>
+#include <drm/drm_pagemap_util.h>
#include "xe_bo.h"
#include "xe_exec_queue_types.h"
@@ -19,6 +24,38 @@
#include "xe_vm_types.h"
#include "xe_vram_types.h"
+/* Identifies subclasses of struct drm_pagemap_peer */
+#define XE_PEER_PAGEMAP ((void *)0ul)
+#define XE_PEER_VM ((void *)1ul)
+
+/**
+ * DOC: drm_pagemap reference-counting in xe:
+ *
+ * In addition to the drm_pagemap internal reference counting by its zone
+ * device data, the xe driver holds the following long-time references:
+ *
+ * - struct xe_pagemap:
+ * The xe_pagemap struct derives from struct drm_pagemap and uses its
+ * reference count.
+ * - SVM-enabled VMs:
+ * SVM-enabled VMs look up and keeps a reference to all xe_pagemaps on
+ * the same device.
+ * - VMAs:
+ * vmas keep a reference on the drm_pagemap indicated by a gpu_madvise()
+ * call.
+ *
+ * In addition, all drm_pagemap or xe_pagemap pointers where lifetime cannot
+ * be guaranteed by a vma reference under the vm lock should keep a reference.
+ * That includes the range->pages.dpagemap pointer.
+ */
+
+static int xe_svm_get_pagemaps(struct xe_vm *vm);
+
+void *xe_svm_private_page_owner(struct xe_vm *vm, bool force_smem)
+{
+ return force_smem ? NULL : vm->svm.peer.owner;
+}
+
static bool xe_svm_range_in_vram(struct xe_svm_range *range)
{
/*
@@ -176,24 +213,13 @@ xe_svm_range_notifier_event_end(struct xe_vm *vm, struct drm_gpusvm_range *r,
mmu_range);
}
-static s64 xe_svm_stats_ktime_us_delta(ktime_t start)
-{
- return IS_ENABLED(CONFIG_DEBUG_FS) ?
- ktime_us_delta(ktime_get(), start) : 0;
-}
-
static void xe_svm_tlb_inval_us_stats_incr(struct xe_gt *gt, ktime_t start)
{
- s64 us_delta = xe_svm_stats_ktime_us_delta(start);
+ s64 us_delta = xe_gt_stats_ktime_us_delta(start);
xe_gt_stats_incr(gt, XE_GT_STATS_ID_SVM_TLB_INVAL_US, us_delta);
}
-static ktime_t xe_svm_stats_ktime_get(void)
-{
- return IS_ENABLED(CONFIG_DEBUG_FS) ? ktime_get() : 0;
-}
-
static void xe_svm_invalidate(struct drm_gpusvm *gpusvm,
struct drm_gpusvm_notifier *notifier,
const struct mmu_notifier_range *mmu_range)
@@ -202,7 +228,7 @@ static void xe_svm_invalidate(struct drm_gpusvm *gpusvm,
struct xe_device *xe = vm->xe;
struct drm_gpusvm_range *r, *first;
struct xe_tile *tile;
- ktime_t start = xe_svm_stats_ktime_get();
+ ktime_t start = xe_gt_stats_ktime_get();
u64 adj_start = mmu_range->start, adj_end = mmu_range->end;
u8 tile_mask = 0, id;
long err;
@@ -285,19 +311,25 @@ static int __xe_svm_garbage_collector(struct xe_vm *vm,
return 0;
}
-static int xe_svm_range_set_default_attr(struct xe_vm *vm, u64 range_start, u64 range_end)
+static void xe_vma_set_default_attributes(struct xe_vma *vma)
{
- struct xe_vma *vma;
struct xe_vma_mem_attr default_attr = {
- .preferred_loc = {
- .devmem_fd = DRM_XE_PREFERRED_LOC_DEFAULT_DEVICE,
- .migration_policy = DRM_XE_MIGRATE_ALL_PAGES,
- },
+ .preferred_loc.devmem_fd = DRM_XE_PREFERRED_LOC_DEFAULT_DEVICE,
+ .preferred_loc.migration_policy = DRM_XE_MIGRATE_ALL_PAGES,
+ .pat_index = vma->attr.default_pat_index,
.atomic_access = DRM_XE_ATOMIC_UNDEFINED,
};
- int err = 0;
- vma = xe_vm_find_vma_by_addr(vm, range_start);
+ xe_vma_mem_attr_copy(&vma->attr, &default_attr);
+}
+
+static int xe_svm_range_set_default_attr(struct xe_vm *vm, u64 start, u64 end)
+{
+ struct xe_vma *vma;
+ bool has_default_attr;
+ int err;
+
+ vma = xe_vm_find_vma_by_addr(vm, start);
if (!vma)
return -EINVAL;
@@ -306,25 +338,30 @@ static int xe_svm_range_set_default_attr(struct xe_vm *vm, u64 range_start, u64
return 0;
}
- if (xe_vma_has_default_mem_attrs(vma))
- return 0;
-
vm_dbg(&vm->xe->drm, "Existing VMA start=0x%016llx, vma_end=0x%016llx",
xe_vma_start(vma), xe_vma_end(vma));
- if (xe_vma_start(vma) == range_start && xe_vma_end(vma) == range_end) {
- default_attr.pat_index = vma->attr.default_pat_index;
- default_attr.default_pat_index = vma->attr.default_pat_index;
- vma->attr = default_attr;
- } else {
- vm_dbg(&vm->xe->drm, "Split VMA start=0x%016llx, vma_end=0x%016llx",
- range_start, range_end);
- err = xe_vm_alloc_cpu_addr_mirror_vma(vm, range_start, range_end - range_start);
- if (err) {
- drm_warn(&vm->xe->drm, "VMA SPLIT failed: %pe\n", ERR_PTR(err));
- xe_vm_kill(vm, true);
- return err;
- }
+ has_default_attr = xe_vma_has_default_mem_attrs(vma);
+
+ if (has_default_attr) {
+ start = xe_vma_start(vma);
+ end = xe_vma_end(vma);
+ } else if (xe_vma_start(vma) == start && xe_vma_end(vma) == end) {
+ xe_vma_set_default_attributes(vma);
+ }
+
+ xe_vm_find_cpu_addr_mirror_vma_range(vm, &start, &end);
+
+ if (xe_vma_start(vma) == start && xe_vma_end(vma) == end && has_default_attr)
+ return 0;
+
+ vm_dbg(&vm->xe->drm, "New VMA start=0x%016llx, vma_end=0x%016llx", start, end);
+
+ err = xe_vm_alloc_cpu_addr_mirror_vma(vm, start, end - start);
+ if (err) {
+ drm_warn(&vm->xe->drm, "New VMA MAP failed: %pe\n", ERR_PTR(err));
+ xe_vm_kill(vm, true);
+ return err;
}
/*
@@ -394,27 +431,47 @@ static void xe_svm_garbage_collector_work_func(struct work_struct *w)
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
-static struct xe_vram_region *page_to_vr(struct page *page)
+static struct xe_vram_region *xe_pagemap_to_vr(struct xe_pagemap *xpagemap)
{
- return container_of(page_pgmap(page), struct xe_vram_region, pagemap);
+ return xpagemap->vr;
}
-static u64 xe_vram_region_page_to_dpa(struct xe_vram_region *vr,
- struct page *page)
+static struct xe_pagemap *xe_page_to_pagemap(struct page *page)
{
- u64 dpa;
+ return container_of(page_pgmap(page), struct xe_pagemap, pagemap);
+}
+
+static struct xe_vram_region *xe_page_to_vr(struct page *page)
+{
+ return xe_pagemap_to_vr(xe_page_to_pagemap(page));
+}
+
+static u64 xe_page_to_dpa(struct page *page)
+{
+ struct xe_pagemap *xpagemap = xe_page_to_pagemap(page);
+ struct xe_vram_region *vr = xe_pagemap_to_vr(xpagemap);
+ u64 hpa_base = xpagemap->hpa_base;
u64 pfn = page_to_pfn(page);
u64 offset;
+ u64 dpa;
xe_assert(vr->xe, is_device_private_page(page));
- xe_assert(vr->xe, (pfn << PAGE_SHIFT) >= vr->hpa_base);
+ xe_assert(vr->xe, (pfn << PAGE_SHIFT) >= hpa_base);
- offset = (pfn << PAGE_SHIFT) - vr->hpa_base;
+ offset = (pfn << PAGE_SHIFT) - hpa_base;
dpa = vr->dpa_base + offset;
return dpa;
}
+static u64 xe_page_to_pcie(struct page *page)
+{
+ struct xe_pagemap *xpagemap = xe_page_to_pagemap(page);
+ struct xe_vram_region *vr = xe_pagemap_to_vr(xpagemap);
+
+ return xe_page_to_dpa(page) - vr->dpa_base + vr->io_start;
+}
+
enum xe_svm_copy_dir {
XE_SVM_COPY_TO_VRAM,
XE_SVM_COPY_TO_SRAM,
@@ -435,7 +492,7 @@ static void xe_svm_copy_us_stats_incr(struct xe_gt *gt,
unsigned long npages,
ktime_t start)
{
- s64 us_delta = xe_svm_stats_ktime_us_delta(start);
+ s64 us_delta = xe_gt_stats_ktime_us_delta(start);
if (dir == XE_SVM_COPY_TO_VRAM) {
switch (npages) {
@@ -476,7 +533,8 @@ static void xe_svm_copy_us_stats_incr(struct xe_gt *gt,
static int xe_svm_copy(struct page **pages,
struct drm_pagemap_addr *pagemap_addr,
- unsigned long npages, const enum xe_svm_copy_dir dir)
+ unsigned long npages, const enum xe_svm_copy_dir dir,
+ struct dma_fence *pre_migrate_fence)
{
struct xe_vram_region *vr = NULL;
struct xe_gt *gt = NULL;
@@ -487,7 +545,7 @@ static int xe_svm_copy(struct page **pages,
u64 vram_addr = XE_VRAM_ADDR_INVALID;
int err = 0, pos = 0;
bool sram = dir == XE_SVM_COPY_TO_SRAM;
- ktime_t start = xe_svm_stats_ktime_get();
+ ktime_t start = xe_gt_stats_ktime_get();
/*
* This flow is complex: it locates physically contiguous device pages,
@@ -513,11 +571,11 @@ static int xe_svm_copy(struct page **pages,
continue;
if (!vr && spage) {
- vr = page_to_vr(spage);
+ vr = xe_page_to_vr(spage);
gt = xe_migrate_exec_queue(vr->migrate)->gt;
xe = vr->xe;
}
- XE_WARN_ON(spage && page_to_vr(spage) != vr);
+ XE_WARN_ON(spage && xe_page_to_vr(spage) != vr);
/*
* CPU page and device page valid, capture physical address on
@@ -525,7 +583,7 @@ static int xe_svm_copy(struct page **pages,
* device pages.
*/
if (pagemap_addr[i].addr && spage) {
- __vram_addr = xe_vram_region_page_to_dpa(vr, spage);
+ __vram_addr = xe_page_to_dpa(spage);
if (vram_addr == XE_VRAM_ADDR_INVALID) {
vram_addr = __vram_addr;
pos = i;
@@ -565,7 +623,8 @@ static int xe_svm_copy(struct page **pages,
__fence = xe_migrate_from_vram(vr->migrate,
i - pos + incr,
vram_addr,
- &pagemap_addr[pos]);
+ &pagemap_addr[pos],
+ pre_migrate_fence);
} else {
vm_dbg(&xe->drm,
"COPY TO VRAM - 0x%016llx -> 0x%016llx, NPAGES=%ld",
@@ -574,13 +633,14 @@ static int xe_svm_copy(struct page **pages,
__fence = xe_migrate_to_vram(vr->migrate,
i - pos + incr,
&pagemap_addr[pos],
- vram_addr);
+ vram_addr,
+ pre_migrate_fence);
}
if (IS_ERR(__fence)) {
err = PTR_ERR(__fence);
goto err_out;
}
-
+ pre_migrate_fence = NULL;
dma_fence_put(fence);
fence = __fence;
}
@@ -603,20 +663,22 @@ static int xe_svm_copy(struct page **pages,
vram_addr, (u64)pagemap_addr[pos].addr, 1);
__fence = xe_migrate_from_vram(vr->migrate, 1,
vram_addr,
- &pagemap_addr[pos]);
+ &pagemap_addr[pos],
+ pre_migrate_fence);
} else {
vm_dbg(&xe->drm,
"COPY TO VRAM - 0x%016llx -> 0x%016llx, NPAGES=%d",
(u64)pagemap_addr[pos].addr, vram_addr, 1);
__fence = xe_migrate_to_vram(vr->migrate, 1,
&pagemap_addr[pos],
- vram_addr);
+ vram_addr,
+ pre_migrate_fence);
}
if (IS_ERR(__fence)) {
err = PTR_ERR(__fence);
goto err_out;
}
-
+ pre_migrate_fence = NULL;
dma_fence_put(fence);
fence = __fence;
}
@@ -629,6 +691,8 @@ err_out:
dma_fence_wait(fence, false);
dma_fence_put(fence);
}
+ if (pre_migrate_fence)
+ dma_fence_wait(pre_migrate_fence, false);
/*
* XXX: We can't derive the GT here (or anywhere in this functions, but
@@ -645,16 +709,20 @@ err_out:
static int xe_svm_copy_to_devmem(struct page **pages,
struct drm_pagemap_addr *pagemap_addr,
- unsigned long npages)
+ unsigned long npages,
+ struct dma_fence *pre_migrate_fence)
{
- return xe_svm_copy(pages, pagemap_addr, npages, XE_SVM_COPY_TO_VRAM);
+ return xe_svm_copy(pages, pagemap_addr, npages, XE_SVM_COPY_TO_VRAM,
+ pre_migrate_fence);
}
static int xe_svm_copy_to_ram(struct page **pages,
struct drm_pagemap_addr *pagemap_addr,
- unsigned long npages)
+ unsigned long npages,
+ struct dma_fence *pre_migrate_fence)
{
- return xe_svm_copy(pages, pagemap_addr, npages, XE_SVM_COPY_TO_SRAM);
+ return xe_svm_copy(pages, pagemap_addr, npages, XE_SVM_COPY_TO_SRAM,
+ pre_migrate_fence);
}
static struct xe_bo *to_xe_bo(struct drm_pagemap_devmem *devmem_allocation)
@@ -667,13 +735,16 @@ static void xe_svm_devmem_release(struct drm_pagemap_devmem *devmem_allocation)
struct xe_bo *bo = to_xe_bo(devmem_allocation);
struct xe_device *xe = xe_bo_device(bo);
+ dma_fence_put(devmem_allocation->pre_migrate_fence);
xe_bo_put_async(bo);
xe_pm_runtime_put(xe);
}
-static u64 block_offset_to_pfn(struct xe_vram_region *vr, u64 offset)
+static u64 block_offset_to_pfn(struct drm_pagemap *dpagemap, u64 offset)
{
- return PHYS_PFN(offset + vr->hpa_base);
+ struct xe_pagemap *xpagemap = container_of(dpagemap, typeof(*xpagemap), dpagemap);
+
+ return PHYS_PFN(offset + xpagemap->hpa_base);
}
static struct drm_buddy *vram_to_buddy(struct xe_vram_region *vram)
@@ -693,7 +764,8 @@ static int xe_svm_populate_devmem_pfn(struct drm_pagemap_devmem *devmem_allocati
list_for_each_entry(block, blocks, link) {
struct xe_vram_region *vr = block->private;
struct drm_buddy *buddy = vram_to_buddy(vr);
- u64 block_pfn = block_offset_to_pfn(vr, drm_buddy_block_offset(block));
+ u64 block_pfn = block_offset_to_pfn(devmem_allocation->dpagemap,
+ drm_buddy_block_offset(block));
int i;
for (i = 0; i < drm_buddy_block_size(buddy, block) >> PAGE_SHIFT; ++i)
@@ -710,6 +782,11 @@ static const struct drm_pagemap_devmem_ops dpagemap_devmem_ops = {
.copy_to_ram = xe_svm_copy_to_ram,
};
+#else
+static int xe_svm_get_pagemaps(struct xe_vm *vm)
+{
+ return 0;
+}
#endif
static const struct drm_gpusvm_ops gpusvm_ops = {
@@ -724,6 +801,48 @@ static const unsigned long fault_chunk_sizes[] = {
SZ_4K,
};
+static void xe_pagemap_put(struct xe_pagemap *xpagemap)
+{
+ drm_pagemap_put(&xpagemap->dpagemap);
+}
+
+static void xe_svm_put_pagemaps(struct xe_vm *vm)
+{
+ struct xe_device *xe = vm->xe;
+ struct xe_tile *tile;
+ int id;
+
+ for_each_tile(tile, xe, id) {
+ struct xe_pagemap *xpagemap = vm->svm.pagemaps[id];
+
+ if (xpagemap)
+ xe_pagemap_put(xpagemap);
+ vm->svm.pagemaps[id] = NULL;
+ }
+}
+
+static struct device *xe_peer_to_dev(struct drm_pagemap_peer *peer)
+{
+ if (peer->private == XE_PEER_PAGEMAP)
+ return container_of(peer, struct xe_pagemap, peer)->dpagemap.drm->dev;
+
+ return container_of(peer, struct xe_vm, svm.peer)->xe->drm.dev;
+}
+
+static bool xe_has_interconnect(struct drm_pagemap_peer *peer1,
+ struct drm_pagemap_peer *peer2)
+{
+ struct device *dev1 = xe_peer_to_dev(peer1);
+ struct device *dev2 = xe_peer_to_dev(peer2);
+
+ if (dev1 == dev2)
+ return true;
+
+ return pci_p2pdma_distance(to_pci_dev(dev1), dev2, true) >= 0;
+}
+
+static DRM_PAGEMAP_OWNER_LIST_DEFINE(xe_owner_list);
+
/**
* xe_svm_init() - SVM initialize
* @vm: The VM.
@@ -742,12 +861,30 @@ int xe_svm_init(struct xe_vm *vm)
INIT_WORK(&vm->svm.garbage_collector.work,
xe_svm_garbage_collector_work_func);
+ vm->svm.peer.private = XE_PEER_VM;
+ err = drm_pagemap_acquire_owner(&vm->svm.peer, &xe_owner_list,
+ xe_has_interconnect);
+ if (err)
+ return err;
+
+ err = xe_svm_get_pagemaps(vm);
+ if (err) {
+ drm_pagemap_release_owner(&vm->svm.peer);
+ return err;
+ }
+
err = drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM", &vm->xe->drm,
current->mm, 0, vm->size,
xe_modparam.svm_notifier_size * SZ_1M,
&gpusvm_ops, fault_chunk_sizes,
ARRAY_SIZE(fault_chunk_sizes));
drm_gpusvm_driver_set_lock(&vm->svm.gpusvm, &vm->lock);
+
+ if (err) {
+ xe_svm_put_pagemaps(vm);
+ drm_pagemap_release_owner(&vm->svm.peer);
+ return err;
+ }
} else {
err = drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM (simple)",
&vm->xe->drm, NULL, 0, 0, 0, NULL,
@@ -767,6 +904,8 @@ void xe_svm_close(struct xe_vm *vm)
{
xe_assert(vm->xe, xe_vm_is_closed(vm));
flush_work(&vm->svm.garbage_collector.work);
+ xe_svm_put_pagemaps(vm);
+ drm_pagemap_release_owner(&vm->svm.peer);
}
/**
@@ -782,13 +921,34 @@ void xe_svm_fini(struct xe_vm *vm)
drm_gpusvm_fini(&vm->svm.gpusvm);
}
+static bool xe_svm_range_has_pagemap_locked(const struct xe_svm_range *range,
+ const struct drm_pagemap *dpagemap)
+{
+ return range->base.pages.dpagemap == dpagemap;
+}
+
+static bool xe_svm_range_has_pagemap(struct xe_svm_range *range,
+ const struct drm_pagemap *dpagemap)
+{
+ struct xe_vm *vm = range_to_vm(&range->base);
+ bool ret;
+
+ xe_svm_notifier_lock(vm);
+ ret = xe_svm_range_has_pagemap_locked(range, dpagemap);
+ xe_svm_notifier_unlock(vm);
+
+ return ret;
+}
+
static bool xe_svm_range_is_valid(struct xe_svm_range *range,
struct xe_tile *tile,
- bool devmem_only)
+ bool devmem_only,
+ const struct drm_pagemap *dpagemap)
+
{
return (xe_vm_has_valid_gpu_mapping(tile, range->tile_present,
range->tile_invalidated) &&
- (!devmem_only || xe_svm_range_in_vram(range)));
+ (!devmem_only || xe_svm_range_has_pagemap(range, dpagemap)));
}
/** xe_svm_range_migrate_to_smem() - Move range pages from VRAM to SMEM
@@ -809,7 +969,8 @@ void xe_svm_range_migrate_to_smem(struct xe_vm *vm, struct xe_svm_range *range)
* @vm: xe_vm pointer
* @range: Pointer to the SVM range structure
* @tile_mask: Mask representing the tiles to be checked
- * @devmem_preferred : if true range needs to be in devmem
+ * @dpagemap: if !%NULL, the range is expected to be present
+ * in device memory identified by this parameter.
*
* The xe_svm_range_validate() function checks if a range is
* valid and located in the desired memory region.
@@ -818,14 +979,17 @@ void xe_svm_range_migrate_to_smem(struct xe_vm *vm, struct xe_svm_range *range)
*/
bool xe_svm_range_validate(struct xe_vm *vm,
struct xe_svm_range *range,
- u8 tile_mask, bool devmem_preferred)
+ u8 tile_mask, const struct drm_pagemap *dpagemap)
{
bool ret;
xe_svm_notifier_lock(vm);
- ret = (range->tile_present & ~range->tile_invalidated & tile_mask) == tile_mask &&
- (devmem_preferred == range->base.pages.flags.has_devmem_pages);
+ ret = (range->tile_present & ~range->tile_invalidated & tile_mask) == tile_mask;
+ if (dpagemap)
+ ret = ret && xe_svm_range_has_pagemap_locked(range, dpagemap);
+ else
+ ret = ret && !range->base.pages.dpagemap;
xe_svm_notifier_unlock(vm);
@@ -860,7 +1024,13 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
struct mm_struct *mm,
unsigned long timeslice_ms)
{
- struct xe_vram_region *vr = container_of(dpagemap, typeof(*vr), dpagemap);
+ struct xe_pagemap *xpagemap = container_of(dpagemap, typeof(*xpagemap), dpagemap);
+ struct drm_pagemap_migrate_details mdetails = {
+ .timeslice_ms = timeslice_ms,
+ .source_peer_migrates = 1,
+ };
+ struct xe_vram_region *vr = xe_pagemap_to_vr(xpagemap);
+ struct dma_fence *pre_migrate_fence = NULL;
struct xe_device *xe = vr->xe;
struct device *dev = xe->drm.dev;
struct drm_buddy_block *block;
@@ -887,8 +1057,20 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
break;
}
+ /* Ensure that any clearing or async eviction will complete before migration. */
+ if (!dma_resv_test_signaled(bo->ttm.base.resv, DMA_RESV_USAGE_KERNEL)) {
+ err = dma_resv_get_singleton(bo->ttm.base.resv, DMA_RESV_USAGE_KERNEL,
+ &pre_migrate_fence);
+ if (err)
+ dma_resv_wait_timeout(bo->ttm.base.resv, DMA_RESV_USAGE_KERNEL,
+ false, MAX_SCHEDULE_TIMEOUT);
+ else if (pre_migrate_fence)
+ dma_fence_enable_sw_signaling(pre_migrate_fence);
+ }
+
drm_pagemap_devmem_init(&bo->devmem_allocation, dev, mm,
- &dpagemap_devmem_ops, dpagemap, end - start);
+ &dpagemap_devmem_ops, dpagemap, end - start,
+ pre_migrate_fence);
blocks = &to_xe_ttm_vram_mgr_resource(bo->ttm.resource)->blocks;
list_for_each_entry(block, blocks, link)
@@ -898,11 +1080,9 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
/* Ensure the device has a pm ref while there are device pages active. */
xe_pm_runtime_get_noresume(xe);
+ /* Consumes the devmem allocation ref. */
err = drm_pagemap_migrate_to_devmem(&bo->devmem_allocation, mm,
- start, end, timeslice_ms,
- xe_svm_devm_owner(xe));
- if (err)
- xe_svm_devmem_release(&bo->devmem_allocation);
+ start, end, &mdetails);
xe_bo_unlock(bo);
xe_bo_put(bo);
}
@@ -925,23 +1105,23 @@ static bool supports_4K_migration(struct xe_device *xe)
* xe_svm_range_needs_migrate_to_vram() - SVM range needs migrate to VRAM or not
* @range: SVM range for which migration needs to be decided
* @vma: vma which has range
- * @preferred_region_is_vram: preferred region for range is vram
+ * @dpagemap: The preferred struct drm_pagemap to migrate to.
*
* Return: True for range needing migration and migration is supported else false
*/
bool xe_svm_range_needs_migrate_to_vram(struct xe_svm_range *range, struct xe_vma *vma,
- bool preferred_region_is_vram)
+ const struct drm_pagemap *dpagemap)
{
struct xe_vm *vm = range_to_vm(&range->base);
u64 range_size = xe_svm_range_size(range);
- if (!range->base.pages.flags.migrate_devmem || !preferred_region_is_vram)
+ if (!range->base.pages.flags.migrate_devmem || !dpagemap)
return false;
xe_assert(vm->xe, IS_DGFX(vm->xe));
- if (xe_svm_range_in_vram(range)) {
- drm_info(&vm->xe->drm, "Range is already in VRAM\n");
+ if (xe_svm_range_has_pagemap(range, dpagemap)) {
+ drm_dbg(&vm->xe->drm, "Range is already in VRAM\n");
return false;
}
@@ -979,7 +1159,7 @@ static void xe_svm_range_##elem##_us_stats_incr(struct xe_gt *gt, \
struct xe_svm_range *range, \
ktime_t start) \
{ \
- s64 us_delta = xe_svm_stats_ktime_us_delta(start); \
+ s64 us_delta = xe_gt_stats_ktime_us_delta(start); \
\
switch (xe_svm_range_size(range)) { \
case SZ_4K: \
@@ -1015,7 +1195,6 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
.devmem_only = need_vram && devmem_possible,
.timeslice_ms = need_vram && devmem_possible ?
vm->xe->atomic_svm_timeslice_ms : 0,
- .device_private_page_owner = xe_svm_devm_owner(vm->xe),
};
struct xe_validation_ctx vctx;
struct drm_exec exec;
@@ -1024,7 +1203,7 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
struct drm_pagemap *dpagemap;
struct xe_tile *tile = gt_to_tile(gt);
int migrate_try_count = ctx.devmem_only ? 3 : 1;
- ktime_t start = xe_svm_stats_ktime_get(), bind_start, get_pages_start;
+ ktime_t start = xe_gt_stats_ktime_get(), bind_start, get_pages_start;
int err;
lockdep_assert_held_write(&vm->lock);
@@ -1038,9 +1217,9 @@ retry:
if (err)
return err;
- dpagemap = xe_vma_resolve_pagemap(vma, tile);
- if (!dpagemap && !ctx.devmem_only)
- ctx.device_private_page_owner = NULL;
+ dpagemap = ctx.devmem_only ? xe_tile_local_pagemap(tile) :
+ xe_vma_resolve_pagemap(vma, tile);
+ ctx.device_private_page_owner = xe_svm_private_page_owner(vm, !dpagemap);
range = xe_svm_range_find_or_insert(vm, fault_addr, vma, &ctx);
if (IS_ERR(range))
@@ -1053,7 +1232,7 @@ retry:
goto out;
}
- if (xe_svm_range_is_valid(range, tile, ctx.devmem_only)) {
+ if (xe_svm_range_is_valid(range, tile, ctx.devmem_only, dpagemap)) {
xe_svm_range_valid_fault_count_stats_incr(gt, range);
range_debug(range, "PAGE FAULT - VALID");
goto out;
@@ -1062,16 +1241,11 @@ retry:
range_debug(range, "PAGE FAULT");
if (--migrate_try_count >= 0 &&
- xe_svm_range_needs_migrate_to_vram(range, vma, !!dpagemap || ctx.devmem_only)) {
- ktime_t migrate_start = xe_svm_stats_ktime_get();
+ xe_svm_range_needs_migrate_to_vram(range, vma, dpagemap)) {
+ ktime_t migrate_start = xe_gt_stats_ktime_get();
- /* TODO : For multi-device dpagemap will be used to find the
- * remote tile and remote device. Will need to modify
- * xe_svm_alloc_vram to use dpagemap for future multi-device
- * support.
- */
xe_svm_range_migrate_count_stats_incr(gt, range);
- err = xe_svm_alloc_vram(tile, range, &ctx);
+ err = xe_svm_alloc_vram(range, &ctx, dpagemap);
xe_svm_range_migrate_us_stats_incr(gt, range, migrate_start);
ctx.timeslice_ms <<= 1; /* Double timeslice if we have to retry */
if (err) {
@@ -1100,7 +1274,7 @@ retry:
}
get_pages:
- get_pages_start = xe_svm_stats_ktime_get();
+ get_pages_start = xe_gt_stats_ktime_get();
range_debug(range, "GET PAGES");
err = xe_svm_range_get_pages(vm, range, &ctx);
@@ -1122,12 +1296,16 @@ get_pages:
if (err) {
range_debug(range, "PAGE FAULT - FAIL PAGE COLLECT");
goto out;
+ } else if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)) {
+ drm_dbg(&vm->xe->drm, "After page collect data location is %sin \"%s\".\n",
+ xe_svm_range_has_pagemap(range, dpagemap) ? "" : "NOT ",
+ dpagemap ? dpagemap->drm->unique : "System.");
}
xe_svm_range_get_pages_us_stats_incr(gt, range, get_pages_start);
range_debug(range, "PAGE FAULT - BIND");
- bind_start = xe_svm_stats_ktime_get();
+ bind_start = xe_gt_stats_ktime_get();
xe_validation_guard(&vctx, &vm->xe->val, &exec, (struct xe_val_flags) {}, err) {
err = xe_vm_drm_exec_lock(vm, &exec);
drm_exec_retry_on_contention(&exec);
@@ -1369,11 +1547,6 @@ u8 xe_svm_ranges_zap_ptes_in_range(struct xe_vm *vm, u64 start, u64 end)
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
-static struct drm_pagemap *tile_local_pagemap(struct xe_tile *tile)
-{
- return &tile->mem.vram->dpagemap;
-}
-
/**
* xe_vma_resolve_pagemap - Resolve the appropriate DRM pagemap for a VMA
* @vma: Pointer to the xe_vma structure containing memory attributes
@@ -1393,40 +1566,87 @@ static struct drm_pagemap *tile_local_pagemap(struct xe_tile *tile)
*/
struct drm_pagemap *xe_vma_resolve_pagemap(struct xe_vma *vma, struct xe_tile *tile)
{
- s32 fd = (s32)vma->attr.preferred_loc.devmem_fd;
+ struct drm_pagemap *dpagemap = vma->attr.preferred_loc.dpagemap;
+ s32 fd;
+
+ if (dpagemap)
+ return dpagemap;
+
+ fd = (s32)vma->attr.preferred_loc.devmem_fd;
if (fd == DRM_XE_PREFERRED_LOC_DEFAULT_SYSTEM)
return NULL;
if (fd == DRM_XE_PREFERRED_LOC_DEFAULT_DEVICE)
- return IS_DGFX(tile_to_xe(tile)) ? tile_local_pagemap(tile) : NULL;
+ return IS_DGFX(tile_to_xe(tile)) ? xe_tile_local_pagemap(tile) : NULL;
- /* TODO: Support multi-device with drm_pagemap_from_fd(fd) */
return NULL;
}
/**
* xe_svm_alloc_vram()- Allocate device memory pages for range,
* migrating existing data.
- * @tile: tile to allocate vram from
* @range: SVM range
* @ctx: DRM GPU SVM context
+ * @dpagemap: The struct drm_pagemap representing the memory to allocate.
*
* Return: 0 on success, error code on failure.
*/
-int xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range,
- const struct drm_gpusvm_ctx *ctx)
+int xe_svm_alloc_vram(struct xe_svm_range *range, const struct drm_gpusvm_ctx *ctx,
+ struct drm_pagemap *dpagemap)
{
- struct drm_pagemap *dpagemap;
+ static DECLARE_RWSEM(driver_migrate_lock);
+ struct xe_vm *vm = range_to_vm(&range->base);
+ enum drm_gpusvm_scan_result migration_state;
+ struct xe_device *xe = vm->xe;
+ int err, retries = 1;
+ bool write_locked = false;
- xe_assert(tile_to_xe(tile), range->base.pages.flags.migrate_devmem);
+ xe_assert(range_to_vm(&range->base)->xe, range->base.pages.flags.migrate_devmem);
range_debug(range, "ALLOCATE VRAM");
- dpagemap = tile_local_pagemap(tile);
- return drm_pagemap_populate_mm(dpagemap, xe_svm_range_start(range),
- xe_svm_range_end(range),
- range->base.gpusvm->mm,
- ctx->timeslice_ms);
+ migration_state = drm_gpusvm_scan_mm(&range->base,
+ xe_svm_private_page_owner(vm, false),
+ dpagemap->pagemap);
+
+ if (migration_state == DRM_GPUSVM_SCAN_EQUAL) {
+ if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM))
+ drm_dbg(dpagemap->drm, "Already migrated!\n");
+ return 0;
+ }
+
+ if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM))
+ drm_dbg(&xe->drm, "Request migration to device memory on \"%s\".\n",
+ dpagemap->drm->unique);
+
+ err = down_read_interruptible(&driver_migrate_lock);
+ if (err)
+ return err;
+ do {
+ err = drm_pagemap_populate_mm(dpagemap, xe_svm_range_start(range),
+ xe_svm_range_end(range),
+ range->base.gpusvm->mm,
+ ctx->timeslice_ms);
+
+ if (err == -EBUSY && retries) {
+ if (!write_locked) {
+ int lock_err;
+
+ up_read(&driver_migrate_lock);
+ lock_err = down_write_killable(&driver_migrate_lock);
+ if (lock_err)
+ return lock_err;
+ write_locked = true;
+ }
+ drm_gpusvm_range_evict(range->base.gpusvm, &range->base);
+ }
+ } while (err == -EBUSY && retries--);
+ if (write_locked)
+ up_write(&driver_migrate_lock);
+ else
+ up_read(&driver_migrate_lock);
+
+ return err;
}
static struct drm_pagemap_addr
@@ -1436,92 +1656,363 @@ xe_drm_pagemap_device_map(struct drm_pagemap *dpagemap,
unsigned int order,
enum dma_data_direction dir)
{
- struct device *pgmap_dev = dpagemap->dev;
+ struct device *pgmap_dev = dpagemap->drm->dev;
enum drm_interconnect_protocol prot;
dma_addr_t addr;
if (pgmap_dev == dev) {
- addr = xe_vram_region_page_to_dpa(page_to_vr(page), page);
+ addr = xe_page_to_dpa(page);
prot = XE_INTERCONNECT_VRAM;
} else {
- addr = DMA_MAPPING_ERROR;
- prot = 0;
+ addr = dma_map_resource(dev,
+ xe_page_to_pcie(page),
+ PAGE_SIZE << order, dir,
+ DMA_ATTR_SKIP_CPU_SYNC);
+ prot = XE_INTERCONNECT_P2P;
}
return drm_pagemap_addr_encode(addr, prot, order, dir);
}
+static void xe_drm_pagemap_device_unmap(struct drm_pagemap *dpagemap,
+ struct device *dev,
+ struct drm_pagemap_addr addr)
+{
+ if (addr.proto != XE_INTERCONNECT_P2P)
+ return;
+
+ dma_unmap_resource(dev, addr.addr, PAGE_SIZE << addr.order,
+ addr.dir, DMA_ATTR_SKIP_CPU_SYNC);
+}
+
+static void xe_pagemap_destroy_work(struct work_struct *work)
+{
+ struct xe_pagemap *xpagemap = container_of(work, typeof(*xpagemap), destroy_work);
+ struct dev_pagemap *pagemap = &xpagemap->pagemap;
+ struct drm_device *drm = xpagemap->dpagemap.drm;
+ int idx;
+
+ /*
+ * Only unmap / release if devm_ release hasn't run yet.
+ * Otherwise the devm_ callbacks have already released, or
+ * will do shortly.
+ */
+ if (drm_dev_enter(drm, &idx)) {
+ devm_memunmap_pages(drm->dev, pagemap);
+ devm_release_mem_region(drm->dev, pagemap->range.start,
+ pagemap->range.end - pagemap->range.start + 1);
+ drm_dev_exit(idx);
+ }
+
+ drm_pagemap_release_owner(&xpagemap->peer);
+ kfree(xpagemap);
+}
+
+static void xe_pagemap_destroy(struct drm_pagemap *dpagemap, bool from_atomic_or_reclaim)
+{
+ struct xe_pagemap *xpagemap = container_of(dpagemap, typeof(*xpagemap), dpagemap);
+ struct xe_device *xe = to_xe_device(dpagemap->drm);
+
+ if (from_atomic_or_reclaim)
+ queue_work(xe->destroy_wq, &xpagemap->destroy_work);
+ else
+ xe_pagemap_destroy_work(&xpagemap->destroy_work);
+}
+
static const struct drm_pagemap_ops xe_drm_pagemap_ops = {
.device_map = xe_drm_pagemap_device_map,
+ .device_unmap = xe_drm_pagemap_device_unmap,
.populate_mm = xe_drm_pagemap_populate_mm,
+ .destroy = xe_pagemap_destroy,
};
/**
- * xe_devm_add: Remap and provide memmap backing for device memory
- * @tile: tile that the memory region belongs to
- * @vr: vram memory region to remap
+ * xe_pagemap_create() - Create a struct xe_pagemap object
+ * @xe: The xe device.
+ * @vr: Back-pointer to the struct xe_vram_region.
*
- * This remap device memory to host physical address space and create
- * struct page to back device memory
+ * Allocate and initialize a struct xe_pagemap. On successful
+ * return, drm_pagemap_put() on the embedded struct drm_pagemap
+ * should be used to unreference.
*
- * Return: 0 on success standard error code otherwise
+ * Return: Pointer to a struct xe_pagemap if successful. Error pointer
+ * on failure.
*/
-int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)
+static struct xe_pagemap *xe_pagemap_create(struct xe_device *xe, struct xe_vram_region *vr)
{
- struct xe_device *xe = tile_to_xe(tile);
- struct device *dev = &to_pci_dev(xe->drm.dev)->dev;
+ struct device *dev = xe->drm.dev;
+ struct xe_pagemap *xpagemap;
+ struct dev_pagemap *pagemap;
+ struct drm_pagemap *dpagemap;
struct resource *res;
void *addr;
- int ret;
+ int err;
+
+ xpagemap = kzalloc(sizeof(*xpagemap), GFP_KERNEL);
+ if (!xpagemap)
+ return ERR_PTR(-ENOMEM);
+
+ pagemap = &xpagemap->pagemap;
+ dpagemap = &xpagemap->dpagemap;
+ INIT_WORK(&xpagemap->destroy_work, xe_pagemap_destroy_work);
+ xpagemap->vr = vr;
+ xpagemap->peer.private = XE_PEER_PAGEMAP;
+
+ err = drm_pagemap_init(dpagemap, pagemap, &xe->drm, &xe_drm_pagemap_ops);
+ if (err)
+ goto out_no_dpagemap;
res = devm_request_free_mem_region(dev, &iomem_resource,
vr->usable_size);
if (IS_ERR(res)) {
- ret = PTR_ERR(res);
- return ret;
+ err = PTR_ERR(res);
+ goto out_err;
}
- vr->pagemap.type = MEMORY_DEVICE_PRIVATE;
- vr->pagemap.range.start = res->start;
- vr->pagemap.range.end = res->end;
- vr->pagemap.nr_range = 1;
- vr->pagemap.ops = drm_pagemap_pagemap_ops_get();
- vr->pagemap.owner = xe_svm_devm_owner(xe);
- addr = devm_memremap_pages(dev, &vr->pagemap);
+ err = drm_pagemap_acquire_owner(&xpagemap->peer, &xe_owner_list,
+ xe_has_interconnect);
+ if (err)
+ goto out_no_owner;
+
+ pagemap->type = MEMORY_DEVICE_PRIVATE;
+ pagemap->range.start = res->start;
+ pagemap->range.end = res->end;
+ pagemap->nr_range = 1;
+ pagemap->owner = xpagemap->peer.owner;
+ pagemap->ops = drm_pagemap_pagemap_ops_get();
+ addr = devm_memremap_pages(dev, pagemap);
+ if (IS_ERR(addr)) {
+ err = PTR_ERR(addr);
+ goto out_no_pages;
+ }
+ xpagemap->hpa_base = res->start;
+ return xpagemap;
+
+out_no_pages:
+ drm_pagemap_release_owner(&xpagemap->peer);
+out_no_owner:
+ devm_release_mem_region(dev, res->start, res->end - res->start + 1);
+out_err:
+ drm_pagemap_put(dpagemap);
+ return ERR_PTR(err);
+
+out_no_dpagemap:
+ kfree(xpagemap);
+ return ERR_PTR(err);
+}
- vr->dpagemap.dev = dev;
- vr->dpagemap.ops = &xe_drm_pagemap_ops;
+/**
+ * xe_pagemap_find_or_create() - Find or create a struct xe_pagemap
+ * @xe: The xe device.
+ * @cache: The struct xe_pagemap_cache.
+ * @vr: The VRAM region.
+ *
+ * Check if there is an already used xe_pagemap for this tile, and in that case,
+ * return it.
+ * If not, check if there is a cached xe_pagemap for this tile, and in that case,
+ * cancel its destruction, re-initialize it and return it.
+ * Finally if there is no cached or already used pagemap, create one and
+ * register it in the tile's pagemap cache.
+ *
+ * Note that this function is typically called from within an IOCTL, and waits are
+ * therefore carried out interruptible if possible.
+ *
+ * Return: A pointer to a struct xe_pagemap if successful, Error pointer on failure.
+ */
+static struct xe_pagemap *
+xe_pagemap_find_or_create(struct xe_device *xe, struct drm_pagemap_cache *cache,
+ struct xe_vram_region *vr)
+{
+ struct drm_pagemap *dpagemap;
+ struct xe_pagemap *xpagemap;
+ int err;
- if (IS_ERR(addr)) {
- devm_release_mem_region(dev, res->start, resource_size(res));
- ret = PTR_ERR(addr);
- drm_err(&xe->drm, "Failed to remap tile %d memory, errno %pe\n",
- tile->id, ERR_PTR(ret));
- return ret;
+ err = drm_pagemap_cache_lock_lookup(cache);
+ if (err)
+ return ERR_PTR(err);
+
+ dpagemap = drm_pagemap_get_from_cache(cache);
+ if (IS_ERR(dpagemap)) {
+ xpagemap = ERR_CAST(dpagemap);
+ } else if (!dpagemap) {
+ xpagemap = xe_pagemap_create(xe, vr);
+ if (IS_ERR(xpagemap))
+ goto out_unlock;
+ drm_pagemap_cache_set_pagemap(cache, &xpagemap->dpagemap);
+ } else {
+ xpagemap = container_of(dpagemap, typeof(*xpagemap), dpagemap);
+ }
+
+out_unlock:
+ drm_pagemap_cache_unlock_lookup(cache);
+ return xpagemap;
+}
+
+static int xe_svm_get_pagemaps(struct xe_vm *vm)
+{
+ struct xe_device *xe = vm->xe;
+ struct xe_pagemap *xpagemap;
+ struct xe_tile *tile;
+ int id;
+
+ for_each_tile(tile, xe, id) {
+ struct xe_vram_region *vr;
+
+ if (!((BIT(id) << 1) & xe->info.mem_region_mask))
+ continue;
+
+ vr = xe_tile_to_vr(tile);
+ xpagemap = xe_pagemap_find_or_create(xe, vr->dpagemap_cache, vr);
+ if (IS_ERR(xpagemap))
+ break;
+ vm->svm.pagemaps[id] = xpagemap;
+ }
+
+ if (IS_ERR(xpagemap)) {
+ xe_svm_put_pagemaps(vm);
+ return PTR_ERR(xpagemap);
}
- vr->hpa_base = res->start;
- drm_dbg(&xe->drm, "Added tile %d memory [%llx-%llx] to devm, remapped to %pr\n",
- tile->id, vr->io_start, vr->io_start + vr->usable_size, res);
return 0;
}
+
+/**
+ * xe_pagemap_shrinker_create() - Create a drm_pagemap shrinker
+ * @xe: The xe device
+ *
+ * Create a drm_pagemap shrinker and register with the xe device.
+ *
+ * Return: %0 on success, negative error code on failure.
+ */
+int xe_pagemap_shrinker_create(struct xe_device *xe)
+{
+ xe->usm.dpagemap_shrinker = drm_pagemap_shrinker_create_devm(&xe->drm);
+ return PTR_ERR_OR_ZERO(xe->usm.dpagemap_shrinker);
+}
+
+/**
+ * xe_pagemap_cache_create() - Create a drm_pagemap cache
+ * @tile: The tile to register the cache with
+ *
+ * Create a drm_pagemap cache and register with the tile.
+ *
+ * Return: %0 on success, negative error code on failure.
+ */
+int xe_pagemap_cache_create(struct xe_tile *tile)
+{
+ struct xe_device *xe = tile_to_xe(tile);
+
+ if (IS_DGFX(xe)) {
+ struct drm_pagemap_cache *cache =
+ drm_pagemap_cache_create_devm(xe->usm.dpagemap_shrinker);
+
+ if (IS_ERR(cache))
+ return PTR_ERR(cache);
+
+ tile->mem.vram->dpagemap_cache = cache;
+ }
+
+ return 0;
+}
+
+static struct drm_pagemap *xe_devmem_open(struct xe_device *xe, u32 region_instance)
+{
+ u32 tile_id = region_instance - 1;
+ struct xe_pagemap *xpagemap;
+ struct xe_vram_region *vr;
+
+ if (tile_id >= xe->info.tile_count)
+ return ERR_PTR(-ENOENT);
+
+ if (!((BIT(tile_id) << 1) & xe->info.mem_region_mask))
+ return ERR_PTR(-ENOENT);
+
+ vr = xe_tile_to_vr(&xe->tiles[tile_id]);
+
+ /* Returns a reference-counted embedded struct drm_pagemap */
+ xpagemap = xe_pagemap_find_or_create(xe, vr->dpagemap_cache, vr);
+ if (IS_ERR(xpagemap))
+ return ERR_CAST(xpagemap);
+
+ return &xpagemap->dpagemap;
+}
+
+/**
+ * xe_drm_pagemap_from_fd() - Return a drm_pagemap pointer from a
+ * (file_descriptor, region_instance) pair.
+ * @fd: An fd opened against an xe device.
+ * @region_instance: The region instance representing the device memory
+ * on the opened xe device.
+ *
+ * Opens a struct drm_pagemap pointer on the
+ * indicated device and region_instance.
+ *
+ * Return: A reference-counted struct drm_pagemap pointer on success,
+ * negative error pointer on failure.
+ */
+struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_instance)
+{
+ struct drm_pagemap *dpagemap;
+ struct file *file;
+ struct drm_file *fpriv;
+ struct drm_device *drm;
+ int idx;
+
+ if (fd <= 0)
+ return ERR_PTR(-EINVAL);
+
+ file = fget(fd);
+ if (!file)
+ return ERR_PTR(-ENOENT);
+
+ if (!xe_is_xe_file(file)) {
+ dpagemap = ERR_PTR(-ENOENT);
+ goto out;
+ }
+
+ fpriv = file->private_data;
+ drm = fpriv->minor->dev;
+ if (!drm_dev_enter(drm, &idx)) {
+ dpagemap = ERR_PTR(-ENODEV);
+ goto out;
+ }
+
+ dpagemap = xe_devmem_open(to_xe_device(drm), region_instance);
+ drm_dev_exit(idx);
+out:
+ fput(file);
+ return dpagemap;
+}
+
#else
-int xe_svm_alloc_vram(struct xe_tile *tile,
- struct xe_svm_range *range,
- const struct drm_gpusvm_ctx *ctx)
+
+int xe_pagemap_shrinker_create(struct xe_device *xe)
{
- return -EOPNOTSUPP;
+ return 0;
}
-int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)
+int xe_pagemap_cache_create(struct xe_tile *tile)
{
return 0;
}
+int xe_svm_alloc_vram(struct xe_svm_range *range,
+ const struct drm_gpusvm_ctx *ctx,
+ struct drm_pagemap *dpagemap)
+{
+ return -EOPNOTSUPP;
+}
+
struct drm_pagemap *xe_vma_resolve_pagemap(struct xe_vma *vma, struct xe_tile *tile)
{
return NULL;
}
+
+struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_instance)
+{
+ return ERR_PTR(-ENOENT);
+}
+
#endif
/**
diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h
index 0955d2ac8d74..b7b8eeacf196 100644
--- a/drivers/gpu/drm/xe/xe_svm.h
+++ b/drivers/gpu/drm/xe/xe_svm.h
@@ -6,29 +6,22 @@
#ifndef _XE_SVM_H_
#define _XE_SVM_H_
-struct xe_device;
-
-/**
- * xe_svm_devm_owner() - Return the owner of device private memory
- * @xe: The xe device.
- *
- * Return: The owner of this device's device private memory to use in
- * hmm_range_fault()-
- */
-static inline void *xe_svm_devm_owner(struct xe_device *xe)
-{
- return xe;
-}
-
#if IS_ENABLED(CONFIG_DRM_XE_GPUSVM)
#include <drm/drm_pagemap.h>
#include <drm/drm_gpusvm.h>
+#include <drm/drm_pagemap_util.h>
#define XE_INTERCONNECT_VRAM DRM_INTERCONNECT_DRIVER
+#define XE_INTERCONNECT_P2P (XE_INTERCONNECT_VRAM + 1)
+
+struct drm_device;
+struct drm_file;
struct xe_bo;
struct xe_gt;
+struct xe_device;
+struct xe_vram_region;
struct xe_tile;
struct xe_vm;
struct xe_vma;
@@ -56,6 +49,24 @@ struct xe_svm_range {
};
/**
+ * struct xe_pagemap - Manages xe device_private memory for SVM.
+ * @pagemap: The struct dev_pagemap providing the struct pages.
+ * @dpagemap: The drm_pagemap managing allocation and migration.
+ * @destroy_work: Handles asnynchronous destruction and caching.
+ * @peer: Used for pagemap owner computation.
+ * @hpa_base: The host physical address base for the managemd memory.
+ * @vr: Backpointer to the xe_vram region.
+ */
+struct xe_pagemap {
+ struct dev_pagemap pagemap;
+ struct drm_pagemap dpagemap;
+ struct work_struct destroy_work;
+ struct drm_pagemap_peer peer;
+ resource_size_t hpa_base;
+ struct xe_vram_region *vr;
+};
+
+/**
* xe_svm_range_pages_valid() - SVM range pages valid
* @range: SVM range
*
@@ -84,8 +95,8 @@ int xe_svm_bo_evict(struct xe_bo *bo);
void xe_svm_range_debug(struct xe_svm_range *range, const char *operation);
-int xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range,
- const struct drm_gpusvm_ctx *ctx);
+int xe_svm_alloc_vram(struct xe_svm_range *range, const struct drm_gpusvm_ctx *ctx,
+ struct drm_pagemap *dpagemap);
struct xe_svm_range *xe_svm_range_find_or_insert(struct xe_vm *vm, u64 addr,
struct xe_vma *vma, struct drm_gpusvm_ctx *ctx);
@@ -94,13 +105,13 @@ int xe_svm_range_get_pages(struct xe_vm *vm, struct xe_svm_range *range,
struct drm_gpusvm_ctx *ctx);
bool xe_svm_range_needs_migrate_to_vram(struct xe_svm_range *range, struct xe_vma *vma,
- bool preferred_region_is_vram);
+ const struct drm_pagemap *dpagemap);
void xe_svm_range_migrate_to_smem(struct xe_vm *vm, struct xe_svm_range *range);
bool xe_svm_range_validate(struct xe_vm *vm,
struct xe_svm_range *range,
- u8 tile_mask, bool devmem_preferred);
+ u8 tile_mask, const struct drm_pagemap *dpagemap);
u64 xe_svm_find_vma_start(struct xe_vm *vm, u64 addr, u64 end, struct xe_vma *vma);
@@ -110,6 +121,8 @@ u8 xe_svm_ranges_zap_ptes_in_range(struct xe_vm *vm, u64 start, u64 end);
struct drm_pagemap *xe_vma_resolve_pagemap(struct xe_vma *vma, struct xe_tile *tile);
+void *xe_svm_private_page_owner(struct xe_vm *vm, bool force_smem);
+
/**
* xe_svm_range_has_dma_mapping() - SVM range has DMA mapping
* @range: SVM range
@@ -171,6 +184,12 @@ static inline unsigned long xe_svm_range_size(struct xe_svm_range *range)
void xe_svm_flush(struct xe_vm *vm);
+int xe_pagemap_shrinker_create(struct xe_device *xe);
+
+int xe_pagemap_cache_create(struct xe_tile *tile);
+
+struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_instance);
+
#else
#include <linux/interval_tree.h>
#include "xe_vm.h"
@@ -179,13 +198,14 @@ struct drm_pagemap_addr;
struct drm_gpusvm_ctx;
struct drm_gpusvm_range;
struct xe_bo;
-struct xe_gt;
+struct xe_device;
struct xe_vm;
struct xe_vma;
struct xe_tile;
struct xe_vram_region;
#define XE_INTERCONNECT_VRAM 1
+#define XE_INTERCONNECT_P2P (XE_INTERCONNECT_VRAM + 1)
struct xe_svm_range {
struct {
@@ -214,7 +234,7 @@ int xe_svm_init(struct xe_vm *vm)
{
#if IS_ENABLED(CONFIG_DRM_GPUSVM)
return drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM (simple)", &vm->xe->drm,
- NULL, NULL, 0, 0, 0, NULL, NULL, 0);
+ NULL, 0, 0, 0, NULL, NULL, 0);
#else
return 0;
#endif
@@ -260,8 +280,8 @@ void xe_svm_range_debug(struct xe_svm_range *range, const char *operation)
}
static inline int
-xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range,
- const struct drm_gpusvm_ctx *ctx)
+xe_svm_alloc_vram(struct xe_svm_range *range, const struct drm_gpusvm_ctx *ctx,
+ struct drm_pagemap *dpagemap)
{
return -EOPNOTSUPP;
}
@@ -302,7 +322,7 @@ static inline unsigned long xe_svm_range_size(struct xe_svm_range *range)
static inline
bool xe_svm_range_needs_migrate_to_vram(struct xe_svm_range *range, struct xe_vma *vma,
- u32 region)
+ const struct drm_pagemap *dpagemap)
{
return false;
}
@@ -343,9 +363,30 @@ struct drm_pagemap *xe_vma_resolve_pagemap(struct xe_vma *vma, struct xe_tile *t
return NULL;
}
+static inline void *xe_svm_private_page_owner(struct xe_vm *vm, bool force_smem)
+{
+ return NULL;
+}
+
static inline void xe_svm_flush(struct xe_vm *vm)
{
}
+
+static inline int xe_pagemap_shrinker_create(struct xe_device *xe)
+{
+ return 0;
+}
+
+static inline int xe_pagemap_cache_create(struct xe_tile *tile)
+{
+ return 0;
+}
+
+static inline struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_instance)
+{
+ return ERR_PTR(-ENOENT);
+}
+
#define xe_svm_range_has_dma_mapping(...) false
#endif /* CONFIG_DRM_XE_GPUSVM */
diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c
index ff74528ca0c6..c8fdcdbd6ae7 100644
--- a/drivers/gpu/drm/xe/xe_sync.c
+++ b/drivers/gpu/drm/xe/xe_sync.c
@@ -228,6 +228,32 @@ int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job)
return 0;
}
+/**
+ * xe_sync_entry_wait() - Wait on in-sync
+ * @sync: Sync object
+ *
+ * If the sync is in an in-sync, wait on the sync to signal.
+ *
+ * Return: 0 on success, -ERESTARTSYS on failure (interruption)
+ */
+int xe_sync_entry_wait(struct xe_sync_entry *sync)
+{
+ return xe_sync_needs_wait(sync) ?
+ dma_fence_wait(sync->fence, true) : 0;
+}
+
+/**
+ * xe_sync_needs_wait() - Sync needs a wait (input dma-fence not signaled)
+ * @sync: Sync object
+ *
+ * Return: True if sync needs a wait, False otherwise
+ */
+bool xe_sync_needs_wait(struct xe_sync_entry *sync)
+{
+ return sync->fence &&
+ !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &sync->fence->flags);
+}
+
void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence)
{
if (!(sync->flags & DRM_XE_SYNC_FLAG_SIGNAL))
@@ -311,8 +337,11 @@ xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
struct xe_tile *tile;
u8 id;
- for_each_tile(tile, vm->xe, id)
- num_fence += (1 + XE_MAX_GT_PER_TILE);
+ for_each_tile(tile, vm->xe, id) {
+ num_fence++;
+ for_each_tlb_inval(i)
+ num_fence++;
+ }
fences = kmalloc_array(num_fence, sizeof(*fences),
GFP_KERNEL);
diff --git a/drivers/gpu/drm/xe/xe_sync.h b/drivers/gpu/drm/xe/xe_sync.h
index 51f2d803e977..6b949194acff 100644
--- a/drivers/gpu/drm/xe/xe_sync.h
+++ b/drivers/gpu/drm/xe/xe_sync.h
@@ -29,6 +29,8 @@ int xe_sync_entry_add_deps(struct xe_sync_entry *sync,
struct xe_sched_job *job);
void xe_sync_entry_signal(struct xe_sync_entry *sync,
struct dma_fence *fence);
+int xe_sync_entry_wait(struct xe_sync_entry *sync);
+bool xe_sync_needs_wait(struct xe_sync_entry *sync);
void xe_sync_entry_cleanup(struct xe_sync_entry *sync);
struct dma_fence *
xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
diff --git a/drivers/gpu/drm/xe/xe_tile.c b/drivers/gpu/drm/xe/xe_tile.c
index 4f4f9a5c43af..c465aae7883c 100644
--- a/drivers/gpu/drm/xe/xe_tile.c
+++ b/drivers/gpu/drm/xe/xe_tile.c
@@ -6,11 +6,11 @@
#include <linux/fault-inject.h>
#include <drm/drm_managed.h>
+#include <drm/drm_pagemap_util.h>
#include "xe_bo.h"
-#include "xe_device.h"
+#include "xe_device_types.h"
#include "xe_ggtt.h"
-#include "xe_gt.h"
#include "xe_memirq.h"
#include "xe_migrate.h"
#include "xe_pcode.h"
@@ -180,17 +180,19 @@ ALLOW_ERROR_INJECTION(xe_tile_init_early, ERRNO); /* See xe_pci_probe() */
int xe_tile_init_noalloc(struct xe_tile *tile)
{
struct xe_device *xe = tile_to_xe(tile);
+ int err;
xe_wa_apply_tile_workarounds(tile);
- if (xe->info.has_usm && IS_DGFX(xe))
- xe_devm_add(tile, tile->mem.vram);
+ err = xe_pagemap_cache_create(tile);
+ if (err)
+ return err;
if (IS_DGFX(xe) && !ttm_resource_manager_used(&tile->mem.vram->ttm.manager)) {
- int err = xe_ttm_vram_mgr_init(xe, tile->mem.vram);
-
+ err = xe_ttm_vram_mgr_init(xe, tile->mem.vram);
if (err)
return err;
+
xe->info.mem_region_mask |= BIT(tile->mem.vram->id) << 1;
}
@@ -209,9 +211,37 @@ int xe_tile_init(struct xe_tile *tile)
if (IS_ERR(tile->mem.kernel_bb_pool))
return PTR_ERR(tile->mem.kernel_bb_pool);
+ /* Optimistically anticipate at most 256 TLB fences with PRL */
+ tile->mem.reclaim_pool = xe_sa_bo_manager_init(tile, SZ_1M, XE_PAGE_RECLAIM_LIST_MAX_SIZE);
+ if (IS_ERR(tile->mem.reclaim_pool))
+ return PTR_ERR(tile->mem.reclaim_pool);
+
return 0;
}
void xe_tile_migrate_wait(struct xe_tile *tile)
{
xe_migrate_wait(tile->migrate);
}
+
+#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
+/**
+ * xe_tile_local_pagemap() - Return a pointer to the tile's local drm_pagemap if any
+ * @tile: The tile.
+ *
+ * Return: A pointer to the tile's local drm_pagemap, or NULL if local pagemap
+ * support has been compiled out.
+ */
+struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile)
+{
+ struct drm_pagemap *dpagemap =
+ drm_pagemap_get_from_cache_if_active(xe_tile_to_vr(tile)->dpagemap_cache);
+
+ if (dpagemap) {
+ xe_assert(tile_to_xe(tile), kref_read(&dpagemap->ref) >= 2);
+ drm_pagemap_put(dpagemap);
+ }
+
+ return dpagemap;
+}
+#endif
+
diff --git a/drivers/gpu/drm/xe/xe_tile.h b/drivers/gpu/drm/xe/xe_tile.h
index dceb6297aa01..734132eddda5 100644
--- a/drivers/gpu/drm/xe/xe_tile.h
+++ b/drivers/gpu/drm/xe/xe_tile.h
@@ -8,6 +8,7 @@
#include "xe_device_types.h"
+struct xe_pagemap;
struct xe_tile;
int xe_tile_init_early(struct xe_tile *tile, struct xe_device *xe, u8 id);
@@ -23,4 +24,24 @@ static inline bool xe_tile_is_root(struct xe_tile *tile)
return tile->id == 0;
}
+/**
+ * xe_tile_to_vr() - Return the struct xe_vram_region pointer from a
+ * struct xe_tile pointer
+ * @tile: Pointer to the struct xe_tile.
+ *
+ * Return: Pointer to the struct xe_vram_region embedded in *@tile.
+ */
+static inline struct xe_vram_region *xe_tile_to_vr(struct xe_tile *tile)
+{
+ return tile->mem.vram;
+}
+
+#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
+struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile);
+#else
+static inline struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile)
+{
+ return NULL;
+}
+#endif
#endif
diff --git a/drivers/gpu/drm/xe/xe_tile_debugfs.c b/drivers/gpu/drm/xe/xe_tile_debugfs.c
index fff242a5ae56..5df2f461b7b7 100644
--- a/drivers/gpu/drm/xe/xe_tile_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_tile_debugfs.c
@@ -82,13 +82,9 @@ int xe_tile_debugfs_show_with_rpm(struct seq_file *m, void *data)
struct drm_info_node *node = m->private;
struct xe_tile *tile = node_to_tile(node);
struct xe_device *xe = tile_to_xe(tile);
- int ret;
- xe_pm_runtime_get(xe);
- ret = xe_tile_debugfs_simple_show(m, data);
- xe_pm_runtime_put(xe);
-
- return ret;
+ guard(xe_pm_runtime)(xe);
+ return xe_tile_debugfs_simple_show(m, data);
}
static int ggtt(struct xe_tile *tile, struct drm_printer *p)
@@ -110,6 +106,13 @@ static const struct drm_info_list vf_safe_debugfs_list[] = {
{ "sa_info", .show = xe_tile_debugfs_show_with_rpm, .data = sa_info },
};
+static void tile_debugfs_create_vram_mm(struct xe_tile *tile)
+{
+ if (tile->mem.vram)
+ ttm_resource_manager_create_debugfs(&tile->mem.vram->ttm.manager, tile->debugfs,
+ "vram_mm");
+}
+
/**
* xe_tile_debugfs_register - Register tile's debugfs attributes
* @tile: the &xe_tile to register
@@ -139,4 +142,6 @@ void xe_tile_debugfs_register(struct xe_tile *tile)
drm_debugfs_create_files(vf_safe_debugfs_list,
ARRAY_SIZE(vf_safe_debugfs_list),
tile->debugfs, minor);
+
+ tile_debugfs_create_vram_mm(tile);
}
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c
index f3f478f14ff5..7f97db2f89bb 100644
--- a/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c
@@ -141,12 +141,11 @@ static int NAME##_set(void *data, u64 val) \
if (val > (TYPE)~0ull) \
return -EOVERFLOW; \
\
- xe_pm_runtime_get(xe); \
+ guard(xe_pm_runtime)(xe); \
err = xe_sriov_pf_wait_ready(xe) ?: \
xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \
if (!err) \
xe_sriov_pf_provision_set_custom_mode(xe); \
- xe_pm_runtime_put(xe); \
\
return err; \
} \
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval.c b/drivers/gpu/drm/xe/xe_tlb_inval.c
index 918a59e686ea..e837888367c4 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval.c
+++ b/drivers/gpu/drm/xe/xe_tlb_inval.c
@@ -5,13 +5,10 @@
#include <drm/drm_managed.h>
-#include "abi/guc_actions_abi.h"
-#include "xe_device.h"
+#include "xe_device_types.h"
#include "xe_force_wake.h"
-#include "xe_gt.h"
-#include "xe_gt_printk.h"
#include "xe_gt_stats.h"
-#include "xe_guc.h"
+#include "xe_gt_types.h"
#include "xe_guc_ct.h"
#include "xe_guc_tlb_inval.h"
#include "xe_mmio.h"
@@ -94,7 +91,7 @@ static void xe_tlb_inval_fence_timeout(struct work_struct *work)
xe_tlb_inval_fence_signal(fence);
}
if (!list_empty(&tlb_inval->pending_fences))
- queue_delayed_work(system_wq, &tlb_inval->fence_tdr,
+ queue_delayed_work(tlb_inval->timeout_wq, &tlb_inval->fence_tdr,
timeout_delay);
spin_unlock_irq(&tlb_inval->pending_lock);
}
@@ -146,6 +143,10 @@ int xe_gt_tlb_inval_init_early(struct xe_gt *gt)
if (IS_ERR(tlb_inval->job_wq))
return PTR_ERR(tlb_inval->job_wq);
+ tlb_inval->timeout_wq = gt->ordered_wq;
+ if (IS_ERR(tlb_inval->timeout_wq))
+ return PTR_ERR(tlb_inval->timeout_wq);
+
/* XXX: Blindly setting up backend to GuC */
xe_guc_tlb_inval_init_early(&gt->uc.guc, tlb_inval);
@@ -199,6 +200,20 @@ void xe_tlb_inval_reset(struct xe_tlb_inval *tlb_inval)
mutex_unlock(&tlb_inval->seqno_lock);
}
+/**
+ * xe_tlb_inval_reset_timeout() - Reset TLB inval fence timeout
+ * @tlb_inval: TLB invalidation client
+ *
+ * Reset the TLB invalidation timeout timer.
+ */
+static void xe_tlb_inval_reset_timeout(struct xe_tlb_inval *tlb_inval)
+{
+ lockdep_assert_held(&tlb_inval->pending_lock);
+
+ mod_delayed_work(system_wq, &tlb_inval->fence_tdr,
+ tlb_inval->ops->timeout_delay(tlb_inval));
+}
+
static bool xe_tlb_inval_seqno_past(struct xe_tlb_inval *tlb_inval, int seqno)
{
int seqno_recv = READ_ONCE(tlb_inval->seqno_recv);
@@ -226,7 +241,7 @@ static void xe_tlb_inval_fence_prep(struct xe_tlb_inval_fence *fence)
list_add_tail(&fence->link, &tlb_inval->pending_fences);
if (list_is_singular(&tlb_inval->pending_fences))
- queue_delayed_work(system_wq, &tlb_inval->fence_tdr,
+ queue_delayed_work(tlb_inval->timeout_wq, &tlb_inval->fence_tdr,
tlb_inval->ops->timeout_delay(tlb_inval));
spin_unlock_irq(&tlb_inval->pending_lock);
@@ -299,6 +314,7 @@ int xe_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval)
* @start: start address
* @end: end address
* @asid: address space id
+ * @prl_sa: suballocation of page reclaim list if used, NULL indicates PPC flush
*
* Issue a range based TLB invalidation if supported, if not fallback to a full
* TLB invalidation. Completion of TLB is asynchronous and caller can use
@@ -308,10 +324,10 @@ int xe_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval)
*/
int xe_tlb_inval_range(struct xe_tlb_inval *tlb_inval,
struct xe_tlb_inval_fence *fence, u64 start, u64 end,
- u32 asid)
+ u32 asid, struct drm_suballoc *prl_sa)
{
return xe_tlb_inval_issue(tlb_inval, fence, tlb_inval->ops->ppgtt,
- start, end, asid);
+ start, end, asid, prl_sa);
}
/**
@@ -327,7 +343,7 @@ void xe_tlb_inval_vm(struct xe_tlb_inval *tlb_inval, struct xe_vm *vm)
u64 range = 1ull << vm->xe->info.va_bits;
xe_tlb_inval_fence_init(tlb_inval, &fence, true);
- xe_tlb_inval_range(tlb_inval, &fence, 0, range, vm->usm.asid);
+ xe_tlb_inval_range(tlb_inval, &fence, 0, range, vm->usm.asid, NULL);
xe_tlb_inval_fence_wait(&fence);
}
@@ -360,6 +376,12 @@ void xe_tlb_inval_done_handler(struct xe_tlb_inval *tlb_inval, int seqno)
* process_g2h_msg().
*/
spin_lock_irqsave(&tlb_inval->pending_lock, flags);
+ if (seqno == TLB_INVALIDATION_SEQNO_INVALID) {
+ xe_tlb_inval_reset_timeout(tlb_inval);
+ spin_unlock_irqrestore(&tlb_inval->pending_lock, flags);
+ return;
+ }
+
if (xe_tlb_inval_seqno_past(tlb_inval, seqno)) {
spin_unlock_irqrestore(&tlb_inval->pending_lock, flags);
return;
@@ -378,7 +400,7 @@ void xe_tlb_inval_done_handler(struct xe_tlb_inval *tlb_inval, int seqno)
}
if (!list_empty(&tlb_inval->pending_fences))
- mod_delayed_work(system_wq,
+ mod_delayed_work(tlb_inval->timeout_wq,
&tlb_inval->fence_tdr,
tlb_inval->ops->timeout_delay(tlb_inval));
else
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval.h b/drivers/gpu/drm/xe/xe_tlb_inval.h
index 05614915463a..858d0690f995 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval.h
+++ b/drivers/gpu/drm/xe/xe_tlb_inval.h
@@ -23,7 +23,7 @@ int xe_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval);
void xe_tlb_inval_vm(struct xe_tlb_inval *tlb_inval, struct xe_vm *vm);
int xe_tlb_inval_range(struct xe_tlb_inval *tlb_inval,
struct xe_tlb_inval_fence *fence,
- u64 start, u64 end, u32 asid);
+ u64 start, u64 end, u32 asid, struct drm_suballoc *prl_sa);
void xe_tlb_inval_fence_init(struct xe_tlb_inval *tlb_inval,
struct xe_tlb_inval_fence *fence,
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_job.c b/drivers/gpu/drm/xe/xe_tlb_inval_job.c
index 1ae0dec2cf31..6a7bd6315797 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval_job.c
+++ b/drivers/gpu/drm/xe/xe_tlb_inval_job.c
@@ -7,7 +7,9 @@
#include "xe_dep_job_types.h"
#include "xe_dep_scheduler.h"
#include "xe_exec_queue.h"
+#include "xe_gt_printk.h"
#include "xe_gt_types.h"
+#include "xe_page_reclaim.h"
#include "xe_tlb_inval.h"
#include "xe_tlb_inval_job.h"
#include "xe_migrate.h"
@@ -24,6 +26,8 @@ struct xe_tlb_inval_job {
struct xe_exec_queue *q;
/** @vm: VM which TLB invalidation is being issued for */
struct xe_vm *vm;
+ /** @prl: Embedded copy of page reclaim list */
+ struct xe_page_reclaim_list prl;
/** @refcount: ref count of this job */
struct kref refcount;
/**
@@ -47,9 +51,16 @@ static struct dma_fence *xe_tlb_inval_job_run(struct xe_dep_job *dep_job)
container_of(dep_job, typeof(*job), dep);
struct xe_tlb_inval_fence *ifence =
container_of(job->fence, typeof(*ifence), base);
+ struct drm_suballoc *prl_sa = NULL;
+
+ if (xe_page_reclaim_list_valid(&job->prl)) {
+ prl_sa = xe_page_reclaim_create_prl_bo(job->tlb_inval, &job->prl, ifence);
+ if (IS_ERR(prl_sa))
+ prl_sa = NULL; /* Indicate fall back PPC flush with NULL */
+ }
xe_tlb_inval_range(job->tlb_inval, ifence, job->start,
- job->end, job->vm->usm.asid);
+ job->end, job->vm->usm.asid, prl_sa);
return job->fence;
}
@@ -107,6 +118,7 @@ xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
job->start = start;
job->end = end;
job->fence_armed = false;
+ xe_page_reclaim_list_init(&job->prl);
job->dep.ops = &dep_job_ops;
job->type = type;
kref_init(&job->refcount);
@@ -140,6 +152,25 @@ err_job:
return ERR_PTR(err);
}
+/**
+ * xe_tlb_inval_job_add_page_reclaim() - Embed PRL into a TLB job
+ * @job: TLB invalidation job that may trigger reclamation
+ * @prl: Page reclaim list populated during unbind
+ *
+ * Copies @prl into the job and takes an extra reference to the entry page so
+ * ownership can transfer to the TLB fence when the job is pushed.
+ */
+void xe_tlb_inval_job_add_page_reclaim(struct xe_tlb_inval_job *job,
+ struct xe_page_reclaim_list *prl)
+{
+ struct xe_device *xe = gt_to_xe(job->q->gt);
+
+ xe_gt_WARN_ON(job->q->gt, !xe->info.has_page_reclaim_hw_assist);
+ job->prl = *prl;
+ /* Pair with put in job_destroy */
+ xe_page_reclaim_entries_get(job->prl.entries);
+}
+
static void xe_tlb_inval_job_destroy(struct kref *ref)
{
struct xe_tlb_inval_job *job = container_of(ref, typeof(*job),
@@ -150,6 +181,9 @@ static void xe_tlb_inval_job_destroy(struct kref *ref)
struct xe_device *xe = gt_to_xe(q->gt);
struct xe_vm *vm = job->vm;
+ /* BO creation retains a copy (if used), so no longer needed */
+ xe_page_reclaim_entries_put(job->prl.entries);
+
if (!job->fence_armed)
kfree(ifence);
else
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_job.h b/drivers/gpu/drm/xe/xe_tlb_inval_job.h
index 4d6df1a6c6ca..03d6e21cd611 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval_job.h
+++ b/drivers/gpu/drm/xe/xe_tlb_inval_job.h
@@ -12,6 +12,7 @@ struct dma_fence;
struct xe_dep_scheduler;
struct xe_exec_queue;
struct xe_migrate;
+struct xe_page_reclaim_list;
struct xe_tlb_inval;
struct xe_tlb_inval_job;
struct xe_vm;
@@ -21,6 +22,9 @@ xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
struct xe_dep_scheduler *dep_scheduler,
struct xe_vm *vm, u64 start, u64 end, int type);
+void xe_tlb_inval_job_add_page_reclaim(struct xe_tlb_inval_job *job,
+ struct xe_page_reclaim_list *prl);
+
int xe_tlb_inval_job_alloc_dep(struct xe_tlb_inval_job *job);
struct dma_fence *xe_tlb_inval_job_push(struct xe_tlb_inval_job *job,
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_types.h b/drivers/gpu/drm/xe/xe_tlb_inval_types.h
index 8f8b060e9005..3b089f90f002 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval_types.h
+++ b/drivers/gpu/drm/xe/xe_tlb_inval_types.h
@@ -9,6 +9,7 @@
#include <linux/workqueue.h>
#include <linux/dma-fence.h>
+struct drm_suballoc;
struct xe_tlb_inval;
/** struct xe_tlb_inval_ops - TLB invalidation ops (backend) */
@@ -40,12 +41,13 @@ struct xe_tlb_inval_ops {
* @start: Start address
* @end: End address
* @asid: Address space ID
+ * @prl_sa: Suballocation for page reclaim list
*
* Return 0 on success, -ECANCELED if backend is mid-reset, error on
* failure
*/
int (*ppgtt)(struct xe_tlb_inval *tlb_inval, u32 seqno, u64 start,
- u64 end, u32 asid);
+ u64 end, u32 asid, struct drm_suballoc *prl_sa);
/**
* @initialized: Backend is initialized
@@ -80,6 +82,7 @@ struct xe_tlb_inval {
const struct xe_tlb_inval_ops *ops;
/** @tlb_inval.seqno: TLB invalidation seqno, protected by CT lock */
#define TLB_INVALIDATION_SEQNO_MAX 0x100000
+#define TLB_INVALIDATION_SEQNO_INVALID TLB_INVALIDATION_SEQNO_MAX
int seqno;
/** @tlb_invalidation.seqno_lock: protects @tlb_invalidation.seqno */
struct mutex seqno_lock;
@@ -106,6 +109,8 @@ struct xe_tlb_inval {
struct workqueue_struct *job_wq;
/** @tlb_inval.lock: protects TLB invalidation fences */
spinlock_t lock;
+ /** @timeout_wq: schedules TLB invalidation fence timeouts */
+ struct workqueue_struct *timeout_wq;
};
/**
diff --git a/drivers/gpu/drm/xe/xe_trace.h b/drivers/gpu/drm/xe/xe_trace.h
index 79a97b086cb2..750fa32c13b2 100644
--- a/drivers/gpu/drm/xe/xe_trace.h
+++ b/drivers/gpu/drm/xe/xe_trace.h
@@ -13,6 +13,7 @@
#include <linux/types.h>
#include "xe_exec_queue_types.h"
+#include "xe_exec_queue.h"
#include "xe_gpu_scheduler_types.h"
#include "xe_gt_types.h"
#include "xe_guc_exec_queue_types.h"
@@ -97,11 +98,51 @@ DECLARE_EVENT_CLASS(xe_exec_queue,
__entry->guc_state, __entry->flags)
);
+DECLARE_EVENT_CLASS(xe_exec_queue_multi_queue,
+ TP_PROTO(struct xe_exec_queue *q),
+ TP_ARGS(q),
+
+ TP_STRUCT__entry(
+ __string(dev, __dev_name_eq(q))
+ __field(enum xe_engine_class, class)
+ __field(u32, logical_mask)
+ __field(u8, gt_id)
+ __field(u16, width)
+ __field(u32, guc_id)
+ __field(u32, guc_state)
+ __field(u32, flags)
+ __field(u32, primary)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev);
+ __entry->class = q->class;
+ __entry->logical_mask = q->logical_mask;
+ __entry->gt_id = q->gt->info.id;
+ __entry->width = q->width;
+ __entry->guc_id = q->guc->id;
+ __entry->guc_state = atomic_read(&q->guc->state);
+ __entry->flags = q->flags;
+ __entry->primary = xe_exec_queue_multi_queue_primary(q)->guc->id;
+ ),
+
+ TP_printk("dev=%s, %d:0x%x, gt=%d, width=%d guc_id=%d, guc_state=0x%x, flags=0x%x, primary=%d",
+ __get_str(dev), __entry->class, __entry->logical_mask,
+ __entry->gt_id, __entry->width, __entry->guc_id,
+ __entry->guc_state, __entry->flags,
+ __entry->primary)
+);
+
DEFINE_EVENT(xe_exec_queue, xe_exec_queue_create,
TP_PROTO(struct xe_exec_queue *q),
TP_ARGS(q)
);
+DEFINE_EVENT(xe_exec_queue_multi_queue, xe_exec_queue_create_multi_queue,
+ TP_PROTO(struct xe_exec_queue *q),
+ TP_ARGS(q)
+);
+
DEFINE_EVENT(xe_exec_queue, xe_exec_queue_supress_resume,
TP_PROTO(struct xe_exec_queue *q),
TP_ARGS(q)
@@ -172,17 +213,17 @@ DEFINE_EVENT(xe_exec_queue, xe_exec_queue_memory_cat_error,
TP_ARGS(q)
);
-DEFINE_EVENT(xe_exec_queue, xe_exec_queue_stop,
+DEFINE_EVENT(xe_exec_queue, xe_exec_queue_cgp_context_error,
TP_PROTO(struct xe_exec_queue *q),
TP_ARGS(q)
);
-DEFINE_EVENT(xe_exec_queue, xe_exec_queue_resubmit,
+DEFINE_EVENT(xe_exec_queue, xe_exec_queue_stop,
TP_PROTO(struct xe_exec_queue *q),
TP_ARGS(q)
);
-DEFINE_EVENT(xe_exec_queue, xe_exec_queue_lr_cleanup,
+DEFINE_EVENT(xe_exec_queue, xe_exec_queue_resubmit,
TP_PROTO(struct xe_exec_queue *q),
TP_ARGS(q)
);
diff --git a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
index 1bddecfb723a..27c9d72222cf 100644
--- a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
@@ -17,7 +17,6 @@
#include "regs/xe_regs.h"
#include "xe_bo.h"
#include "xe_device.h"
-#include "xe_gt.h"
#include "xe_gt_printk.h"
#include "xe_mmio.h"
#include "xe_res_cursor.h"
diff --git a/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c b/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c
index 3e404eb8d098..99fb7e99eb7f 100644
--- a/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c
@@ -13,7 +13,6 @@
#include <drm/ttm/ttm_tt.h>
#include "xe_bo.h"
-#include "xe_gt.h"
struct xe_ttm_sys_node {
struct ttm_buffer_object *tbo;
diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
index 9f70802fce92..6553a19f7cf2 100644
--- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
@@ -12,7 +12,6 @@
#include "xe_bo.h"
#include "xe_device.h"
-#include "xe_gt.h"
#include "xe_res_cursor.h"
#include "xe_ttm_vram_mgr.h"
#include "xe_vram_types.h"
diff --git a/drivers/gpu/drm/xe/xe_uc.c b/drivers/gpu/drm/xe/xe_uc.c
index 465bda355443..3f63c2a7e86d 100644
--- a/drivers/gpu/drm/xe/xe_uc.c
+++ b/drivers/gpu/drm/xe/xe_uc.c
@@ -8,7 +8,6 @@
#include "xe_assert.h"
#include "xe_device.h"
#include "xe_gsc.h"
-#include "xe_gsc_proxy.h"
#include "xe_gt.h"
#include "xe_gt_printk.h"
#include "xe_gt_sriov_vf.h"
@@ -17,7 +16,6 @@
#include "xe_guc_engine_activity.h"
#include "xe_huc.h"
#include "xe_sriov.h"
-#include "xe_uc_fw.h"
#include "xe_wopcm.h"
static struct xe_gt *
@@ -218,9 +216,12 @@ int xe_uc_load_hw(struct xe_uc *uc)
xe_guc_engine_activity_enable_stats(&uc->guc);
- /* We don't fail the driver load if HuC fails to auth, but let's warn */
+ /* We don't fail the driver load if HuC fails to auth */
ret = xe_huc_auth(&uc->huc, XE_HUC_AUTH_VIA_GUC);
- xe_gt_assert(uc_to_gt(uc), !ret);
+ if (ret)
+ xe_gt_err(uc_to_gt(uc),
+ "HuC authentication failed (%pe), continuing with no HuC\n",
+ ERR_PTR(ret));
/* GSC load is async */
xe_gsc_load_start(&uc->gsc);
@@ -302,6 +303,34 @@ int xe_uc_suspend(struct xe_uc *uc)
}
/**
+ * xe_uc_runtime_suspend() - UC runtime suspend
+ * @uc: the UC object
+ *
+ * Runtime suspend all UCs.
+ */
+void xe_uc_runtime_suspend(struct xe_uc *uc)
+{
+ if (!xe_device_uc_enabled(uc_to_xe(uc)))
+ return;
+
+ xe_guc_runtime_suspend(&uc->guc);
+}
+
+/**
+ * xe_uc_runtime_resume() - UC runtime resume
+ * @uc: the UC object
+ *
+ * Runtime resume all UCs.
+ */
+void xe_uc_runtime_resume(struct xe_uc *uc)
+{
+ if (!xe_device_uc_enabled(uc_to_xe(uc)))
+ return;
+
+ xe_guc_runtime_resume(&uc->guc);
+}
+
+/**
* xe_uc_declare_wedged() - Declare UC wedged
* @uc: the UC object
*
diff --git a/drivers/gpu/drm/xe/xe_uc.h b/drivers/gpu/drm/xe/xe_uc.h
index 21c9306098cf..5398da1a8097 100644
--- a/drivers/gpu/drm/xe/xe_uc.h
+++ b/drivers/gpu/drm/xe/xe_uc.h
@@ -14,6 +14,8 @@ int xe_uc_init_post_hwconfig(struct xe_uc *uc);
int xe_uc_load_hw(struct xe_uc *uc);
void xe_uc_gucrc_disable(struct xe_uc *uc);
int xe_uc_reset_prepare(struct xe_uc *uc);
+void xe_uc_runtime_resume(struct xe_uc *uc);
+void xe_uc_runtime_suspend(struct xe_uc *uc);
void xe_uc_stop_prepare(struct xe_uc *uc);
void xe_uc_stop(struct xe_uc *uc);
int xe_uc_start(struct xe_uc *uc);
diff --git a/drivers/gpu/drm/xe/xe_uc_debugfs.c b/drivers/gpu/drm/xe/xe_uc_debugfs.c
index 24a4209051ee..45119993f5cb 100644
--- a/drivers/gpu/drm/xe/xe_uc_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_uc_debugfs.c
@@ -7,12 +7,12 @@
#include <drm/drm_debugfs.h>
-#include "xe_gt.h"
#include "xe_gsc_debugfs.h"
#include "xe_guc_debugfs.h"
#include "xe_huc_debugfs.h"
#include "xe_macros.h"
#include "xe_uc_debugfs.h"
+#include "xe_uc_types.h"
void xe_uc_debugfs_register(struct xe_uc *uc, struct dentry *parent)
{
diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c
index 622b76078567..d35bc4989144 100644
--- a/drivers/gpu/drm/xe/xe_uc_fw.c
+++ b/drivers/gpu/drm/xe/xe_uc_fw.c
@@ -14,9 +14,9 @@
#include "xe_device_types.h"
#include "xe_force_wake.h"
#include "xe_gsc.h"
-#include "xe_gt.h"
#include "xe_gt_printk.h"
#include "xe_gt_sriov_vf.h"
+#include "xe_gt_types.h"
#include "xe_guc.h"
#include "xe_map.h"
#include "xe_mmio.h"
@@ -115,11 +115,12 @@ struct fw_blobs_by_type {
#define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED
#define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \
- fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 49, 4)) \
- fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 49, 4)) \
- fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 45, 2)) \
- fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 44, 1)) \
- fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 45, 2)) \
+ fw_def(NOVALAKE_S, GT_TYPE_ANY, mmp_ver(xe, guc, nvl, 70, 55, 4)) \
+ fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 54, 0)) \
+ fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 54, 0)) \
+ fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 53, 0)) \
+ fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 53, 0)) \
+ fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 53, 0)) \
fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \
fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \
fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 44, 1)) \
@@ -140,6 +141,7 @@ struct fw_blobs_by_type {
/* for the GSC FW we match the compatibility version and not the release one */
#define XE_GSC_FIRMWARE_DEFS(fw_def, major_ver) \
+ fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, gsc, ptl, 105, 1, 0)) \
fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, gsc, lnl, 104, 1, 0)) \
fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, gsc, mtl, 102, 1, 0))
@@ -738,7 +740,7 @@ static int uc_fw_request(struct xe_uc_fw *uc_fw, const struct firmware **firmwar
return 0;
}
- err = request_firmware(&fw, uc_fw->path, dev);
+ err = firmware_request_nowarn(&fw, uc_fw->path, dev);
if (err)
goto fail;
@@ -767,8 +769,12 @@ fail:
XE_UC_FIRMWARE_MISSING :
XE_UC_FIRMWARE_ERROR);
- xe_gt_notice(gt, "%s firmware %s: fetch failed with error %pe\n",
- xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, ERR_PTR(err));
+ if (err == -ENOENT)
+ xe_gt_info(gt, "%s firmware %s not found\n",
+ xe_uc_fw_type_repr(uc_fw->type), uc_fw->path);
+ else
+ xe_gt_notice(gt, "%s firmware %s: fetch failed with error %pe\n",
+ xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, ERR_PTR(err));
xe_gt_info(gt, "%s firmware(s) can be downloaded from %s\n",
xe_uc_fw_type_repr(uc_fw->type), XE_UC_FIRMWARE_URL);
diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c
index 0d9130b1958a..e120323c43bc 100644
--- a/drivers/gpu/drm/xe/xe_userptr.c
+++ b/drivers/gpu/drm/xe/xe_userptr.c
@@ -55,7 +55,7 @@ int xe_vma_userptr_pin_pages(struct xe_userptr_vma *uvma)
struct xe_device *xe = vm->xe;
struct drm_gpusvm_ctx ctx = {
.read_only = xe_vma_read_only(vma),
- .device_private_page_owner = xe_svm_devm_owner(xe),
+ .device_private_page_owner = xe_svm_private_page_owner(vm, false),
.allow_mixed = true,
};
diff --git a/drivers/gpu/drm/xe/xe_validation.c b/drivers/gpu/drm/xe/xe_validation.c
index 826cd09966ef..a611438eaafe 100644
--- a/drivers/gpu/drm/xe/xe_validation.c
+++ b/drivers/gpu/drm/xe/xe_validation.c
@@ -2,7 +2,6 @@
/*
* Copyright © 2024 Intel Corporation
*/
-#include "xe_bo.h"
#include <drm/drm_exec.h>
#include <drm/drm_gem.h>
#include <drm/drm_gpuvm.h>
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 0e7f82eb38fc..dd6d99d7fca8 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -33,7 +33,6 @@
#include "xe_preempt_fence.h"
#include "xe_pt.h"
#include "xe_pxp.h"
-#include "xe_res_cursor.h"
#include "xe_sriov_vf.h"
#include "xe_svm.h"
#include "xe_sync.h"
@@ -957,14 +956,37 @@ free_ops:
return fence;
}
+static void xe_vma_mem_attr_fini(struct xe_vma_mem_attr *attr)
+{
+ drm_pagemap_put(attr->preferred_loc.dpagemap);
+}
+
static void xe_vma_free(struct xe_vma *vma)
{
+ xe_vma_mem_attr_fini(&vma->attr);
+
if (xe_vma_is_userptr(vma))
kfree(to_userptr_vma(vma));
else
kfree(vma);
}
+/**
+ * xe_vma_mem_attr_copy() - copy an xe_vma_mem_attr structure.
+ * @to: Destination.
+ * @from: Source.
+ *
+ * Copies an xe_vma_mem_attr structure taking care to get reference
+ * counting of individual members right.
+ */
+void xe_vma_mem_attr_copy(struct xe_vma_mem_attr *to, struct xe_vma_mem_attr *from)
+{
+ xe_vma_mem_attr_fini(to);
+ *to = *from;
+ if (to->preferred_loc.dpagemap)
+ drm_pagemap_get(to->preferred_loc.dpagemap);
+}
+
static struct xe_vma *xe_vma_create(struct xe_vm *vm,
struct xe_bo *bo,
u64 bo_offset_or_userptr,
@@ -1015,8 +1037,7 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
if (vm->xe->info.has_atomic_enable_pte_bit)
vma->gpuva.flags |= XE_VMA_ATOMIC_PTE_BIT;
- vma->attr = *attr;
-
+ xe_vma_mem_attr_copy(&vma->attr, attr);
if (bo) {
struct drm_gpuvm_bo *vm_bo;
@@ -1508,7 +1529,10 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
INIT_WORK(&vm->destroy_work, vm_destroy_work_func);
INIT_LIST_HEAD(&vm->preempt.exec_queues);
- vm->preempt.min_run_period_ms = 10; /* FIXME: Wire up to uAPI */
+ if (flags & XE_VM_FLAG_FAULT_MODE)
+ vm->preempt.min_run_period_ms = xe->min_run_period_pf_ms;
+ else
+ vm->preempt.min_run_period_ms = xe->min_run_period_lr_ms;
for_each_tile(tile, xe, id)
xe_range_fence_tree_init(&vm->rftree[id]);
@@ -1614,7 +1638,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
if (!vm->pt_root[id])
continue;
- q = xe_exec_queue_create_bind(xe, tile, create_flags, 0);
+ q = xe_exec_queue_create_bind(xe, tile, vm, create_flags, 0);
if (IS_ERR(q)) {
err = PTR_ERR(q);
goto err_close;
@@ -2184,7 +2208,7 @@ static void print_op(struct xe_device *xe, struct drm_gpuva_op *op)
(ULL)xe_vma_start(vma), (ULL)xe_vma_size(vma));
break;
default:
- drm_warn(&xe->drm, "NOT POSSIBLE");
+ drm_warn(&xe->drm, "NOT POSSIBLE\n");
}
}
#else
@@ -2233,6 +2257,7 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops,
struct drm_gpuva_ops *ops;
struct drm_gpuva_op *__op;
struct drm_gpuvm_bo *vm_bo;
+ u64 range_start = addr;
u64 range_end = addr + range;
int err;
@@ -2245,10 +2270,16 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops,
switch (operation) {
case DRM_XE_VM_BIND_OP_MAP:
+ if (flags & DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR) {
+ xe_vm_find_cpu_addr_mirror_vma_range(vm, &range_start, &range_end);
+ vops->flags |= XE_VMA_OPS_FLAG_ALLOW_SVM_UNMAP;
+ }
+
+ fallthrough;
case DRM_XE_VM_BIND_OP_MAP_USERPTR: {
struct drm_gpuvm_map_req map_req = {
- .map.va.addr = addr,
- .map.va.range = range,
+ .map.va.addr = range_start,
+ .map.va.range = range_end - range_start,
.map.gem.obj = obj,
.map.gem.offset = bo_offset_or_userptr,
};
@@ -2280,7 +2311,7 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops,
xe_bo_unlock(bo);
break;
default:
- drm_warn(&vm->xe->drm, "NOT POSSIBLE");
+ drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
ops = ERR_PTR(-EINVAL);
}
if (IS_ERR(ops))
@@ -2310,7 +2341,7 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops,
struct xe_tile *tile;
struct xe_svm_range *svm_range;
struct drm_gpusvm_ctx ctx = {};
- struct drm_pagemap *dpagemap;
+ struct drm_pagemap *dpagemap = NULL;
u8 id, tile_mask = 0;
u32 i;
@@ -2328,23 +2359,17 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops,
xa_init_flags(&op->prefetch_range.range, XA_FLAGS_ALLOC);
op->prefetch_range.ranges_count = 0;
- tile = NULL;
if (prefetch_region == DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC) {
dpagemap = xe_vma_resolve_pagemap(vma,
xe_device_get_root_tile(vm->xe));
- /*
- * TODO: Once multigpu support is enabled will need
- * something to dereference tile from dpagemap.
- */
- if (dpagemap)
- tile = xe_device_get_root_tile(vm->xe);
} else if (prefetch_region) {
tile = &vm->xe->tiles[region_to_mem_type[prefetch_region] -
XE_PL_VRAM0];
+ dpagemap = xe_tile_local_pagemap(tile);
}
- op->prefetch_range.tile = tile;
+ op->prefetch_range.dpagemap = dpagemap;
alloc_next_range:
svm_range = xe_svm_range_find_or_insert(vm, addr, vma, &ctx);
@@ -2363,7 +2388,7 @@ alloc_next_range:
goto unwind_prefetch_ops;
}
- if (xe_svm_range_validate(vm, svm_range, tile_mask, !!tile)) {
+ if (xe_svm_range_validate(vm, svm_range, tile_mask, dpagemap)) {
xe_svm_range_debug(svm_range, "PREFETCH - RANGE IS VALID");
goto check_next_range;
}
@@ -2448,8 +2473,17 @@ static struct xe_vma *new_vma(struct xe_vm *vm, struct drm_gpuva_op_map *op,
if (IS_ERR(vma))
return vma;
- if (xe_vma_is_userptr(vma))
+ if (xe_vma_is_userptr(vma)) {
err = xe_vma_userptr_pin_pages(to_userptr_vma(vma));
+ /*
+ * -EBUSY has dedicated meaning that a user fence
+ * attached to the VMA is busy, in practice
+ * xe_vma_userptr_pin_pages can only fail with -EBUSY if
+ * we are low on memory so convert this to -ENOMEM.
+ */
+ if (err == -EBUSY)
+ err = -ENOMEM;
+ }
}
if (err) {
prep_vma_destroy(vm, vma, false);
@@ -2549,7 +2583,7 @@ static int xe_vma_op_commit(struct xe_vm *vm, struct xe_vma_op *op)
op->flags |= XE_VMA_OP_COMMITTED;
break;
default:
- drm_warn(&vm->xe->drm, "NOT POSSIBLE");
+ drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
}
return err;
@@ -2724,7 +2758,8 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
if (xe_vma_is_cpu_addr_mirror(vma) &&
xe_svm_has_mapping(vm, xe_vma_start(vma),
- xe_vma_end(vma)))
+ xe_vma_end(vma)) &&
+ !(vops->flags & XE_VMA_OPS_FLAG_ALLOW_SVM_UNMAP))
return -EBUSY;
if (!xe_vma_is_cpu_addr_mirror(vma))
@@ -2747,7 +2782,7 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
break;
default:
- drm_warn(&vm->xe->drm, "NOT POSSIBLE");
+ drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
}
err = xe_vma_op_commit(vm, op);
@@ -2809,7 +2844,7 @@ static void xe_vma_op_unwind(struct xe_vm *vm, struct xe_vma_op *op,
/* Nothing to do */
break;
default:
- drm_warn(&vm->xe->drm, "NOT POSSIBLE");
+ drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
}
}
@@ -2875,7 +2910,7 @@ static int prefetch_ranges(struct xe_vm *vm, struct xe_vma_op *op)
{
bool devmem_possible = IS_DGFX(vm->xe) && IS_ENABLED(CONFIG_DRM_XE_PAGEMAP);
struct xe_vma *vma = gpuva_to_vma(op->base.prefetch.va);
- struct xe_tile *tile = op->prefetch_range.tile;
+ struct drm_pagemap *dpagemap = op->prefetch_range.dpagemap;
int err = 0;
struct xe_svm_range *svm_range;
@@ -2888,15 +2923,22 @@ static int prefetch_ranges(struct xe_vm *vm, struct xe_vma_op *op)
ctx.read_only = xe_vma_read_only(vma);
ctx.devmem_possible = devmem_possible;
ctx.check_pages_threshold = devmem_possible ? SZ_64K : 0;
- ctx.device_private_page_owner = xe_svm_devm_owner(vm->xe);
+ ctx.device_private_page_owner = xe_svm_private_page_owner(vm, !dpagemap);
/* TODO: Threading the migration */
xa_for_each(&op->prefetch_range.range, i, svm_range) {
- if (!tile)
+ if (!dpagemap)
xe_svm_range_migrate_to_smem(vm, svm_range);
- if (xe_svm_range_needs_migrate_to_vram(svm_range, vma, !!tile)) {
- err = xe_svm_alloc_vram(tile, svm_range, &ctx);
+ if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)) {
+ drm_dbg(&vm->xe->drm,
+ "Prefetch pagemap is %s start 0x%016lx end 0x%016lx\n",
+ dpagemap ? dpagemap->drm->unique : "system",
+ xe_svm_range_start(svm_range), xe_svm_range_end(svm_range));
+ }
+
+ if (xe_svm_range_needs_migrate_to_vram(svm_range, vma, dpagemap)) {
+ err = xe_svm_alloc_vram(svm_range, &ctx, dpagemap);
if (err) {
drm_dbg(&vm->xe->drm, "VRAM allocation failed, retry from userspace, asid=%u, gpusvm=%p, errno=%pe\n",
vm->usm.asid, &vm->svm.gpusvm, ERR_PTR(err));
@@ -2986,7 +3028,7 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm,
break;
}
default:
- drm_warn(&vm->xe->drm, "NOT POSSIBLE");
+ drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
}
return err;
@@ -3104,19 +3146,19 @@ static struct dma_fence *ops_execute(struct xe_vm *vm,
struct dma_fence *fence = NULL;
struct dma_fence **fences = NULL;
struct dma_fence_array *cf = NULL;
- int number_tiles = 0, current_fence = 0, n_fence = 0, err;
+ int number_tiles = 0, current_fence = 0, n_fence = 0, err, i;
u8 id;
number_tiles = vm_ops_setup_tile_args(vm, vops);
if (number_tiles == 0)
return ERR_PTR(-ENODATA);
- if (vops->flags & XE_VMA_OPS_FLAG_SKIP_TLB_WAIT) {
- for_each_tile(tile, vm->xe, id)
- ++n_fence;
- } else {
- for_each_tile(tile, vm->xe, id)
- n_fence += (1 + XE_MAX_GT_PER_TILE);
+ for_each_tile(tile, vm->xe, id) {
+ ++n_fence;
+
+ if (!(vops->flags & XE_VMA_OPS_FLAG_SKIP_TLB_WAIT))
+ for_each_tlb_inval(i)
+ ++n_fence;
}
fences = kmalloc_array(n_fence, sizeof(*fences), GFP_KERNEL);
@@ -3146,7 +3188,6 @@ static struct dma_fence *ops_execute(struct xe_vm *vm,
for_each_tile(tile, vm->xe, id) {
struct xe_exec_queue *q = vops->pt_update_ops[tile->id].q;
- int i;
fence = NULL;
if (!vops->pt_update_ops[id].num_ops)
@@ -3211,7 +3252,8 @@ static void op_add_ufence(struct xe_vm *vm, struct xe_vma_op *op,
{
switch (op->base.op) {
case DRM_GPUVA_OP_MAP:
- vma_add_ufence(op->map.vma, ufence);
+ if (!xe_vma_is_cpu_addr_mirror(op->map.vma))
+ vma_add_ufence(op->map.vma, ufence);
break;
case DRM_GPUVA_OP_REMAP:
if (op->remap.prev)
@@ -3225,7 +3267,7 @@ static void op_add_ufence(struct xe_vm *vm, struct xe_vma_op *op,
vma_add_ufence(gpuva_to_vma(op->base.prefetch.va), ufence);
break;
default:
- drm_warn(&vm->xe->drm, "NOT POSSIBLE");
+ drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
}
}
@@ -3324,6 +3366,9 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm,
if (XE_IOCTL_DBG(xe, args->extensions))
return -EINVAL;
+ if (XE_IOCTL_DBG(xe, args->num_syncs > DRM_XE_MAX_SYNCS))
+ return -EINVAL;
+
if (args->num_binds > 1) {
u64 __user *bind_user =
u64_to_user_ptr(args->vector_of_binds);
@@ -3359,6 +3404,7 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm,
DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR;
u16 pat_index = (*bind_ops)[i].pat_index;
u16 coh_mode;
+ bool comp_en;
if (XE_IOCTL_DBG(xe, is_cpu_addr_mirror &&
(!xe_vm_in_fault_mode(vm) ||
@@ -3375,6 +3421,7 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm,
pat_index = array_index_nospec(pat_index, xe->pat.n_entries);
(*bind_ops)[i].pat_index = pat_index;
coh_mode = xe_pat_index_get_coh_mode(xe, pat_index);
+ comp_en = xe_pat_index_get_comp_en(xe, pat_index);
if (XE_IOCTL_DBG(xe, !coh_mode)) { /* hw reserved */
err = -EINVAL;
goto free_bind_ops;
@@ -3405,6 +3452,8 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm,
op == DRM_XE_VM_BIND_OP_MAP_USERPTR) ||
XE_IOCTL_DBG(xe, coh_mode == XE_COH_NONE &&
op == DRM_XE_VM_BIND_OP_MAP_USERPTR) ||
+ XE_IOCTL_DBG(xe, comp_en &&
+ op == DRM_XE_VM_BIND_OP_MAP_USERPTR) ||
XE_IOCTL_DBG(xe, op == DRM_XE_VM_BIND_OP_MAP_USERPTR &&
!IS_ENABLED(CONFIG_DRM_GPUSVM)) ||
XE_IOCTL_DBG(xe, obj &&
@@ -3483,6 +3532,11 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo,
u16 pat_index, u32 op, u32 bind_flags)
{
u16 coh_mode;
+ bool comp_en;
+
+ if (XE_IOCTL_DBG(xe, (bo->flags & XE_BO_FLAG_NO_COMPRESSION) &&
+ xe_pat_index_get_comp_en(xe, pat_index)))
+ return -EINVAL;
if (XE_IOCTL_DBG(xe, range > xe_bo_size(bo)) ||
XE_IOCTL_DBG(xe, obj_offset >
@@ -3524,6 +3578,14 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo,
return -EINVAL;
}
+ /*
+ * Ensures that imported buffer objects (dma-bufs) are not mapped
+ * with a PAT index that enables compression.
+ */
+ comp_en = xe_pat_index_get_comp_en(xe, pat_index);
+ if (XE_IOCTL_DBG(xe, bo->ttm.base.import_attach && comp_en))
+ return -EINVAL;
+
/* If a BO is protected it can only be mapped if the key is still valid */
if ((bind_flags & DRM_XE_VM_BIND_FLAG_CHECK_PXP) && xe_bo_is_protected(bo) &&
op != DRM_XE_VM_BIND_OP_UNMAP && op != DRM_XE_VM_BIND_OP_UNMAP_ALL)
@@ -3572,6 +3634,11 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
}
}
+ if (XE_IOCTL_DBG(xe, q && vm != q->user_vm)) {
+ err = -EINVAL;
+ goto put_exec_queue;
+ }
+
/* Ensure all UNMAPs visible */
xe_svm_flush(vm);
@@ -3907,7 +3974,7 @@ int xe_vm_range_tilemask_tlb_inval(struct xe_vm *vm, u64 start,
err = xe_tlb_inval_range(&tile->primary_gt->tlb_inval,
&fence[fence_id], start, end,
- vm->usm.asid);
+ vm->usm.asid, NULL);
if (err)
goto wait;
++fence_id;
@@ -3920,7 +3987,7 @@ int xe_vm_range_tilemask_tlb_inval(struct xe_vm *vm, u64 start,
err = xe_tlb_inval_range(&tile->media_gt->tlb_inval,
&fence[fence_id], start, end,
- vm->usm.asid);
+ vm->usm.asid, NULL);
if (err)
goto wait;
++fence_id;
@@ -4026,10 +4093,18 @@ int xe_vm_validate_protected(struct xe_vm *vm)
}
struct xe_vm_snapshot {
+ int uapi_flags;
unsigned long num_snaps;
struct {
u64 ofs, bo_ofs;
unsigned long len;
+#define XE_VM_SNAP_FLAG_USERPTR BIT(0)
+#define XE_VM_SNAP_FLAG_READ_ONLY BIT(1)
+#define XE_VM_SNAP_FLAG_IS_NULL BIT(2)
+ unsigned long flags;
+ int uapi_mem_region;
+ int pat_index;
+ int cpu_caching;
struct xe_bo *bo;
void *data;
struct mm_struct *mm;
@@ -4058,6 +4133,13 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm)
goto out_unlock;
}
+ if (vm->flags & XE_VM_FLAG_FAULT_MODE)
+ snap->uapi_flags |= DRM_XE_VM_CREATE_FLAG_FAULT_MODE;
+ if (vm->flags & XE_VM_FLAG_LR_MODE)
+ snap->uapi_flags |= DRM_XE_VM_CREATE_FLAG_LR_MODE;
+ if (vm->flags & XE_VM_FLAG_SCRATCH_PAGE)
+ snap->uapi_flags |= DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE;
+
snap->num_snaps = num_snaps;
i = 0;
drm_gpuvm_for_each_va(gpuva, &vm->gpuvm) {
@@ -4070,9 +4152,25 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm)
snap->snap[i].ofs = xe_vma_start(vma);
snap->snap[i].len = xe_vma_size(vma);
+ snap->snap[i].flags = xe_vma_read_only(vma) ?
+ XE_VM_SNAP_FLAG_READ_ONLY : 0;
+ snap->snap[i].pat_index = vma->attr.pat_index;
if (bo) {
+ snap->snap[i].cpu_caching = bo->cpu_caching;
snap->snap[i].bo = xe_bo_get(bo);
snap->snap[i].bo_ofs = xe_vma_bo_offset(vma);
+ switch (bo->ttm.resource->mem_type) {
+ case XE_PL_SYSTEM:
+ case XE_PL_TT:
+ snap->snap[i].uapi_mem_region = 0;
+ break;
+ case XE_PL_VRAM0:
+ snap->snap[i].uapi_mem_region = 1;
+ break;
+ case XE_PL_VRAM1:
+ snap->snap[i].uapi_mem_region = 2;
+ break;
+ }
} else if (xe_vma_is_userptr(vma)) {
struct mm_struct *mm =
to_userptr_vma(vma)->userptr.notifier.mm;
@@ -4083,8 +4181,14 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm)
snap->snap[i].data = ERR_PTR(-EFAULT);
snap->snap[i].bo_ofs = xe_vma_userptr(vma);
+ snap->snap[i].flags |= XE_VM_SNAP_FLAG_USERPTR;
+ snap->snap[i].uapi_mem_region = 0;
+ } else if (xe_vma_is_null(vma)) {
+ snap->snap[i].flags |= XE_VM_SNAP_FLAG_IS_NULL;
+ snap->snap[i].uapi_mem_region = -1;
} else {
snap->snap[i].data = ERR_PTR(-ENOENT);
+ snap->snap[i].uapi_mem_region = -1;
}
i++;
}
@@ -4103,7 +4207,8 @@ void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap)
struct xe_bo *bo = snap->snap[i].bo;
int err;
- if (IS_ERR(snap->snap[i].data))
+ if (IS_ERR(snap->snap[i].data) ||
+ snap->snap[i].flags & XE_VM_SNAP_FLAG_IS_NULL)
continue;
snap->snap[i].data = kvmalloc(snap->snap[i].len, GFP_USER);
@@ -4149,15 +4254,32 @@ void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p)
return;
}
+ drm_printf(p, "VM.uapi_flags: 0x%x\n", snap->uapi_flags);
for (i = 0; i < snap->num_snaps; i++) {
drm_printf(p, "[%llx].length: 0x%lx\n", snap->snap[i].ofs, snap->snap[i].len);
+ drm_printf(p, "[%llx].properties: %s|%s|mem_region=0x%lx|pat_index=%d|cpu_caching=%d\n",
+ snap->snap[i].ofs,
+ snap->snap[i].flags & XE_VM_SNAP_FLAG_READ_ONLY ?
+ "read_only" : "read_write",
+ snap->snap[i].flags & XE_VM_SNAP_FLAG_IS_NULL ?
+ "null_sparse" :
+ snap->snap[i].flags & XE_VM_SNAP_FLAG_USERPTR ?
+ "userptr" : "bo",
+ snap->snap[i].uapi_mem_region == -1 ? 0 :
+ BIT(snap->snap[i].uapi_mem_region),
+ snap->snap[i].pat_index,
+ snap->snap[i].cpu_caching);
+
if (IS_ERR(snap->snap[i].data)) {
drm_printf(p, "[%llx].error: %li\n", snap->snap[i].ofs,
PTR_ERR(snap->snap[i].data));
continue;
}
+ if (snap->snap[i].flags & XE_VM_SNAP_FLAG_IS_NULL)
+ continue;
+
drm_printf(p, "[%llx].data: ", snap->snap[i].ofs);
for (j = 0; j < snap->snap[i].len; j += sizeof(u32)) {
@@ -4242,7 +4364,7 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
struct drm_gpuva_op *__op;
unsigned int vma_flags = 0;
bool remap_op = false;
- struct xe_vma_mem_attr tmp_attr;
+ struct xe_vma_mem_attr tmp_attr = {};
u16 default_pat;
int err;
@@ -4311,6 +4433,8 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
if (is_madvise)
vops.flags |= XE_VMA_OPS_FLAG_MADVISE;
+ else
+ vops.flags |= XE_VMA_OPS_FLAG_ALLOW_SVM_UNMAP;
err = vm_bind_ioctl_ops_parse(vm, ops, &vops);
if (err)
@@ -4335,7 +4459,7 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
* VMA, so they can be assigned to newly MAP created vma.
*/
if (is_madvise)
- tmp_attr = vma->attr;
+ xe_vma_mem_attr_copy(&tmp_attr, &vma->attr);
xe_vma_destroy(gpuva_to_vma(op->base.remap.unmap->va), NULL);
} else if (__op->op == DRM_GPUVA_OP_MAP) {
@@ -4345,12 +4469,13 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
* copy them to new vma.
*/
if (is_madvise)
- vma->attr = tmp_attr;
+ xe_vma_mem_attr_copy(&vma->attr, &tmp_attr);
}
}
xe_vm_unlock(vm);
drm_gpuva_ops_free(&vm->gpuvm, ops);
+ xe_vma_mem_attr_fini(&tmp_attr);
return 0;
unwind_ops:
@@ -4384,6 +4509,46 @@ int xe_vm_alloc_madvise_vma(struct xe_vm *vm, uint64_t start, uint64_t range)
return xe_vm_alloc_vma(vm, &map_req, true);
}
+static bool is_cpu_addr_vma_with_default_attr(struct xe_vma *vma)
+{
+ return vma && xe_vma_is_cpu_addr_mirror(vma) &&
+ xe_vma_has_default_mem_attrs(vma);
+}
+
+/**
+ * xe_vm_find_cpu_addr_mirror_vma_range - Extend a VMA range to include adjacent CPU-mirrored VMAs
+ * @vm: VM to search within
+ * @start: Input/output pointer to the starting address of the range
+ * @end: Input/output pointer to the end address of the range
+ *
+ * Given a range defined by @start and @range, this function checks the VMAs
+ * immediately before and after the range. If those neighboring VMAs are
+ * CPU-address-mirrored and have default memory attributes, the function
+ * updates @start and @range to include them. This extended range can then
+ * be used for merging or other operations that require a unified VMA.
+ *
+ * The function does not perform the merge itself; it only computes the
+ * mergeable boundaries.
+ */
+void xe_vm_find_cpu_addr_mirror_vma_range(struct xe_vm *vm, u64 *start, u64 *end)
+{
+ struct xe_vma *prev, *next;
+
+ lockdep_assert_held(&vm->lock);
+
+ if (*start >= SZ_4K) {
+ prev = xe_vm_find_vma_by_addr(vm, *start - SZ_4K);
+ if (is_cpu_addr_vma_with_default_attr(prev))
+ *start = xe_vma_start(prev);
+ }
+
+ if (*end < vm->size) {
+ next = xe_vm_find_vma_by_addr(vm, *end + 1);
+ if (is_cpu_addr_vma_with_default_attr(next))
+ *end = xe_vma_end(next);
+ }
+}
+
/**
* xe_vm_alloc_cpu_addr_mirror_vma - Allocate CPU addr mirror vma
* @vm: Pointer to the xe_vm structure
@@ -4408,3 +4573,4 @@ int xe_vm_alloc_cpu_addr_mirror_vma(struct xe_vm *vm, uint64_t start, uint64_t r
return xe_vm_alloc_vma(vm, &map_req, false);
}
+
diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
index ef8a5019574e..6cc98df47291 100644
--- a/drivers/gpu/drm/xe/xe_vm.h
+++ b/drivers/gpu/drm/xe/xe_vm.h
@@ -68,6 +68,9 @@ xe_vm_find_overlapping_vma(struct xe_vm *vm, u64 start, u64 range);
bool xe_vma_has_default_mem_attrs(struct xe_vma *vma);
+void xe_vm_find_cpu_addr_mirror_vma_range(struct xe_vm *vm,
+ u64 *start,
+ u64 *end);
/**
* xe_vm_has_scratch() - Whether the vm is configured for scratch PTEs
* @vm: The vm
@@ -379,7 +382,7 @@ static inline void xe_vm_set_validation_exec(struct xe_vm *vm, struct drm_exec *
}
/**
- * xe_vm_set_validation_exec() - Accessor to read the drm_exec object
+ * xe_vm_validation_exec() - Accessor to read the drm_exec object
* @vm: The vm we want to register a drm_exec object with.
*
* Return: The drm_exec object used to lock the vm's resv. The value
@@ -411,4 +414,5 @@ static inline struct drm_exec *xe_vm_validation_exec(struct xe_vm *vm)
#define xe_vm_has_valid_gpu_mapping(tile, tile_present, tile_invalidated) \
((READ_ONCE(tile_present) & ~READ_ONCE(tile_invalidated)) & BIT((tile)->id))
+void xe_vma_mem_attr_copy(struct xe_vma_mem_attr *to, struct xe_vma_mem_attr *from);
#endif
diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c
index cad3cf627c3f..add9a6ca2390 100644
--- a/drivers/gpu/drm/xe/xe_vm_madvise.c
+++ b/drivers/gpu/drm/xe/xe_vm_madvise.c
@@ -22,6 +22,19 @@ struct xe_vmas_in_madvise_range {
bool has_svm_userptr_vmas;
};
+/**
+ * struct xe_madvise_details - Argument to madvise_funcs
+ * @dpagemap: Reference-counted pointer to a struct drm_pagemap.
+ *
+ * The madvise IOCTL handler may, in addition to the user-space
+ * args, have additional info to pass into the madvise_func that
+ * handles the madvise type. Use a struct_xe_madvise_details
+ * for that and extend the struct as necessary.
+ */
+struct xe_madvise_details {
+ struct drm_pagemap *dpagemap;
+};
+
static int get_vmas(struct xe_vm *vm, struct xe_vmas_in_madvise_range *madvise_range)
{
u64 addr = madvise_range->addr;
@@ -74,34 +87,41 @@ static int get_vmas(struct xe_vm *vm, struct xe_vmas_in_madvise_range *madvise_r
static void madvise_preferred_mem_loc(struct xe_device *xe, struct xe_vm *vm,
struct xe_vma **vmas, int num_vmas,
- struct drm_xe_madvise *op)
+ struct drm_xe_madvise *op,
+ struct xe_madvise_details *details)
{
int i;
xe_assert(vm->xe, op->type == DRM_XE_MEM_RANGE_ATTR_PREFERRED_LOC);
for (i = 0; i < num_vmas; i++) {
+ struct xe_vma *vma = vmas[i];
+ struct xe_vma_preferred_loc *loc = &vma->attr.preferred_loc;
+
/*TODO: Extend attributes to bo based vmas */
- if ((vmas[i]->attr.preferred_loc.devmem_fd == op->preferred_mem_loc.devmem_fd &&
- vmas[i]->attr.preferred_loc.migration_policy ==
- op->preferred_mem_loc.migration_policy) ||
- !xe_vma_is_cpu_addr_mirror(vmas[i])) {
- vmas[i]->skip_invalidation = true;
+ if ((loc->devmem_fd == op->preferred_mem_loc.devmem_fd &&
+ loc->migration_policy == op->preferred_mem_loc.migration_policy) ||
+ !xe_vma_is_cpu_addr_mirror(vma)) {
+ vma->skip_invalidation = true;
} else {
- vmas[i]->skip_invalidation = false;
- vmas[i]->attr.preferred_loc.devmem_fd = op->preferred_mem_loc.devmem_fd;
+ vma->skip_invalidation = false;
+ loc->devmem_fd = op->preferred_mem_loc.devmem_fd;
/* Till multi-device support is not added migration_policy
* is of no use and can be ignored.
*/
- vmas[i]->attr.preferred_loc.migration_policy =
- op->preferred_mem_loc.migration_policy;
+ loc->migration_policy = op->preferred_mem_loc.migration_policy;
+ drm_pagemap_put(loc->dpagemap);
+ loc->dpagemap = NULL;
+ if (details->dpagemap)
+ loc->dpagemap = drm_pagemap_get(details->dpagemap);
}
}
}
static void madvise_atomic(struct xe_device *xe, struct xe_vm *vm,
struct xe_vma **vmas, int num_vmas,
- struct drm_xe_madvise *op)
+ struct drm_xe_madvise *op,
+ struct xe_madvise_details *details)
{
struct xe_bo *bo;
int i;
@@ -142,7 +162,8 @@ static void madvise_atomic(struct xe_device *xe, struct xe_vm *vm,
static void madvise_pat_index(struct xe_device *xe, struct xe_vm *vm,
struct xe_vma **vmas, int num_vmas,
- struct drm_xe_madvise *op)
+ struct drm_xe_madvise *op,
+ struct xe_madvise_details *details)
{
int i;
@@ -160,7 +181,8 @@ static void madvise_pat_index(struct xe_device *xe, struct xe_vm *vm,
typedef void (*madvise_func)(struct xe_device *xe, struct xe_vm *vm,
struct xe_vma **vmas, int num_vmas,
- struct drm_xe_madvise *op);
+ struct drm_xe_madvise *op,
+ struct xe_madvise_details *details);
static const madvise_func madvise_funcs[] = {
[DRM_XE_MEM_RANGE_ATTR_PREFERRED_LOC] = madvise_preferred_mem_loc,
@@ -244,11 +266,12 @@ static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madv
if (XE_IOCTL_DBG(xe, fd < DRM_XE_PREFERRED_LOC_DEFAULT_SYSTEM))
return false;
- if (XE_IOCTL_DBG(xe, args->preferred_mem_loc.migration_policy >
- DRM_XE_MIGRATE_ONLY_SYSTEM_PAGES))
+ if (XE_IOCTL_DBG(xe, fd <= DRM_XE_PREFERRED_LOC_DEFAULT_DEVICE &&
+ args->preferred_mem_loc.region_instance != 0))
return false;
- if (XE_IOCTL_DBG(xe, args->preferred_mem_loc.pad))
+ if (XE_IOCTL_DBG(xe, args->preferred_mem_loc.migration_policy >
+ DRM_XE_MIGRATE_ONLY_SYSTEM_PAGES))
return false;
if (XE_IOCTL_DBG(xe, args->preferred_mem_loc.reserved))
@@ -294,6 +317,41 @@ static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madv
return true;
}
+static int xe_madvise_details_init(struct xe_vm *vm, const struct drm_xe_madvise *args,
+ struct xe_madvise_details *details)
+{
+ struct xe_device *xe = vm->xe;
+
+ memset(details, 0, sizeof(*details));
+
+ if (args->type == DRM_XE_MEM_RANGE_ATTR_PREFERRED_LOC) {
+ int fd = args->preferred_mem_loc.devmem_fd;
+ struct drm_pagemap *dpagemap;
+
+ if (fd <= 0)
+ return 0;
+
+ dpagemap = xe_drm_pagemap_from_fd(args->preferred_mem_loc.devmem_fd,
+ args->preferred_mem_loc.region_instance);
+ if (XE_IOCTL_DBG(xe, IS_ERR(dpagemap)))
+ return PTR_ERR(dpagemap);
+
+ /* Don't allow a foreign placement without a fast interconnect! */
+ if (XE_IOCTL_DBG(xe, dpagemap->pagemap->owner != vm->svm.peer.owner)) {
+ drm_pagemap_put(dpagemap);
+ return -ENOLINK;
+ }
+ details->dpagemap = dpagemap;
+ }
+
+ return 0;
+}
+
+static void xe_madvise_details_fini(struct xe_madvise_details *details)
+{
+ drm_pagemap_put(details->dpagemap);
+}
+
static bool check_bo_args_are_sane(struct xe_vm *vm, struct xe_vma **vmas,
int num_vmas, u32 atomic_val)
{
@@ -347,6 +405,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
struct drm_xe_madvise *args = data;
struct xe_vmas_in_madvise_range madvise_range = {.addr = args->start,
.range = args->range, };
+ struct xe_madvise_details details;
struct xe_vm *vm;
struct drm_exec exec;
int err, attr_type;
@@ -371,13 +430,17 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
goto unlock_vm;
}
- err = xe_vm_alloc_madvise_vma(vm, args->start, args->range);
+ err = xe_madvise_details_init(vm, args, &details);
if (err)
goto unlock_vm;
+ err = xe_vm_alloc_madvise_vma(vm, args->start, args->range);
+ if (err)
+ goto madv_fini;
+
err = get_vmas(vm, &madvise_range);
if (err || !madvise_range.num_vmas)
- goto unlock_vm;
+ goto madv_fini;
if (madvise_range.has_bo_vmas) {
if (args->type == DRM_XE_MEM_RANGE_ATTR_ATOMIC) {
@@ -385,7 +448,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
madvise_range.num_vmas,
args->atomic.val)) {
err = -EINVAL;
- goto unlock_vm;
+ goto madv_fini;
}
}
@@ -411,7 +474,8 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
}
attr_type = array_index_nospec(args->type, ARRAY_SIZE(madvise_funcs));
- madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args);
+ madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args,
+ &details);
err = xe_vm_invalidate_madvise_range(vm, args->start, args->start + args->range);
@@ -423,6 +487,8 @@ err_fini:
drm_exec_fini(&exec);
kfree(madvise_range.vmas);
madvise_range.vmas = NULL;
+madv_fini:
+ xe_madvise_details_fini(&details);
unlock_vm:
up_write(&vm->lock);
put_vm:
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index ccd6cc090309..437f64202f3b 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -8,6 +8,7 @@
#include <drm/drm_gpusvm.h>
#include <drm/drm_gpuvm.h>
+#include <drm/drm_pagemap_util.h>
#include <linux/dma-resv.h>
#include <linux/kref.h>
@@ -19,6 +20,8 @@
#include "xe_range_fence.h"
#include "xe_userptr.h"
+struct drm_pagemap;
+
struct xe_bo;
struct xe_svm_range;
struct xe_sync_entry;
@@ -53,7 +56,7 @@ struct xe_vm_pgtable_update_op;
*/
struct xe_vma_mem_attr {
/** @preferred_loc: preferred memory_location */
- struct {
+ struct xe_vma_preferred_loc {
/** @preferred_loc.migration_policy: Pages migration policy */
u32 migration_policy;
@@ -64,6 +67,13 @@ struct xe_vma_mem_attr {
* closest device memory respectively.
*/
u32 devmem_fd;
+ /**
+ * @preferred_loc.dpagemap: Reference-counted pointer to the drm_pagemap preferred
+ * for migration on a SVM page-fault. The pointer is protected by the
+ * vm lock, and is %NULL if @devmem_fd should be consulted for special
+ * values.
+ */
+ struct drm_pagemap *dpagemap;
} preferred_loc;
/**
@@ -191,6 +201,9 @@ struct xe_vm {
*/
struct work_struct work;
} garbage_collector;
+ struct xe_pagemap *pagemaps[XE_MAX_TILES_PER_DEVICE];
+ /** @svm.peer: Used for pagemap connectivity computations. */
+ struct drm_pagemap_peer peer;
} svm;
struct xe_device *xe;
@@ -263,7 +276,7 @@ struct xe_vm {
* @min_run_period_ms: The minimum run period before preempting
* an engine again
*/
- s64 min_run_period_ms;
+ unsigned int min_run_period_ms;
/** @exec_queues: list of exec queues attached to this VM */
struct list_head exec_queues;
/** @num_exec_queues: number exec queues attached to this VM */
@@ -395,10 +408,10 @@ struct xe_vma_op_prefetch_range {
/** @ranges_count: number of svm ranges to map */
u32 ranges_count;
/**
- * @tile: Pointer to the tile structure containing memory to prefetch.
- * NULL if prefetch requested region is smem
+ * @dpagemap: Pointer to the dpagemap structure containing memory to prefetch.
+ * NULL if prefetch requested region is smem
*/
- struct xe_tile *tile;
+ struct drm_pagemap *dpagemap;
};
/** enum xe_vma_op_flags - flags for VMA operation */
@@ -467,6 +480,7 @@ struct xe_vma_ops {
#define XE_VMA_OPS_FLAG_MADVISE BIT(1)
#define XE_VMA_OPS_ARRAY_OF_BINDS BIT(2)
#define XE_VMA_OPS_FLAG_SKIP_TLB_WAIT BIT(3)
+#define XE_VMA_OPS_FLAG_ALLOW_SVM_UNMAP BIT(4)
u32 flags;
#ifdef TEST_VM_OPS_ERROR
/** @inject_error: inject error to test error handling */
diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c
index d50baefcd124..0538dcb8b18c 100644
--- a/drivers/gpu/drm/xe/xe_vram.c
+++ b/drivers/gpu/drm/xe/xe_vram.c
@@ -13,109 +13,16 @@
#include "regs/xe_gt_regs.h"
#include "regs/xe_regs.h"
#include "xe_assert.h"
-#include "xe_bo.h"
#include "xe_device.h"
#include "xe_force_wake.h"
#include "xe_gt_mcr.h"
#include "xe_mmio.h"
-#include "xe_module.h"
#include "xe_sriov.h"
#include "xe_tile_sriov_vf.h"
#include "xe_ttm_vram_mgr.h"
#include "xe_vram.h"
#include "xe_vram_types.h"
-static void resize_bar(struct xe_device *xe, int resno, resource_size_t size)
-{
- struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
- int bar_size = pci_rebar_bytes_to_size(size);
- int ret;
-
- ret = pci_resize_resource(pdev, resno, bar_size, 0);
- if (ret) {
- drm_info(&xe->drm, "Failed to resize BAR%d to %dM (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n",
- resno, 1 << bar_size, ERR_PTR(ret));
- return;
- }
-
- drm_info(&xe->drm, "BAR%d resized to %dM\n", resno, 1 << bar_size);
-}
-
-/*
- * if force_vram_bar_size is set, attempt to set to the requested size
- * else set to maximum possible size
- */
-void xe_vram_resize_bar(struct xe_device *xe)
-{
- int force_vram_bar_size = xe_modparam.force_vram_bar_size;
- struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
- struct pci_bus *root = pdev->bus;
- resource_size_t current_size;
- resource_size_t rebar_size;
- struct resource *root_res;
- int max_size, i;
- u32 pci_cmd;
-
- /* gather some relevant info */
- current_size = pci_resource_len(pdev, LMEM_BAR);
-
- if (force_vram_bar_size < 0)
- return;
-
- /* set to a specific size? */
- if (force_vram_bar_size) {
- rebar_size = pci_rebar_bytes_to_size(force_vram_bar_size *
- (resource_size_t)SZ_1M);
-
- if (!pci_rebar_size_supported(pdev, LMEM_BAR, rebar_size)) {
- drm_info(&xe->drm,
- "Requested size: %lluMiB is not supported by rebar sizes: 0x%llx. Leaving default: %lluMiB\n",
- (u64)pci_rebar_size_to_bytes(rebar_size) >> 20,
- pci_rebar_get_possible_sizes(pdev, LMEM_BAR),
- (u64)current_size >> 20);
- return;
- }
-
- rebar_size = pci_rebar_size_to_bytes(rebar_size);
- if (rebar_size == current_size)
- return;
- } else {
- max_size = pci_rebar_get_max_size(pdev, LMEM_BAR);
- if (max_size < 0)
- return;
- rebar_size = pci_rebar_size_to_bytes(max_size);
-
- /* only resize if larger than current */
- if (rebar_size <= current_size)
- return;
- }
-
- drm_info(&xe->drm, "Attempting to resize bar from %lluMiB -> %lluMiB\n",
- (u64)current_size >> 20, (u64)rebar_size >> 20);
-
- while (root->parent)
- root = root->parent;
-
- pci_bus_for_each_resource(root, root_res, i) {
- if (root_res && root_res->flags & (IORESOURCE_MEM | IORESOURCE_MEM_64) &&
- (u64)root_res->start > 0x100000000ul)
- break;
- }
-
- if (!root_res) {
- drm_info(&xe->drm, "Can't resize VRAM BAR - platform support is missing. Consider enabling 'Resizable BAR' support in your BIOS\n");
- return;
- }
-
- pci_read_config_dword(pdev, PCI_COMMAND, &pci_cmd);
- pci_write_config_dword(pdev, PCI_COMMAND, pci_cmd & ~PCI_COMMAND_MEMORY);
-
- resize_bar(xe, LMEM_BAR, rebar_size);
-
- pci_assign_unassigned_bus_resources(pdev->bus);
- pci_write_config_dword(pdev, PCI_COMMAND, pci_cmd);
-}
-
static bool resource_is_valid(struct pci_dev *pdev, int bar)
{
if (!pci_resource_flags(pdev, bar))
@@ -156,12 +63,11 @@ static int determine_lmem_bar_size(struct xe_device *xe, struct xe_vram_region *
static int get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size, u64 *poffset)
{
struct xe_device *xe = gt_to_xe(gt);
- unsigned int fw_ref;
u64 offset;
u32 reg;
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref.domains)
return -ETIMEDOUT;
if (GRAPHICS_VER(xe) >= 20) {
@@ -193,7 +99,6 @@ static int get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size, u64 *poffset)
offset = (u64)REG_FIELD_GET(XEHP_FLAT_CCS_PTR, reg) * SZ_64K;
}
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
*poffset = offset;
return 0;
@@ -248,7 +153,7 @@ static int tile_vram_size(struct xe_tile *tile, u64 *vram_size,
*tile_offset = 0;
} else {
reg = xe_mmio_read32(&tile->mmio, SG_TILE_ADDR_RANGE(tile->id));
- *tile_size = (u64)REG_FIELD_GET(GENMASK(14, 8), reg) * SZ_1G;
+ *tile_size = (u64)REG_FIELD_GET(GENMASK(17, 8), reg) * SZ_1G;
*tile_offset = (u64)REG_FIELD_GET(GENMASK(7, 1), reg) * SZ_1G;
}
diff --git a/drivers/gpu/drm/xe/xe_vram.h b/drivers/gpu/drm/xe/xe_vram.h
index 13505cfb184d..72860f714fc6 100644
--- a/drivers/gpu/drm/xe/xe_vram.h
+++ b/drivers/gpu/drm/xe/xe_vram.h
@@ -11,7 +11,6 @@
struct xe_device;
struct xe_vram_region;
-void xe_vram_resize_bar(struct xe_device *xe);
int xe_vram_probe(struct xe_device *xe);
struct xe_vram_region *xe_vram_region_alloc(struct xe_device *xe, u8 id, u32 placement);
diff --git a/drivers/gpu/drm/xe/xe_vram_freq.c b/drivers/gpu/drm/xe/xe_vram_freq.c
index 17bc84da4cdc..6f8281e0b96a 100644
--- a/drivers/gpu/drm/xe/xe_vram_freq.c
+++ b/drivers/gpu/drm/xe/xe_vram_freq.c
@@ -5,7 +5,6 @@
#include <linux/sysfs.h>
#include <drm/drm_managed.h>
-#include "xe_gt_types.h"
#include "xe_pcode.h"
#include "xe_pcode_api.h"
#include "xe_tile.h"
diff --git a/drivers/gpu/drm/xe/xe_vram_types.h b/drivers/gpu/drm/xe/xe_vram_types.h
index 83772dcbf1af..646e3c12ae9f 100644
--- a/drivers/gpu/drm/xe/xe_vram_types.h
+++ b/drivers/gpu/drm/xe/xe_vram_types.h
@@ -66,19 +66,8 @@ struct xe_vram_region {
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
/** @migrate: Back pointer to migrate */
struct xe_migrate *migrate;
- /** @pagemap: Used to remap device memory as ZONE_DEVICE */
- struct dev_pagemap pagemap;
- /**
- * @dpagemap: The struct drm_pagemap of the ZONE_DEVICE memory
- * pages of this tile.
- */
- struct drm_pagemap dpagemap;
- /**
- * @hpa_base: base host physical address
- *
- * This is generated when remap device memory as ZONE_DEVICE
- */
- resource_size_t hpa_base;
+ /** @dpagemap_cache: drm_pagemap cache. */
+ struct drm_pagemap_cache *dpagemap_cache;
#endif
};
diff --git a/drivers/gpu/drm/xe/xe_vsec.c b/drivers/gpu/drm/xe/xe_vsec.c
index 8f23a27871b6..4ebb4dbe1c9b 100644
--- a/drivers/gpu/drm/xe/xe_vsec.c
+++ b/drivers/gpu/drm/xe/xe_vsec.c
@@ -12,7 +12,6 @@
#include "xe_device.h"
#include "xe_device_types.h"
-#include "xe_drv.h"
#include "xe_mmio.h"
#include "xe_platform_types.h"
#include "xe_pm.h"
@@ -158,13 +157,15 @@ int xe_pmt_telem_read(struct pci_dev *pdev, u32 guid, u64 *data, loff_t user_off
guard(mutex)(&xe->pmt.lock);
+ if (!xe->soc_remapper.set_telem_region)
+ return -ENODEV;
+
/* indicate that we are not at an appropriate power level */
if (!xe_pm_runtime_get_if_active(xe))
return -ENODATA;
/* set SoC re-mapper index register based on GUID memory region */
- xe_mmio_rmw32(xe_root_tile_mmio(xe), SG_REMAP_INDEX1, SG_REMAP_BITS,
- REG_FIELD_PREP(SG_REMAP_BITS, mem_region));
+ xe->soc_remapper.set_telem_region(xe, mem_region);
memcpy_fromio(data, telem_addr, count);
xe_pm_runtime_put(xe);
diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c
index 3764abca3d4f..a991ee2b8781 100644
--- a/drivers/gpu/drm/xe/xe_wa.c
+++ b/drivers/gpu/drm/xe/xe_wa.c
@@ -15,10 +15,11 @@
#include "regs/xe_engine_regs.h"
#include "regs/xe_gt_regs.h"
+#include "regs/xe_guc_regs.h"
#include "regs/xe_regs.h"
#include "xe_device_types.h"
#include "xe_force_wake.h"
-#include "xe_gt.h"
+#include "xe_gt_types.h"
#include "xe_hw_engine_types.h"
#include "xe_mmio.h"
#include "xe_platform_types.h"
@@ -216,20 +217,6 @@ static const struct xe_rtp_entry_sr gt_was[] = {
XE_RTP_ACTIONS(SET(XELPMP_SQCNT1, ENFORCE_RAR))
},
- /* Xe2_LPG */
-
- { XE_RTP_NAME("16020975621"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0)),
- XE_RTP_ACTIONS(SET(XEHP_SLICE_UNIT_LEVEL_CLKGATE, SBEUNIT_CLKGATE_DIS))
- },
- { XE_RTP_NAME("14018157293"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0)),
- XE_RTP_ACTIONS(SET(XEHPC_L3CLOS_MASK(0), ~0),
- SET(XEHPC_L3CLOS_MASK(1), ~0),
- SET(XEHPC_L3CLOS_MASK(2), ~0),
- SET(XEHPC_L3CLOS_MASK(3), ~0))
- },
-
/* Xe2_LPM */
{ XE_RTP_NAME("14017421178"),
@@ -270,14 +257,6 @@ static const struct xe_rtp_entry_sr gt_was[] = {
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)),
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
},
- { XE_RTP_NAME("14020316580"),
- XE_RTP_RULES(MEDIA_VERSION(1301)),
- XE_RTP_ACTIONS(CLR(POWERGATE_ENABLE,
- VDN_HCP_POWERGATE_ENABLE(0) |
- VDN_MFXVDENC_POWERGATE_ENABLE(0) |
- VDN_HCP_POWERGATE_ENABLE(2) |
- VDN_MFXVDENC_POWERGATE_ENABLE(2))),
- },
{ XE_RTP_NAME("14019449301"),
XE_RTP_RULES(MEDIA_VERSION(1301), ENGINE_CLASS(VIDEO_DECODE)),
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F08(0), CG3DDISHRS_CLKGATE_DIS)),
@@ -323,6 +302,10 @@ static const struct xe_rtp_entry_sr gt_was[] = {
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F10(0), RAMDFTUNIT_CLKGATE_DIS)),
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
},
+ { XE_RTP_NAME("16028005424"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005)),
+ XE_RTP_ACTIONS(SET(GUC_INTR_CHICKEN, DISABLE_SIGNALING_ENGINES))
+ },
};
static const struct xe_rtp_entry_sr engine_was[] = {
@@ -512,11 +495,6 @@ static const struct xe_rtp_entry_sr engine_was[] = {
XE_RTP_RULES(GRAPHICS_VERSION(2004), FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, XE2_ALLOC_DPA_STARVE_FIX_DIS))
},
- { XE_RTP_NAME("14018957109"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN5, DISABLE_SAMPLE_G_PERFORMANCE))
- },
{ XE_RTP_NAME("14020338487"),
XE_RTP_RULES(GRAPHICS_VERSION(2004), FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(ROW_CHICKEN3, XE2_EUPEND_CHK_FLUSH_DIS))
@@ -526,11 +504,6 @@ static const struct xe_rtp_entry_sr engine_was[] = {
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(ROW_CHICKEN4, DISABLE_TDL_PUSH))
},
- { XE_RTP_NAME("14019322943"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0, TGM_WRITE_EOM_FORCE))
- },
{ XE_RTP_NAME("14018471104"),
XE_RTP_RULES(GRAPHICS_VERSION(2004), FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, ENABLE_SMP_LD_RENDER_SURFACE_CONTROL))
@@ -701,7 +674,7 @@ static const struct xe_rtp_entry_sr engine_was[] = {
XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
},
{ XE_RTP_NAME("18041344222"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3001),
+ XE_RTP_RULES(GRAPHICS_VERSION(3000),
FUNC(xe_rtp_match_first_render_or_compute),
FUNC(xe_rtp_match_not_sriov_vf),
FUNC(xe_rtp_match_gt_has_discontiguous_dss_groups)),
@@ -807,17 +780,6 @@ static const struct xe_rtp_entry_sr lrc_was[] = {
/* Xe2_LPG */
- { XE_RTP_NAME("16020518922"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0),
- ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(FF_MODE,
- DIS_TE_AUTOSTRIP |
- DIS_MESH_PARTIAL_AUTOSTRIP |
- DIS_MESH_AUTOSTRIP),
- SET(VFLSKPD,
- DIS_PARTIAL_AUTOSTRIP |
- DIS_AUTOSTRIP))
- },
{ XE_RTP_NAME("14019386621"),
XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(VF_SCRATCHPAD, XE2_VFG_TED_CREDIT_INTERFACE_DISABLE))
@@ -826,20 +788,10 @@ static const struct xe_rtp_entry_sr lrc_was[] = {
XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FD_END_COLLECT))
},
- { XE_RTP_NAME("14020013138"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0),
- ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(WM_CHICKEN3, HIZ_PLANE_COMPRESSION_DIS))
- },
{ XE_RTP_NAME("14019988906"),
XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FLSH_IGNORES_PSD))
},
- { XE_RTP_NAME("16020183090"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0),
- ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(INSTPM(RENDER_RING_BASE), ENABLE_SEMAPHORE_POLL_BIT))
- },
{ XE_RTP_NAME("18033852989"),
XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST))
diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules
index fb38eb3d6e9a..5cd7fa6d2a5c 100644
--- a/drivers/gpu/drm/xe/xe_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_wa_oob.rules
@@ -16,15 +16,11 @@
16017236439 PLATFORM(PVC)
14019821291 MEDIA_VERSION_RANGE(1300, 2000)
14015076503 MEDIA_VERSION(1300)
-16020292621 GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0)
-14018913170 GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0)
- MEDIA_VERSION(2000), GRAPHICS_STEP(A0, A1)
- GRAPHICS_VERSION_RANGE(1270, 1274)
+14018913170 GRAPHICS_VERSION_RANGE(1270, 1274)
MEDIA_VERSION(1300)
PLATFORM(DG2)
14018094691 GRAPHICS_VERSION_RANGE(2001, 2002)
GRAPHICS_VERSION(2004)
-14019882105 GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0)
18024947630 GRAPHICS_VERSION(2001)
GRAPHICS_VERSION(2004)
MEDIA_VERSION(2000)
@@ -76,3 +72,4 @@
15015404425_disable PLATFORM(PANTHERLAKE), MEDIA_STEP(B0, FOREVER)
16026007364 MEDIA_VERSION(3000)
+14020316580 MEDIA_VERSION(1301)
diff --git a/drivers/gpu/drm/xe/xe_wait_user_fence.c b/drivers/gpu/drm/xe/xe_wait_user_fence.c
index 5b4264ea38bd..51eb940ceb4e 100644
--- a/drivers/gpu/drm/xe/xe_wait_user_fence.c
+++ b/drivers/gpu/drm/xe/xe_wait_user_fence.c
@@ -11,7 +11,6 @@
#include <uapi/drm/xe_drm.h>
#include "xe_device.h"
-#include "xe_gt.h"
#include "xe_macros.h"
#include "xe_exec_queue.h"
diff --git a/drivers/gpu/drm/xe/xe_wopcm.c b/drivers/gpu/drm/xe/xe_wopcm.c
index ada0d0aa6b74..dde4f4967ca3 100644
--- a/drivers/gpu/drm/xe/xe_wopcm.c
+++ b/drivers/gpu/drm/xe/xe_wopcm.c
@@ -10,7 +10,7 @@
#include "regs/xe_guc_regs.h"
#include "xe_device.h"
#include "xe_force_wake.h"
-#include "xe_gt.h"
+#include "xe_gt_types.h"
#include "xe_mmio.h"
#include "xe_uc_fw.h"